Skip to content

fix(google): route tool responses by capability, not model name#5344

Open
leonobitech wants to merge 1 commit intolivekit:mainfrom
leonobitech:fix/gemini-3.1-tool-response
Open

fix(google): route tool responses by capability, not model name#5344
leonobitech wants to merge 1 commit intolivekit:mainfrom
leonobitech:fix/gemini-3.1-tool-response

Conversation

@leonobitech
Copy link
Copy Markdown

Summary

Fixes tool calling with gemini-3.1-flash-live-preview where the agent stops responding after function_tool execution.

Root cause

update_chat_ctx was completely disabled for Gemini 3.1 via an early return, because send_client_content is not supported mid-session on 3.1+. However, this also silently dropped tool responses (send_tool_response), which are a separate API method and still work on 3.1.

The model never received the tool result, timed out after ~7-12s, and sent LiveServerToolCallCancellation.

Timeline from production logs

t=14.231  user stops speaking
t=14.265  TOOL:START — executes in 5ms
t=14.265  TOOL:END — returns result
t=14.265  update_chat_ctx → early return (not compatible) ← tool response dropped
          ... 7 seconds of silence ...
t=21.581  user speaks again
t=23.071  server cancelled tool calls

Fix

Instead of hardcoding if model == "gemini-3.1-flash-live-preview", this PR adds a supports_client_content capability to RealtimeCapabilities and routes by capability:

  • Tool responses → always sent via send_tool_response regardless of model
  • Chat turns → sent via send_client_content only when the capability is True, skipped otherwise
  • No model name checks in routing logic — fully capability-driven

Why capability-based

Google separated their API into distinct methods (send_client_content, send_realtime_input, send_tool_response), each with different compatibility across model versions. The transport layer should route based on what the model supports, not based on model name strings. Future models only need to declare their capabilities.

Changes

File Change
livekit-agents/.../llm/realtime.py Add supports_client_content: bool = True to RealtimeCapabilities
livekit-plugins-google/.../realtime_api.py Set capability based on model version, refactor update_chat_ctx to route by capability

Testing

Tested in production with gemini-3.1-flash-live-preview + livekit-agents==1.5.1:

  • Function tools execute and Gemini continues speaking with the results ✅
  • No server cancelled tool calls warnings ✅
  • Data tracks sent from tools arrive at the frontend correctly ✅
  • Regular conversation (without tools) unaffected ✅

Related: #5260

Fixes tool calling with `gemini-3.1-flash-live-preview` where the agent
stops responding after function_tool execution.

Root cause: `update_chat_ctx` was completely disabled for Gemini 3.1 via
an early return because `send_client_content` is not supported mid-session.
This also silently dropped tool responses (`send_tool_response`), which
are a separate API method that still works on 3.1. The model never received
the tool result, timed out after ~7-12s, and sent a
`LiveServerToolCallCancellation`.

Changes:
- Add `supports_client_content` capability to `RealtimeCapabilities`
  (default True, backwards compatible with all existing plugins)
- Google plugin sets it False for Gemini 3.1+ models
- `update_chat_ctx` always sends tool responses via `send_tool_response`
  regardless of model capabilities
- Chat turns routed via `send_client_content` only when the capability
  is True, skipped otherwise
- No model name checks in routing logic — fully capability-driven

Related: livekit#5260
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 5 additional findings.

Open in Devin Review

@davidzhao davidzhao requested a review from tinalenguyen April 5, 2026 17:12
@tinalenguyen
Copy link
Copy Markdown
Member

Hi, thank you for creating this PR! The discrepancy in sending content between models has been raised and may be resolved in the near future. With this in mind, I am not sure if adding a capability field is the best way to resolve this.

Since the capability flag is determined by the models containing "3.1" anyway, perhaps we can still filter by that and add a comment?

@FMFigueroa
Copy link
Copy Markdown

Hey @tinalenguyen, thanks for the review!
I'd like to clarify something — send_tool_response has never been broken on Google's side. It works correctly on all model versions, including 3.1. It's a completely separate API method from send_client_content.

The issue is that LiveKit's early return in update_chat_ctx blocks both methods, even though only send_client_content is unsupported on 3.1. Tool responses get silently dropped, and the model cancels the call after ~7 seconds of silence.

What exactly would Google need to resolve here? The three methods (send_client_content, send_tool_response, send_realtime_input) are independent by design — they just shouldn't be gated by the same condition.
Happy to simplify the implementation approach — but the core issue remains: tool responses should never be blocked by a send_client_content guard.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants