Discard incomplete streaming replies - #1497
Merged
AbirAbbas merged 7 commits intoSep 30, 2026
Merged
Conversation
Member
|
Hey, thanks for the PR. Could you sign the CLA when you get a chance so we can review it? |
When the last retry of a truncated stream also failed, the turn fell through to the generic permanent-failure path, which keeps the streamed text. The person was told the partial reply was dropped while the transcript kept it as an interrupted assistant message. A truncated cut now resets the partial and tells the surface to withdraw that attempt's text, the same way a retry already does. Other permanent mid-stream failures still keep what the person watched arrive. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… cut The translator wrote [DONE] after any clean upstream end, so a Responses stream that stopped before response.completed reached the provider looking finished and the truncated-stream guard could never see it. [DONE] is now written only after a mapped terminal event. A length stop (max_output_tokens) still ends as a normal answer, and mapped failures are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Collaborator
|
Thanks for this one, it's a real fix. Testing it end to end turned up two gaps, so I pushed a commit for each:
Checked with the real binary against a local server that cuts every reply: it retries, exits saying the partial reply was dropped, and the transcript has no trace of it. Tests for both fail without the fixes. |
dev added a stop check before the permanent-failure path, an Addressed field on Event and a steer-consumed case in the feed at the same places this branch changed. All three are kept beside this branch's discard of an exhausted cut, and the feed's final discard now also resets the update stream the way dev's retry case does. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
dev now journals an interrupted partial as an aside rather than as an assistant reply, so a test that only looked for assistant rows would pass while the cut text was still saved. Both tests now check every row for the text, which is what they are meant to prove. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AbirAbbas
approved these changes
Sep 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1467
Issue: #1467
Streams that end without a finish reason or [DONE] marker are discarded and retried, so partial replies are not retained in conversation history. Either explicit completion signal remains sufficient.
Tests: go test -count=1 -p 1 -timeout 15m ./internal/provider; go test -count=1 -run 'TestATruncatedProviderReplyIsNotSettledByTheSession|TestAnIncompleteReplyIsAskedAgainWithoutKeepingItsText|TestRepeatedIncompleteRepliesSayWhatWasDropped' ./internal/session; go test ./internal/manual/; go test -run Manual ./internal/tui3/ ./internal/session/.