Commit 5ce517c7 authored by edy's avatar edy

fix(ui): clear completed assistant stream state

parent bdb82411
...@@ -5,7 +5,7 @@ import { ...@@ -5,7 +5,7 @@ import {
COMPOSER_TEXTAREA_MAX_RATIO, COMPOSER_TEXTAREA_MAX_RATIO,
COMPOSER_TEXTAREA_MIN_RATIO, COMPOSER_TEXTAREA_MIN_RATIO,
COMPOSER_TEXTAREA_SAFE_MIN_HEIGHT COMPOSER_TEXTAREA_SAFE_MIN_HEIGHT
} from "./constants" } from "./constants.ts"
export type MessageStreamState = "streaming" | "error" export type MessageStreamState = "streaming" | "error"
...@@ -92,14 +92,18 @@ export function mergeSessionHistory(current: UiChatMessage[], nextMessages: UiCh ...@@ -92,14 +92,18 @@ export function mergeSessionHistory(current: UiChatMessage[], nextMessages: UiCh
return message return message
} }
return { if (local.streamState === "streaming" && local.content.length > message.content.length) {
...message, return {
content: local.content.length > message.content.length ? local.content : message.content, ...message,
createdAt: message.createdAt || local.createdAt, content: local.content,
streamState: local.streamState ?? message.streamState, createdAt: message.createdAt || local.createdAt,
statusLabel: local.statusLabel ?? message.statusLabel, streamState: local.streamState ?? message.streamState,
statusDetail: local.statusDetail ?? message.statusDetail statusLabel: local.statusLabel ?? message.statusLabel,
statusDetail: local.statusDetail ?? message.statusDetail
}
} }
return message
}) })
const nextIds = new Set(nextMessages.map((message) => message.id)) const nextIds = new Set(nextMessages.map((message) => message.id))
const localOnlyMessages = current.filter((message) => !nextIds.has(message.id)) const localOnlyMessages = current.filter((message) => !nextIds.has(message.id))
......
import test from "node:test"
import assert from "node:assert/strict"
import type { UiChatMessage } from "../src/lib/chat-utils.ts"
import { mergeSessionHistory } from "../src/lib/chat-utils.ts"
test("mergeSessionHistory clears local assistant transient state when server has final content", () => {
const current: UiChatMessage[] = [{
id: "assistant-1",
role: "assistant",
content: "Final answer",
createdAt: "2026-05-18T08:00:00.000Z",
streamState: "streaming",
statusLabel: "Thinking",
statusDetail: "Using tools"
}]
const nextMessages: UiChatMessage[] = [{
id: "assistant-1",
role: "assistant",
content: "Final answer",
createdAt: "2026-05-18T08:00:01.000Z"
}]
const [message] = mergeSessionHistory(current, nextMessages)
assert.equal(message.content, "Final answer")
assert.equal(message.streamState, undefined)
assert.equal(message.statusLabel, undefined)
assert.equal(message.statusDetail, undefined)
})
test("mergeSessionHistory preserves active local streaming state when server content is stale", () => {
const current: UiChatMessage[] = [{
id: "assistant-1",
role: "assistant",
content: "Streaming answer with more text",
createdAt: "2026-05-18T08:00:00.000Z",
streamState: "streaming",
statusLabel: "Thinking",
statusDetail: "Drafting"
}]
const nextMessages: UiChatMessage[] = [{
id: "assistant-1",
role: "assistant",
content: "Streaming answer",
createdAt: "2026-05-18T08:00:01.000Z"
}]
const [message] = mergeSessionHistory(current, nextMessages)
assert.equal(message.content, "Streaming answer with more text")
assert.equal(message.streamState, "streaming")
assert.equal(message.statusLabel, "Thinking")
assert.equal(message.statusDetail, "Drafting")
})
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
"compilerOptions": { "compilerOptions": {
"module": "ESNext", "module": "ESNext",
"moduleResolution": "Bundler", "moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"jsx": "react-jsx", "jsx": "react-jsx",
"lib": ["ES2022", "DOM", "DOM.Iterable"], "lib": ["ES2022", "DOM", "DOM.Iterable"],
"types": ["vite/client"] "types": ["vite/client"]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment