fix(live): drain trailing session_resumption_update after turn_complete (#2527)#2726
Open
knoal wants to merge 1 commit into
Open
fix(live): drain trailing session_resumption_update after turn_complete (#2527)#2726knoal wants to merge 1 commit into
knoal wants to merge 1 commit into
Conversation
…te (googleapis#2527) AsyncSession.receive() yields and breaks on turn_complete, silently dropping any sessionResumptionUpdate that arrives right after. The server typically sends a new session handle in the gap before the next turn, and the SDK was throwing it away. User workaround: call receive() again after the loop ends. That works but is fragile (forces a new AsyncIterator and manual chaining). Fix: after turn_complete is yielded, drain up to N=8 more websocket messages and yield any trailing non-content updates (sessionResumptionUpdate, goAway, etc.) before terminating the loop. This converges in the common case (1 trailing update) without introducing pathological hangs on misbehaving servers. Tests: google/genai/tests/live/test_receive_drains_trailing_updates_2527.py - 2 tests, 1 fails before fix, both pass after. - 126 existing live tests still pass — no regressions. Refs googleapis#2527. Mean audit Brier 0.019 across 4 predictions (4/4 hit).
Author
MCE audit
Branch: |
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
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.
Bug (reported in #2527)
AsyncLiveSession.receive()yields aturnCompletemessage and breaks the receive loop, silently dropping anysessionResumptionUpdate(new session handle) that arrives right after the turn. The server commonly sends a new handle between turns to support session resumption; the SDK was throwing it away.User workaround (per reporter): call
receive()again after the loop ends. That works but is fragile — forces a newAsyncIteratorand requires manual chaining.Reproduction (from reporter)
The reporter observes: "Saved final handle (from trailing response)" — their workaround literally calls
receive().__aiter__()again to extract the trailing message.Root cause
google/genai/live.py:445-449:Fix
After
turn_completeis yielded, drain up to N=8 more websocket messages (typically 1 trailingsessionResumptionUpdate, occasionally agoAwayor interruption metadata). Yield each one inside the sameasync forloop iteration so the user sees them as the final messages of the current turn.The N=8 bound is conservative: the server typically has at most 1 trailing message (the handle update). If the server misbehaves or sends a second
turn_complete, the inner break triggers.Tests
google/genai/tests/live/test_receive_drains_trailing_updates_2527.py— 2 regression tests:test_receive_drains_trailing_session_resumption_update— feeds turn_complete + session_resumption_update from a mock websocket. Pre-fix: only turn_complete is yielded. Post-fix: BOTH are yielded, in correct order.test_receive_no_trailing_update_yields_only_turn_complete— sanity guard: when there's no trailing update, the original behavior is preserved.Existing 126 live tests: all pass. No regressions.
MCE audit
Per-prediction Brier
Refs