fix(ios): preserve repeated optimistic user messages on refresh
This commit is contained in:
parent
9ee9006af5
commit
09618f91df
@ -376,9 +376,16 @@ public final class OpenClawChatViewModel {
|
||||
guard !previous.isEmpty else { return incoming }
|
||||
guard !incoming.isEmpty else { return previous }
|
||||
|
||||
func countKeys(_ keys: [String]) -> [String: Int] {
|
||||
keys.reduce(into: [:]) { counts, key in
|
||||
counts[key, default: 0] += 1
|
||||
}
|
||||
}
|
||||
|
||||
var reconciled = Self.reconcileMessageIDs(previous: previous, incoming: incoming)
|
||||
let incomingIdentityKeys = Set(reconciled.compactMap(Self.messageIdentityKey(for:)))
|
||||
let incomingUserRefreshKeys = Set(reconciled.compactMap(Self.userRefreshIdentityKey(for:)))
|
||||
var remainingIncomingUserRefreshCounts = countKeys(
|
||||
reconciled.compactMap(Self.userRefreshIdentityKey(for:)))
|
||||
|
||||
var lastMatchedPreviousIndex: Int?
|
||||
for (index, message) in previous.enumerated() {
|
||||
@ -389,8 +396,10 @@ public final class OpenClawChatViewModel {
|
||||
continue
|
||||
}
|
||||
if let userKey = Self.userRefreshIdentityKey(for: message),
|
||||
incomingUserRefreshKeys.contains(userKey)
|
||||
let remaining = remainingIncomingUserRefreshCounts[userKey],
|
||||
remaining > 0
|
||||
{
|
||||
remainingIncomingUserRefreshCounts[userKey] = remaining - 1
|
||||
lastMatchedPreviousIndex = index
|
||||
}
|
||||
}
|
||||
@ -401,7 +410,12 @@ public final class OpenClawChatViewModel {
|
||||
.filter { message in
|
||||
guard message.role.lowercased() == "user" else { return false }
|
||||
guard let key = Self.userRefreshIdentityKey(for: message) else { return false }
|
||||
return !incomingUserRefreshKeys.contains(key)
|
||||
let remaining = remainingIncomingUserRefreshCounts[key] ?? 0
|
||||
if remaining > 0 {
|
||||
remainingIncomingUserRefreshCounts[key] = remaining - 1
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
guard !trailingUserMessages.isEmpty else {
|
||||
|
||||
@ -553,6 +553,49 @@ extension TestChatTransportState {
|
||||
}
|
||||
}
|
||||
|
||||
@Test func preservesRepeatedOptimisticUserMessagesWithIdenticalContentDuringRefresh() async throws {
|
||||
let sessionId = "sess-main"
|
||||
let now = Date().timeIntervalSince1970 * 1000
|
||||
let history1 = historyPayload(sessionId: sessionId)
|
||||
let history2 = historyPayload(
|
||||
sessionId: sessionId,
|
||||
messages: [
|
||||
chatTextMessage(
|
||||
role: "user",
|
||||
text: "retry",
|
||||
timestamp: now + 5_000),
|
||||
chatTextMessage(
|
||||
role: "assistant",
|
||||
text: "first answer",
|
||||
timestamp: now + 6_000),
|
||||
])
|
||||
|
||||
let (transport, vm) = await makeViewModel(historyResponses: [history1, history2, history2])
|
||||
try await loadAndWaitBootstrap(vm: vm, sessionId: sessionId)
|
||||
try await sendMessageAndEmitFinal(
|
||||
transport: transport,
|
||||
vm: vm,
|
||||
text: "retry")
|
||||
try await sendMessageAndEmitFinal(
|
||||
transport: transport,
|
||||
vm: vm,
|
||||
text: "retry")
|
||||
|
||||
try await waitUntil("repeated optimistic user message is preserved") {
|
||||
await MainActor.run {
|
||||
let retryMessages = vm.messages.filter { message in
|
||||
message.role == "user" &&
|
||||
message.content.compactMap(\.text).joined(separator: "\n") == "retry"
|
||||
}
|
||||
let hasAssistant = vm.messages.contains { message in
|
||||
message.role == "assistant" &&
|
||||
message.content.compactMap(\.text).joined(separator: "\n") == "first answer"
|
||||
}
|
||||
return hasAssistant && retryMessages.count == 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test func acceptsCanonicalSessionKeyEventsForOwnPendingRun() async throws {
|
||||
let history1 = historyPayload()
|
||||
let history2 = historyPayload(
|
||||
|
||||
@ -5,7 +5,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type {
|
||||
OpenClawPluginCommandDefinition,
|
||||
PluginCommandContext,
|
||||
} from "../../src/plugins/types.js";
|
||||
} from "openclaw/plugin-sdk/core";
|
||||
import { createTestPluginApi } from "../../test/helpers/extensions/plugin-api.js";
|
||||
import type { OpenClawPluginApi } from "./api.js";
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user