Repository navigation
fix(agent): deliver non-streamed finalization responses - #5049
Merged
Merged
Conversation
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.
Background
Streaming channels intentionally skip the final outbound package when it is marked as a
StreamedResponseEvent, because that content is expected to have already arrived through deltas. That suppression mechanism originated in #2365.Empty-response recovery later made a non-streaming finalization reachable after a stream had already been closed (#2733), and the silent retries added in #2889 amplify the path into multiple
stream_endevents. #3613 fixed the analogous drop for error/tool-error responses, but empty finalization could still be marked as streamed despite emitting no answer delta.Root cause
AgentLooptreated the presence of anon_streamcallback as proof that the final response content had been streamed. During empty-response finalization, the callback exists, but the final answer comes fromchat_with_retryand never passes through the delta callback.ChannelManagerthen skips the final package, leaving the session populated while streaming clients see no answer.Fix
StreamedResponseEventonly when that final segment delivered content.ChannelManagercallschannel.send().Regression coverage
The new test reproduces the production sequence:
StreamEndEventsIt asserts that the final answer is emitted as a regular outbound message rather than
StreamedResponseEvent.Verification
pytest tests/agent -q— 1383 passed, 2 skippedruff check nanobot/— passedbun run test— 706 passedbun run build— passedwebui-threadroute returned exactly one assistant final answer