Skip to content

Feature Request: Add state_delta Support to LiveRequest for WebSocket Live Mode #4220

@MinkeCheng

Description

@MinkeCheng

Feature Request: Add state_delta Support to LiveRequest for WebSocket Live Mode

Is your feature request related to a problem? Please describe.

Currently, the HTTP POST /run endpoint supports updating session state via the state_delta parameter in RunAgentRequest. However, the WebSocket /run_live endpoint does not provide an equivalent mechanism to update session state when sending messages through LiveRequest.

This creates an inconsistency between the two APIs:

  • HTTP /run: Supports state_delta in RunAgentRequest → passed to runner.run_async(state_delta=...)
  • WebSocket /run_live: LiveRequest only supports content, blob, activity_start, activity_end, and close fields

When building applications that use live bidirectional streaming (especially for text-based interactions, not just audio), there's no direct way to update session state alongside user messages. This limitation forces developers to use workarounds like creating custom tools or parsing state updates from message content, which is less elegant and harder to maintain.

Describe the solution you'd like

Add a state_delta field to the LiveRequest class to enable state updates in live mode, similar to how it works in the standard /run endpoint.

Proposed Implementation:

  1. Extend LiveRequest class in src/google/adk/agents/live_request_queue.py:
class LiveRequest(BaseModel):
  """Request send to live agents."""

  model_config = ConfigDict(ser_json_bytes='base64', val_json_bytes='base64')
  
  content: Optional[types.Content] = None
  """If set, send the content to the model in turn-by-turn mode."""
  blob: Optional[types.Blob] = None
  """If set, send the blob to the model in realtime mode."""
  activity_start: Optional[types.ActivityStart] = None
  """If set, signal the start of user activity to the model."""
  activity_end: Optional[types.ActivityEnd] = None
  """If set, signal the end of user activity to the model."""
  close: bool = False
  """If set, close the queue."""
  state_delta: Optional[dict[str, Any]] = None  # NEW FIELD
  """If set, update the session state with the provided delta."""
  1. Process state_delta in _send_to_model method in src/google/adk/flows/llm_flows/base_llm_flow.py:
async def _send_to_model(self, llm_connection, invocation_context):
    while True:
        live_request = await invocation_context.live_request_queue.get()
        
        # Handle state_delta updates
        if live_request.state_delta:
            event = Event(
                invocation_id=invocation_context.invocation_id,
                author='user',
                actions=EventActions(state_delta=live_request.state_delta)
            )
            await invocation_context.session_service.append_event(
                session=invocation_context.session,
                event=event
            )
        
        # ... rest of the existing logic
  1. Client usage example:
# Python client
live_request = LiveRequest(
    content=types.Content(
        role="user",
        parts=[types.Part(text="Hello")]
    ),
    state_delta={
        "user_preference": "dark_mode",
        "last_action": "greeting"
    }
)
websocket.send_text(live_request.model_dump_json())
// JavaScript client
const liveRequest = {
    content: {
        role: "user",
        parts: [{ text: "Hello" }]
    },
    state_delta: {
        "user_preference": "dark_mode",
        "last_action": "greeting"
    }
};
websocket.send(JSON.stringify(liveRequest));

Describe alternatives you've considered

  1. Custom Tool Approach: Create a tool that updates session state, then have the model call it. This works but adds unnecessary LLM roundtrips and token usage.

  2. Callback-based Parsing: Parse special markers in message content (e.g., "update_state:key=value") and handle them in callbacks. This is fragile and mixes data with content.

  3. Pre-update via HTTP: Call /run endpoint with state_delta before establishing WebSocket connection. This doesn't work for dynamic state updates during an ongoing live session.

  4. Direct Session Modification: Directly modify session.state outside of the event system. This is explicitly discouraged in the documentation as it bypasses event history and breaks persistence.

None of these alternatives are as clean or consistent with ADK's design principles as native state_delta support in LiveRequest.

Additional context

  • Related documentation: https://adk.wiki/sessions/state/#state
  • The official docs emphasize using EventActions.state_delta for proper state management with auditability and persistence
  • This feature would bring parity between HTTP and WebSocket APIs
  • Particularly useful for text-based live interactions where state needs to be updated alongside messages
  • Maintains consistency with the existing state management architecture

References:

  • RunAgentRequest implementation: src/google/adk/cli/adk_web_server.py:201-209
  • LiveRequest implementation: src/google/adk/agents/live_request_queue.py:26-42
  • runner.run_async with state_delta: src/google/adk/runners.py:411-432
  • State management best practices: https://adk.wiki/sessions/state/#state

Metadata

Metadata

Assignees

No one assigned

    Labels

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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions