Skip to content

fix(models): preserve tool_call co-delivered with turn_complete - #6618

Closed
patrickswedish wants to merge 1 commit into
google:mainfrom
patrickswedish:fix/live-multipart-text-and-codelivered-tool-call
Closed

fix(models): preserve tool_call co-delivered with turn_complete#6618
patrickswedish wants to merge 1 commit into
google:mainfrom
patrickswedish:fix/live-multipart-text-and-codelivered-tool-call

Conversation

@patrickswedish

@patrickswedish patrickswedish commented Aug 6, 2026

Copy link
Copy Markdown

Summary

Fixes #6615.

In GeminiLlmConnection.receive(), when a LiveServerMessage carries both server_content.turn_complete=True and message.tool_call, the previous control flow broke out of the receive loop before reaching the lower if message.tool_call: handler, silently discarding the tool call.

Note: Multipart text data loss (#6616) has already landed upstream via commit 2109ea96e1c7431225e57fb6db6c4c116e632867 (PR #6617). This PR is now solely and cleanly focused on #6615.


Root cause

The receive loop was structured as:

python async for message in agen: if message.server_content: ... if message.server_content.turn_complete: ... break # exits receive loop if message.tool_call: # unreachable when turn_complete is present ...

When a single LiveServerMessage contains both turn_complete=True and tool_call, the break terminated receive before if message.tool_call: could execute.


Fix

Restructured GeminiLlmConnection.receive() so that all message fields (including tool_call, session_resumption_update, voice_activity, and go_away) are processed through their standard handling semantics before finalizing turn_complete and exiting the receive loop.

  • Evaluates if message.tool_call: before evaluating if message.server_content.turn_complete:.
  • Preserves single tool-call handling implementation (zero duplicated logic).
  • Preserves ordering: text -> tool call -> turn_complete.
  • Preserves immediate yielding for Gemini 3.x Live models and buffering/aggregation for other models.
  • Preserves all metadata (grounding_metadata, last_grounding_metadata, tool_call_metadata, live_session_id, model_version).

Testing

  • Targeted Unit Tests: pytest tests/unittests/models/test_gemini_llm_connection.py (66 / 66 passed)
    • test_receive_preserves_tool_call_codelivered_with_turn_complete (verifies multi-function tool call preserved before turn_complete)
    • test_receive_preserves_text_and_tool_call_codelivered_with_turn_complete (verifies text + tool_call + turn_complete ordering and content)
    • test_receive_codelivered_tool_call_empty_function_calls (verifies empty function_calls handling)
  • Model Unit Tests: pytest tests/unittests/models/ (1068 / 1068 passed)
  • Linters / Formatters: ruff, isort, pyink (Clean)
  • Build: uv build (Built sdist and wheel successfully)
  • Diff Check: git diff --check (Clean)

@google-cla

google-cla Bot commented Aug 6, 2026

Copy link
Copy Markdown

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.

@patrickswedish

Copy link
Copy Markdown
Author

@googlebot I signed the CLA!

@patrickswedish

Copy link
Copy Markdown
Author

Hi team! 👋

Just a quick heads up on this PR — it fixes two silent data-loss bugs in the live streaming receive loop that affect real-world multimodal and tool-calling use cases:

  1. Bug: Systemic parts[0] indexing drops multimodal streaming data and bypasses content validation #6616 — multi-part text chunks lose all text beyond parts[0]
  2. Live mode: tool_call can be silently dropped when co-framed with turn_complete in gemini_llm_connection.py #6615tool_call co-delivered with turn_complete is silently dropped, deadlocking the agent

Both fixes are surgical (25 lines changed, one file), backward-compatible, and traced directly against the live API message format. Happy to add unit tests or address any review feedback. Looking forward to getting this in! 🙏

@adk-bot adk-bot added the live [Component] This issue is related to live, voice and video chat label Aug 6, 2026
Harshitmishra001 added a commit to Harshitmishra001/adk-python that referenced this pull request Aug 12, 2026
@patrickswedish
patrickswedish force-pushed the fix/live-multipart-text-and-codelivered-tool-call branch from fafc450 to 9f57073 Compare August 16, 2026 20:44
@patrickswedish patrickswedish changed the title fix(models): fix multipart text data loss and co-delivered tool_call drop in live receive loop fix(models): preserve tool_call co-delivered with turn_complete Aug 16, 2026
@patrickswedish
patrickswedish force-pushed the fix/live-multipart-text-and-codelivered-tool-call branch from 9f57073 to 231c7b8 Compare August 16, 2026 21:01
In GeminiLlmConnection.receive(), when a LiveServerMessage carries both server_content.turn_complete=True and message.tool_call, the turn_complete branch previously broke out of the receive loop before the message.tool_call handler could execute, silently dropping the tool call.

Restructure the control flow so all message fields (including tool_call) are processed through their standard handling semantics before finalizing turn_complete and exiting the loop.

Fixes google#6615
@patrickswedish
patrickswedish force-pushed the fix/live-multipart-text-and-codelivered-tool-call branch from 231c7b8 to c5e137f Compare August 16, 2026 21:02
@wuliang229

Copy link
Copy Markdown
Collaborator

This is a non-issue.

@wuliang229 wuliang229 closed this Aug 17, 2026
@patrickswedish

Copy link
Copy Markdown
Author

Thanks for reviewing and providing the feedback, @wuliang229! Appreciate your time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

live [Component] This issue is related to live, voice and video chat

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Live mode: tool_call can be silently dropped when co-framed with turn_complete in gemini_llm_connection.py

3 participants