fix: prevent cross-turn assistant message merging in chat UI
appendAssistantTextMessage was merging consecutive assistant_text messages regardless of whether they belonged to different conversation turns. Added turnComplete flag that gets set on message_complete, error, and status(idle) events, preventing subsequent turns from merging into the previous response.
This commit is contained in:
@@ -174,7 +174,7 @@ function appendAssistantTextMessage(
|
||||
if (!content.trim()) return messages
|
||||
|
||||
const last = messages[messages.length - 1]
|
||||
if (last?.type === 'assistant_text') {
|
||||
if (last?.type === 'assistant_text' && !last.turnComplete) {
|
||||
const merged: UIMessage = {
|
||||
...last,
|
||||
content: last.content + content,
|
||||
@@ -195,6 +195,17 @@ function appendAssistantTextMessage(
|
||||
]
|
||||
}
|
||||
|
||||
function markLastAssistantComplete(messages: UIMessage[]): UIMessage[] {
|
||||
const lastIdx = messages.length - 1
|
||||
const last = messages[lastIdx]
|
||||
if (last?.type === 'assistant_text' && !last.turnComplete) {
|
||||
const updated = [...messages]
|
||||
updated[lastIdx] = { ...last, turnComplete: true }
|
||||
return updated
|
||||
}
|
||||
return messages
|
||||
}
|
||||
|
||||
/** Helper: immutably update a specific session within the sessions record */
|
||||
function updateSessionIn(
|
||||
sessions: Record<string, PerSessionState>,
|
||||
@@ -562,7 +573,7 @@ export const useChatStore = create<ChatStore>((set, get) => ({
|
||||
...(msg.tokens ? { tokenUsage: { ...session.tokenUsage, output_tokens: msg.tokens } } : {}),
|
||||
...(msg.state === 'idle' ? { activeThinkingId: null, statusVerb: '' } : {}),
|
||||
...(shouldFlush ? {
|
||||
messages: appendAssistantTextMessage(session.messages, pendingText, Date.now()),
|
||||
messages: markLastAssistantComplete(appendAssistantTextMessage(session.messages, pendingText, Date.now())),
|
||||
streamingText: '',
|
||||
} : pendingText !== session.streamingText ? { streamingText: pendingText } : {}),
|
||||
}
|
||||
@@ -727,11 +738,14 @@ export const useChatStore = create<ChatStore>((set, get) => ({
|
||||
const text = `${session.streamingText}${consumePendingDelta(sessionId)}`
|
||||
if (text.trim()) {
|
||||
update((s) => ({
|
||||
messages: appendAssistantTextMessage(s.messages, text, Date.now()),
|
||||
messages: markLastAssistantComplete(appendAssistantTextMessage(s.messages, text, Date.now())),
|
||||
streamingText: '',
|
||||
}))
|
||||
} else if (text !== session.streamingText) {
|
||||
update(() => ({ streamingText: text }))
|
||||
update((s) => ({ messages: markLastAssistantComplete(s.messages) }))
|
||||
} else {
|
||||
update((s) => ({ messages: markLastAssistantComplete(s.messages) }))
|
||||
}
|
||||
if (session.elapsedTimer) clearInterval(session.elapsedTimer)
|
||||
update(() => ({
|
||||
@@ -750,7 +764,7 @@ export const useChatStore = create<ChatStore>((set, get) => ({
|
||||
const pendingText = `${s.streamingText}${consumePendingDelta(sessionId)}`
|
||||
let newMessages = s.messages
|
||||
if (pendingText.trim()) {
|
||||
newMessages = appendAssistantTextMessage(newMessages, pendingText, Date.now())
|
||||
newMessages = markLastAssistantComplete(appendAssistantTextMessage(newMessages, pendingText, Date.now()))
|
||||
}
|
||||
newMessages = [...newMessages, { id: nextId(), type: 'error', message: msg.message, code: msg.code, timestamp: Date.now() }]
|
||||
return {
|
||||
|
||||
@@ -158,7 +158,7 @@ export type TaskSummaryItem = {
|
||||
|
||||
export type UIMessage =
|
||||
| { id: string; type: 'user_text'; content: string; timestamp: number; attachments?: UIAttachment[]; pending?: boolean }
|
||||
| { id: string; type: 'assistant_text'; content: string; timestamp: number; model?: string }
|
||||
| { id: string; type: 'assistant_text'; content: string; timestamp: number; model?: string; turnComplete?: boolean }
|
||||
| { id: string; type: 'thinking'; content: string; timestamp: number }
|
||||
| { id: string; type: 'tool_use'; toolName: string; toolUseId: string; input: unknown; timestamp: number; parentToolUseId?: string }
|
||||
| { id: string; type: 'tool_result'; toolUseId: string; content: unknown; isError: boolean; timestamp: number; parentToolUseId?: string }
|
||||
|
||||
Reference in New Issue
Block a user