Skip to content

Google Gemini Live realtime: tool-call result is lost when update_tools() restarts the session (and generation-completion handling breaks newer Live preview models) #6479

Description

@areebkhan-tech

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

  1. 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.
  2. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions