the finished message wins over the buffer that was streaming it
A reply rendered as "ipped." — first two characters gone — while the durable log held it complete. Nothing was lost; the client discarded it. The streaming buffer is an accumulation of deltas and is only as good as the socket. The `assistant:text` event that ends the stream carries the whole message and arrives right after. Committing the buffer and ignoring that text means a single dropped frame is permanent, even though a correct copy came in immediately behind it. Losing a frame is not exotic: restarting the server mid-turn does it, which is how this was found — three times tonight, by me. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -301,9 +301,19 @@ export function useChat(initialSessionId?: string, initialModel?: string | null,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function commitStreaming() {
|
/**
|
||||||
if (!streamingRef.current) return;
|
* Turn the in-flight streaming buffer into a committed message.
|
||||||
const text = streamingRef.current;
|
*
|
||||||
|
* `authoritative` is the complete text from the `assistant:text` event that ends the stream, and it
|
||||||
|
* wins whenever it is present. The buffer is an accumulation of deltas and is only ever as good as the
|
||||||
|
* socket: lose one frame — a restart mid-turn will do it — and it is short by whatever that frame
|
||||||
|
* carried, while the event carries the whole message. Committing the buffer threw away a correct copy
|
||||||
|
* in favour of a damaged one, which is how a reply rendered as "ipped." with its first two characters
|
||||||
|
* missing while the durable log held it intact.
|
||||||
|
*/
|
||||||
|
function commitStreaming(authoritative?: string) {
|
||||||
|
if (!streamingRef.current && !authoritative) return;
|
||||||
|
const text = authoritative ?? streamingRef.current;
|
||||||
streamingRef.current = '';
|
streamingRef.current = '';
|
||||||
setStreamingText('');
|
setStreamingText('');
|
||||||
setMessages((prev) => [...prev, { role: 'assistant', id: randomId(), text }]);
|
setMessages((prev) => [...prev, { role: 'assistant', id: randomId(), text }]);
|
||||||
@@ -427,7 +437,7 @@ export function useChat(initialSessionId?: string, initialModel?: string | null,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (streamingRef.current) {
|
if (streamingRef.current) {
|
||||||
commitStreaming();
|
commitStreaming(msg.text);
|
||||||
} else {
|
} else {
|
||||||
setMessages((prev) => [...prev, { role: 'assistant', id: randomId(), text: msg.text }]);
|
setMessages((prev) => [...prev, { role: 'assistant', id: randomId(), text: msg.text }]);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user