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;
|
||||
const text = streamingRef.current;
|
||||
/**
|
||||
* Turn the in-flight streaming buffer into a committed message.
|
||||
*
|
||||
* `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 = '';
|
||||
setStreamingText('');
|
||||
setMessages((prev) => [...prev, { role: 'assistant', id: randomId(), text }]);
|
||||
@@ -427,7 +437,7 @@ export function useChat(initialSessionId?: string, initialModel?: string | null,
|
||||
break;
|
||||
}
|
||||
if (streamingRef.current) {
|
||||
commitStreaming();
|
||||
commitStreaming(msg.text);
|
||||
} else {
|
||||
setMessages((prev) => [...prev, { role: 'assistant', id: randomId(), text: msg.text }]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user