Bug Description
In livekit-plugins-google realtime (livekit/plugins/google/realtime/realtime_api.py),
the RealtimeSession has two related problems:
1. Tool result dropped across a session restart.
When something restarts the underlying Gemini Live websocket mid-turn — most
commonly update_tools() setting _session_should_close — a function-tool
result produced during that restart window is sent with _send_client_event(...)
on the old, closing session. It is never replayed to the new session after
reconnect, so the model never receives the LiveClientToolResponse. The turn
then stalls: the model is still waiting on a function response it will never get,
and the conversation hangs.
2. Generation-completion handling assumes older Gemini Live turn semantics.
The receive loop and generate_reply() assume (a) only models advertising
mutable_chat_context may generate replies, and (b) every model_turn event
maps to an active generation. Newer Gemini Live preview models stream
generation / model_turn events with different completion timing, so with them:
generate_reply() is rejected outright, and
- stray
model_turn responses that arrive with no active generation are
mishandled (double-counted / attached to the wrong generation).
Expected Behavior
- A pending tool result must survive a session restart: it should be buffered
and replayed to the reconnected session (via session.send_tool_response(...))
so the model receives the function response and continues the turn instead of
hanging.
- The realtime session should track generation-completion explicitly (not infer
it from model name / mutable_chat_context), so newer Gemini Live models are
handled correctly and generate_reply() works for Live models that support it.
Reproduction Steps
1. Create an `AgentSession` with a Google realtime model
(`gemini-live-2.5-flash-preview`, realtime=True).
2. Register a `@function_tool`.
3. During a turn where the model invokes the tool, trigger `update_tools()`
(e.g. a dynamic tool change or agent handoff) so the Live session restarts
right around when the tool result is produced.
4. Observe: the model never receives the tool result — the turn produces no
follow-up response and hangs.
Expected: the model receives the result and replies normally.
Operating System
macOS
Models Used
No response
Package Versions
livekit==1.1.13
livekit-agents==1.6.5
livekit-plugins-google==1.6.5
livekit-api (latest)
Session/Room/Call IDs
No response
Proposed Solution
- Add a `_pending_tool_result` buffer on the RealtimeSession. If a tool result is
produced while the session is restarting (`_session_should_close` set), stash it
instead of sending it to the dying socket. On reconnect in `_main_task`, replay
it with `session.send_tool_response(function_responses=...)` before resuming, and
nudge the model to continue.
- Replace name/`mutable_chat_context`-based assumptions with an explicit
`_generation_completed` state so `model_turn`/generation events are handled
correctly for newer Live models, and allow `generate_reply()` for Live models
that support it.
A proof-of-concept patch demonstrating both fixes exists in a community fork
(livekit-google-plugin) — but it hardcodes model-name string checks
(`== "gemini-3.1-flash-live-preview"`) which should be replaced with
capability/behavior detection before merging upstream.
Additional Context
The two issues share one code path (the tool-result send block and the
_main_task reconnect handler), so they're best fixed together. The tool-result
replay fix is model-agnostic and valuable on its own, independent of any specific
Gemini Live model.
Screenshots and Recordings
No response
Bug Description
In
livekit-plugins-googlerealtime (livekit/plugins/google/realtime/realtime_api.py),the
RealtimeSessionhas two related problems:1. Tool result dropped across a session restart.
When something restarts the underlying Gemini Live websocket mid-turn — most
commonly
update_tools()setting_session_should_close— a function-toolresult produced during that restart window is sent with
_send_client_event(...)on the old, closing session. It is never replayed to the new session after
reconnect, so the model never receives the
LiveClientToolResponse. The turnthen stalls: the model is still waiting on a function response it will never get,
and the conversation hangs.
2. Generation-completion handling assumes older Gemini Live turn semantics.
The receive loop and
generate_reply()assume (a) only models advertisingmutable_chat_contextmay generate replies, and (b) everymodel_turneventmaps to an active generation. Newer Gemini Live preview models stream
generation /
model_turnevents with different completion timing, so with them:generate_reply()is rejected outright, andmodel_turnresponses that arrive with no active generation aremishandled (double-counted / attached to the wrong generation).
Expected Behavior
and replayed to the reconnected session (via
session.send_tool_response(...))so the model receives the function response and continues the turn instead of
hanging.
it from model name /
mutable_chat_context), so newer Gemini Live models arehandled correctly and
generate_reply()works for Live models that support it.Reproduction Steps
Operating System
macOS
Models Used
No response
Package Versions
Session/Room/Call IDs
No response
Proposed Solution
Additional Context
The two issues share one code path (the tool-result send block and the
_main_taskreconnect handler), so they're best fixed together. The tool-resultreplay fix is model-agnostic and valuable on its own, independent of any specific
Gemini Live model.
Screenshots and Recordings
No response