+## Choose your language
-- :fontawesome-brands-python:{ .lg .middle } **Python ADK: Streaming agent**
+
- ---
- This example demonstrates how to set up a basic streaming interaction with an agent using Python ADK. It typically involves using the `Runner.run_live()` method and handling asynchronous events.
+
+
+- :fontawesome-brands-python:{ .lg .middle } **Python**
- [:octicons-arrow-right-24: View Python Streaming Quickstart](streaming-python.md)
+ ---
+ Set up ADK, build a voice agent, and talk to it in `adk web`.
-
+ [:octicons-arrow-right-24: Python quickstart](streaming-python.md)
-- :fontawesome-brands-java:{ .lg .middle } **Java ADK: Streaming agent**
+- :fontawesome-brands-java:{ .lg .middle } **Java**
---
- This example demonstrates how to set up a basic streaming interaction with an agent using Java ADK. It involves using the `Runner.runLive()` method, a `LiveRequestQueue`, and handling the `Flowable` stream.
- [:octicons-arrow-right-24: View Java Streaming Quickstart](streaming-java.md)
+ Set up Maven, build a voice agent, and run it in `adk web` or a custom audio app.
+
+ [:octicons-arrow-right-24: Java quickstart](streaming-java.md)
+
+## Next steps
+
+- **[Configuration](../configuration.md)** — set the voice, language, transcription, and
+ turn detection.
+- **[Tools](../tools.md)** — give the agent tools it can call mid-conversation, including
+ ones that stream results back while they run.
+- **[Sessions](../sessions.md)** and **[Events](../events.md)** — the `run_live()` loop and
+ everything it hands back.
+- **[Evaluation](../evaluation.md)** — score voice conversations before you ship.
+- **[Build a custom server](../custom-server.md)** — `adk web` is a development client, so
+ this is how you run a live agent behind your own server and client.
diff --git a/docs/live/get-started/streaming-java.md b/docs/live/get-started/streaming-java.md
index 21c0c82c53..24f8a67ffc 100644
--- a/docs/live/get-started/streaming-java.md
+++ b/docs/live/get-started/streaming-java.md
@@ -1,8 +1,10 @@
# Build a streaming agent with Java
-This quickstart guide will walk you through the process of creating a basic agent and leveraging ADK Streaming with Java to facilitate low-latency, bidirectional voice interactions.
+Build a Java agent that holds a low-latency, two-way voice conversation with ADK Streaming.
-You'll begin by setting up your Java and Maven environment, structuring your project, and defining the necessary dependencies. Following this, you'll create a simple `ScienceTeacherAgent`, test its text-based streaming capabilities using the Dev UI, and then progress to enabling live audio communication, transforming your agent into an interactive voice-driven application.
+You will set up Java and Maven, define the project dependencies, and build a
+`ScienceTeacherAgent`. You will test it as text streaming in the Dev UI first, then turn on
+live audio and talk to it.
## **Create your first agent** {#create-your-first-agent}
@@ -10,7 +12,7 @@ You'll begin by setting up your Java and Maven environment, structuring your pro
* In this getting started guide, you will be programming in Java. Check if **Java** is installed on your machine. Ideally, you should be using Java 17 or more (you can check that by typing **java \-version**)
-* You’ll also be using the **Maven** build tool for Java. So be sure to have [Maven installed](https://maven.apache.org/install.html) on your machine before going further (this is the case for Cloud Top or Cloud Shell, but not necessarily for your laptop).
+* You’ll also be using the **Maven** build tool for Java, so install [Maven](https://maven.apache.org/install.html) before going further (Cloud Top and Cloud Shell already have it; your laptop may not).
### **Prepare the project structure**
@@ -573,6 +575,5 @@ With this message, the tool is ready to take voice input. Talk to the agent with
!!! Caution
When you observe the agent keep speaking by itself and doesn't stop, try using earphones to suppress the echoing.
-## **Summary** {#summary}
-
-Streaming for ADK enables developers to create agents capable of low-latency, bidirectional voice and video communication, enhancing interactive experiences. The article demonstrates that text streaming is a built-in feature of ADK Agents, requiring no additional specific code, while also showcasing how to implement live audio conversations for real-time voice interaction with an agent. This allows for more natural and dynamic communication, as users can speak to and hear from the agent seamlessly.
+Next, see [Configuration](../configuration.md) to set the voice and turn detection, and
+[Tools](../tools.md) to give your live agent tools.
diff --git a/docs/live/get-started/streaming-python.md b/docs/live/get-started/streaming-python.md
index ac7134ed5c..d2847d18d0 100644
--- a/docs/live/get-started/streaming-python.md
+++ b/docs/live/get-started/streaming-python.md
@@ -6,10 +6,10 @@ With this quickstart, you'll learn to create a simple agent and use ADK Streamin
## Supported models for voice/video streaming {#supported-models}
-In order to use voice/video streaming in ADK, you will need to use Gemini models that support the Live API. You can find the **model ID(s)** that supports the Gemini Live API in the documentation:
+Voice and video streaming requires a Gemini model that supports the Live API. You can find the **model ID(s)** that support it in the documentation:
- [Google AI Studio: Gemini Live API](https://ai.google.dev/gemini-api/docs/models#live-api)
-- [Agent Platform: Gemini Live API](https://cloud.google.com/vertex-ai/generative-ai/docs/live-api)
+- [Agent Platform: Gemini Live API](https://docs.cloud.google.com/gemini-enterprise-agent-platform/models/live-api)
## 1. Setup Environment & Install ADK { #setup-environment-install-adk }
@@ -101,9 +101,9 @@ To run the agent, choose a platform from either Google AI Studio or Google Cloud
[Google Cloud](https://cloud.google.com/?e=48754805&hl=en) account and a
project.
* Set up a
- [Google Cloud project](https://cloud.google.com/vertex-ai/generative-ai/docs/start/quickstarts/quickstart-multimodal#setup-gcp)
+ [Google Cloud project](https://docs.cloud.google.com/gemini-enterprise-agent-platform/models/start)
* Set up the
- [gcloud CLI](https://cloud.google.com/vertex-ai/generative-ai/docs/start/quickstarts/quickstart-multimodal#setup-local)
+ [gcloud CLI](https://docs.cloud.google.com/gemini-enterprise-agent-platform/models/start)
* Authenticate to Google Cloud, from the terminal by running
`gcloud auth login`.
* [Enable the Agent Platform API](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com).
@@ -117,7 +117,7 @@ To run the agent, choose a platform from either Google AI Studio or Google Cloud
```
For more information on connecting to Google Cloud from ADK agents, see
-[Connect to Google Cloud and Agent Platform](/get-started/google-cloud/).
+[Connect to Google Cloud and Agent Platform](../../get-started/google-cloud.md).
## 4. Try the agent with `adk web` { #try-the-agent-with-adk-web }
@@ -187,4 +187,8 @@ Congratulations\! You've successfully created and interacted with your first Str
## Next steps: build custom streaming app
-The [Gemini Live API Toolkit development guide series](../dev-guide/part1.md) gives an overview of the server and client code for a custom asynchronous web app built with ADK Streaming, enabling real-time, bidirectional audio and text communication.
+[Build a custom server](../custom-server.md) walks through the server and client code for
+a custom asynchronous web app built with ADK, enabling real-time, bidirectional audio and
+text communication. From there, [Sessions and the streaming loop](../sessions.md) covers
+the application lifecycle in depth, and [Events](../events.md) covers everything
+`run_live()` hands back to you.
diff --git a/docs/live/index.md b/docs/live/index.md
index c685d31486..43a4e527a9 100644
--- a/docs/live/index.md
+++ b/docs/live/index.md
@@ -1,17 +1,20 @@
-# ADK Gemini Live API Toolkit
+# Live and Voice Agents
- Supported in ADKPython v0.5.0Experimental
+ Supported in ADKPython v0.1.0Java v0.2.0
-
-Gemini Live API Toolkit in ADK adds the low-latency bidirectional voice and video interaction
-capability of [Gemini Live API](https://ai.google.dev/gemini-api/docs/live) to
-AI agents.
-With ADK Gemini Live API Toolkit, you can provide end users with the experience of natural,
-human-like voice conversations, including the ability for the user to interrupt
-the agent's responses with voice commands. Agents with streaming can process
-text, audio, and video inputs, and they can provide text and audio output.
+ADK is the framework for building live and voice agents. A live agent holds an open, two-way
+connection with the user: instead of sending a message and waiting for a reply, the user and
+the agent both speak, listen, and respond at the same time, and the user can interrupt the
+agent mid-sentence the way people interrupt each other in real conversation. Live agents
+accept text, audio, and video input and reply with text or speech.
+
+A live agent is an ADK agent, built with the same agent, tool, and session abstractions you
+use everywhere else. You describe the agent's behavior; ADK manages the real-time connection,
+tool execution, and session state underneath. Today that connection runs on the
+[Gemini Live API](https://ai.google.dev/gemini-api/docs/live-api); ADK handles the wiring so
+your agent code stays the same as the platform evolves.
@@ -26,76 +29,114 @@ text, audio, and video inputs, and they can provide text and audio output.
-## Live Demos
+## Start here
-- :material-shopping-outline: **LensMosaic: Visual Shopping with Live AI**
+- :material-rocket-launch-outline: **Get started**
---
- [](https://lens-mosaic-nhhfh7g7iq-uc.a.run.app)
+ Build your first live agent and talk to it in the browser.
- A demo app that merges live camera input, voice interaction, and intelligent product discovery. Point your camera at any object to find similar products, combine visual and voice input for personalized recommendations, or chat with a real-time AI shopping assistant. Built with ADK Gemini Live API Toolkit, Gemini Embedding, Vector Search, and FastAPI.
+ - [Start here](get-started/index.md) — pick a language and build one
+ - Jump straight to [Python](get-started/streaming-python.md) or
+ [Java](get-started/streaming-java.md)
- - [LensMosaic Demo](https://lens-mosaic-nhhfh7g7iq-uc.a.run.app)
- - [Source Code](https://github.com/kazunori279/lens-mosaic)
+- :material-book-open-variant: **Building**
-
+ ---
-
+ The capability pages, roughly in the order you will need them.
+
+ - [Sessions](sessions.md) — `run_live()`, resumption, scale
+ - [Events](events.md) — what comes back and how to handle it
+ - [Tools](tools.md) — automatic execution and streaming tools
+ - [Workflows](workflows.md) — multi-agent under a live connection
+ - [Audio and video](audio-video.md) — formats and streaming
+ - [Configuration](configuration.md) — `RunConfig`, voice, transcription, turn detection
-- :material-console-line: **Quickstart (Gemini Live API Toolkit)**
+- :material-server-network: **Production**
---
- In this quickstart, you'll build a simple agent and use streaming in ADK to
- implement low-latency and bidirectional voice and video communication.
+ Take a live agent beyond `adk web`.
- - [Quickstart (Gemini Live API Toolkit)](get-started/streaming-python.md)
+ - [Evaluation](evaluation.md) — score voice conversations before you ship
+ - [Build a custom server](custom-server.md)
+ - [Supported models](models.md)
-- :material-console-line: **Blog post: ADK Gemini Live API Toolkit Visual Guide**
+
- ---
+## Which kind of streaming do you need?
- A visual guide to real-time multimodal AI agent development with ADK Gemini Live API Toolkit. This article provides intuitive diagrams and illustrations to help you understand how streaming works and how to build interactive AI agents.
+"Streaming" covers three different things in ADK, and picking the wrong one is a common
+source of confusion.
- - [Blog post: ADK Gemini Live API Toolkit Visual Guide](https://medium.com/google-cloud/adk-bidi-streaming-a-visual-guide-to-real-time-multimodal-ai-agent-development-62dd08c81399)
+| | What it does | User can interrupt? | Use it when | Where |
+| :---- | :---- | :---- | :---- | :---- |
+| **Server-side streaming** | One-way flow from server to client, like a live feed. | No | You push dashboard or feed updates, not a conversation. | Outside ADK |
+| **Token-level streaming** | Text arrives word by word, but you wait for it to finish before sending more. | No | You want a responsive text chat. | `StreamingMode.SSE` ([Configuration](configuration.md#streamingmode-bidi-or-sse)) |
+| **Bidirectional streaming** | Both sides speak, listen, and respond at once over one open connection. | **Yes** | You are building voice or video conversation. | `runner.run_live()` — these pages |
-- :material-console-line: **Gemini Live API Toolkit development guide series**
+These pages are about the third row.
- ---
+```mermaid
+sequenceDiagram
+ participant Client as User
+ participant Agent
+
+ Client->>Agent: "Explain the history of Japan"
+ Agent->>Client: "Sure! Japan's history is a..." (partial)
+ Client->>Agent: "Ah, wait."
+ Agent->>Client: "OK, how can I help?" [interrupted: true]
+```
+
+## Why build live agents on ADK
+
+The Live API gives you the streaming protocol. ADK gives you everything around it, so you
+write agent behavior instead of streaming infrastructure.
- A series of articles for diving deeper into the Gemini Live API Toolkit development with ADK. You can learn basic concepts and use cases, the core API, and end-to-end application design.
+| | Raw Live API (`google-genai`) | ADK |
+|---|---|---|
+| Tool execution | Manual | [Automatic](tools.md#automatic-tool-execution) |
+| Reconnection | Manual | [Automatic session resumption](sessions.md#session-resumption) |
+| Events | Custom structures | [Unified event model](events.md) |
+| Async coordination | Manual | [`LiveRequestQueue` + `run_live()`](sessions.md) |
+| Session persistence | Manual | [SQL, Agent Platform, in-memory](../sessions/index.md) |
+| Multi-agent | Not available | [Workflows, sub-agents, transfer](workflows.md) |
- - [Part 1: Introduction to ADK Gemini Live API Toolkit](dev-guide/part1.md) - Fundamentals of streaming, Live API technology, ADK architecture components, and complete application lifecycle with FastAPI examples
- - [Part 2: Sending messages with LiveRequestQueue](dev-guide/part2.md) - Upstream message flow, sending text/audio/video, activity signals, and concurrency patterns
- - [Part 3: Event handling with run_live()](dev-guide/part3.md) - Processing events, handling text/audio/transcriptions, automatic tool execution, and multi-agent workflows
- - [Part 4: Understanding RunConfig](dev-guide/part4.md) - Response modalities, streaming modes, session management, session resumption, context window compression, and quota management
- - [Part 5: How to Use Audio, Image and Video](dev-guide/part5.md) - Audio specifications, model architectures, audio transcription, voice activity detection, and proactive/affective dialog features
+## Demos and resources
-- :material-console-line: **Streaming Tools**
+
+
+- :material-shopping-outline: **LensMosaic: Visual Shopping with Live AI**
---
- Streaming tools allow tools (functions) to stream intermediate results back to agents and agents can respond to those intermediate results. For example, we can use streaming tools to monitor the changes of the stock price and have the agent react to it. Another example is we can have the agent monitor the video stream, and when there are changes in video stream, the agent can report the changes.
+ Merges live camera input, voice, and product discovery. Point your camera at any object
+ to find similar products. Built with ADK live agents, Gemini Embedding, Vector Search,
+ and FastAPI.
- - [Streaming Tools](streaming-tools.md)
+ - [Live demo](https://lens-mosaic-nhhfh7g7iq-uc.a.run.app)
+ - [Source](https://github.com/kazunori279/lens-mosaic)
-- :material-console-line: **Blog post: Google ADK + Gemini Live API**
+- :material-post-outline: **A Visual Guide to Bidi-streaming**
---
- This article shows how to use Gemini Live API Toolkit in ADK for real-time audio/video streaming. It offers a Python server example using LiveRequestQueue to build custom, interactive AI agents.
+ Diagrams and illustrations covering how streaming works and how to build interactive
+ agents with ADK.
- - [Blog post: Google ADK + Gemini Live API](https://medium.com/google-cloud/google-adk-vertex-ai-live-api-125238982d5e)
+ - [Read the post](https://medium.com/google-cloud/adk-bidi-streaming-a-visual-guide-to-real-time-multimodal-ai-agent-development-62dd08c81399)
-- :material-console-line: **Blog post: Supercharge ADK Development with Claude Code Skills**
+- :material-post-outline: **Google ADK + Gemini Live API**
---
- This article demonstrates how to use Claude Code Skills to accelerate ADK development, with an example of building a streaming chat app. Learn how to leverage AI-powered coding assistance to build better agents faster.
+ Using live agents for real-time audio/video, with a Python server example built on
+ `LiveRequestQueue`.
- - [Blog post: Supercharge ADK Development with Claude Code Skills](https://medium.com/@kazunori279/supercharge-adk-development-with-claude-code-skills-d192481cbe72)
+ - [Read the post](https://medium.com/google-cloud/google-adk-vertex-ai-live-api-125238982d5e)
diff --git a/docs/live/models.md b/docs/live/models.md
new file mode 100644
index 0000000000..1f3eee16cd
--- /dev/null
+++ b/docs/live/models.md
@@ -0,0 +1,169 @@
+# Supported models
+
+
+ Supported in ADKPython v0.1.0
+
+
+Live agents require a model that can hold a bidirectional connection; a standard Gemini
+model will not. For the models ADK supports outside live agents, and for non-Gemini
+providers, see [Models for agents](../agents/models/index.md).
+
+## Live models
+
+Live agents run on models that take audio in and produce audio out, end to end, with no
+intermediate text-to-speech stage. That is what gives them human-like speech with natural
+prosody, and it is what a standard Gemini model cannot do over a bidirectional connection.
+
+The same model has a different ID on each backend:
+
+| Model | AI Studio | Agent Platform |
+|-------|-----------|----------------|
+| Gemini 2.5 Flash Live | `gemini-2.5-flash-native-audio-preview-12-2025` | `gemini-live-2.5-flash-native-audio` |
+
+`gemini-live-2.5-flash-native-audio` is ADK's `LlmAgent.DEFAULT_LIVE_MODEL` and the model
+used in this section's examples.
+
+## Choosing a backend
+
+Live models are reached through one of two backends. ADK talks to both with the same code;
+you switch with environment variables, so you can develop on one and deploy on the other.
+
+| | AI Studio | Agent Platform |
+|---|---|---|
+| **Full name** | Google AI Studio | Gemini Enterprise Agent Platform |
+| **Best for** | Prototyping, development | Production, enterprise |
+| **Auth** | API key (`GOOGLE_API_KEY`) | Cloud credentials (`GOOGLE_CLOUD_PROJECT`, `GOOGLE_CLOUD_LOCATION`) |
+| **Setup** | API key only | Cloud project setup |
+| **Limits** | [Session duration and concurrency](#platform-limits-and-quotas) | [Session duration and concurrency](#platform-limits-and-quotas) |
+
+Switch with the `GOOGLE_GENAI_USE_ENTERPRISE` environment variable (`FALSE` for AI Studio,
+`TRUE` for Agent Platform); no code changes. See the
+[quickstarts](get-started/streaming-python.md) for setup.
+
+!!! note "Agent Platform: the `global` location is not supported"
+
+ Live models are not available at `GOOGLE_CLOUD_LOCATION=global` on Agent Platform.
+ Use a regional endpoint (for example `us-central1`, `us-east1`, or `asia-northeast1`).
+ See [Agent Platform locations](https://docs.cloud.google.com/gemini-enterprise-agent-platform/resources/locations)
+ for the current list.
+
+These models produce audio directly, with natural prosody, and detect the conversation
+language on their own. What you configure on top — voices, transcription, turn detection —
+is described in [Configuration](configuration.md).
+
+One property is fixed at the model level: Live models produce **audio only**. They do not
+support the `TEXT` response modality, so to get text alongside speech you use
+[audio transcription](configuration.md#audio-transcription).
+
+### Per-model feature support
+
+A few `RunConfig` settings depend on which model you are running:
+
+| Feature | `gemini-live-2.5-flash-native-audio` |
+|---|---|
+| [Proactivity and affective dialog](configuration.md#proactivity-and-affective-dialog) | Opt-in via `RunConfig` |
+| [`response_scheduling`](tools.md#non-blocking-tools) on tools | Supported |
+
+## Platform limits and quotas
+
+Both backends cap how long a connection and a session can run and how many sessions run at
+once. These numbers change, so treat the upstream documentation as authoritative and verify
+before you rely on a limit in production.
+
+| Limit | AI Studio | Agent Platform |
+|---|---|---|
+| Session duration, audio-only | 15 min | 10 min |
+| Session duration, audio + video | 2 min | 10 min |
+| Concurrent sessions | 50 (Tier 1), 1,000 (Tier 2+) | Up to 1,000 per project |
+| New-connection rate | [Tier-based](https://ai.google.dev/gemini-api/docs/quota) | 10 per minute |
+
+Enabling [context window compression](sessions.md#context-window-compression) removes the
+session-duration limits on both backends. On Agent Platform, request concurrent-session
+increases from the [Cloud Console Quotas page](https://console.cloud.google.com/iam-admin/quotas)
+under **"Bidi generate content concurrent requests"**. Verify the current numbers against the
+[AI Studio](https://ai.google.dev/gemini-api/docs/live-api/capabilities),
+[Gemini API quotas](https://ai.google.dev/gemini-api/docs/quota), and
+[Agent Platform](https://docs.cloud.google.com/gemini-enterprise-agent-platform/models/live-api)
+documentation.
+
+## How to handle model names
+
+Read the model name from an environment variable rather than hard-coding it. The same model
+has a different ID on AI Studio and Agent Platform, so an env var is what lets one codebase
+target both backends, and it insulates you from model deprecations.
+
+**Recommended Pattern:**
+
+```python
+import os
+from google.adk.agents import Agent
+
+# Use environment variable with fallback to a sensible default
+agent = Agent(
+ name="my_agent",
+ model=os.getenv("DEMO_AGENT_MODEL", "gemini-live-2.5-flash-native-audio"),
+ tools=[...],
+ instruction="..."
+)
+```
+
+**Why use environment variables:**
+
+- **Backend-specific IDs**: The same model is named differently on AI Studio and Agent Platform, so moving between them means changing the model ID. An env var keeps that out of your code
+- **Model availability changes**: Models are released and deprecated regularly. A live agent written a year ago should not be pinned in code to a model that no longer exists
+- **Environment-specific configuration**: Use different models for development, staging, and production
+
+**Configuration in `.env` file:**
+
+```bash
+# AI Studio
+DEMO_AGENT_MODEL=gemini-2.5-flash-native-audio-preview-12-2025
+
+# Agent Platform
+# DEMO_AGENT_MODEL=gemini-live-2.5-flash-native-audio
+```
+
+!!! note "Environment Variable Loading Order"
+
+ When using `.env` files with `python-dotenv`, you must call `load_dotenv()` **before** importing any modules that read environment variables. Otherwise, `os.getenv()` will return `None` and fall back to the default value, ignoring your `.env` configuration.
+
+ **Correct order in `main.py`:**
+
+ ```python
+ from dotenv import load_dotenv
+ from pathlib import Path
+
+ # Load .env file BEFORE importing agent
+ load_dotenv(Path(__file__).parent / ".env")
+
+ # Now safe to import modules that use environment variables
+ from google_search_agent.agent import agent
+ ```
+
+ **Incorrect order (will not work):**
+
+ ```python
+ from dotenv import load_dotenv
+ from google_search_agent.agent import agent # Agent reads env var here
+
+ # Too late! Agent already initialized with default model
+ load_dotenv(Path(__file__).parent / ".env")
+ ```
+
+ This is a Python import behavior: when you import a module, its top-level code executes immediately. If your agent module calls `os.getenv("DEMO_AGENT_MODEL")` at import time, the `.env` file must already be loaded.
+
+**Selecting the right model:**
+
+1. **Choose a backend**: AI Studio for prototyping, Agent Platform for production. This picks the ID column in the table above
+2. **Check current availability**: Refer to the model table above and the official documentation
+3. **Configure environment variable**: Set the model name in your `.env` file and read it from there when constructing the agent
+
+## Model compatibility and availability
+
+For the latest information on model compatibility and availability:
+
+- **AI Studio**: See the [Gemini models documentation](https://ai.google.dev/gemini-api/docs/models) and the [Live API capabilities guide](https://ai.google.dev/gemini-api/docs/live-api/capabilities)
+- **Agent Platform**: See the [Live API overview](https://docs.cloud.google.com/gemini-enterprise-agent-platform/models/live-api) and the [Agent Platform model documentation](https://docs.cloud.google.com/gemini-enterprise-agent-platform/models/google-models)
+
+Always verify model availability and feature support in the official documentation before deploying to production.
+
diff --git a/docs/live/sessions.md b/docs/live/sessions.md
new file mode 100644
index 0000000000..df05e09cfc
--- /dev/null
+++ b/docs/live/sessions.md
@@ -0,0 +1,344 @@
+# Sessions
+
+
+ Supported in ADKPython v0.1.0
+
+
+A live agent is a connection that stays open while the user talks, listens, interrupts, and
+falls silent.
+
+Live agents use the same `Session`, `SessionService`, and state model as any ADK agent, all
+covered in [Conversational context](../sessions/index.md). What a live session adds is a
+*connection*: one that can drop, time out, or outlive the model's context window. For what
+comes *back* out of that connection, see [Events](events.md); for the settings that shape
+it, see [Configuration](configuration.md).
+
+## Set up a live application
+
+A live application has two kinds of objects: ones you create once at startup and reuse for
+every session, and ones you create fresh per session.
+
+**Create once, reuse everywhere:**
+
+- **`Agent`**: your model, tools, and instructions. Stateless and reusable.
+- **`SessionService`**: stores conversation history so sessions survive reconnects and
+ restarts.
+- **`Runner`**: the runtime that drives the agent and yields events.
+
+```python
+import os
+from google.adk.agents import Agent
+from google.adk.runners import Runner
+from google.adk.sessions import InMemorySessionService
+from google.adk.tools import google_search
+
+APP_NAME = "live-agent"
+
+agent = Agent(
+ name="google_search_agent",
+ model=os.getenv("DEMO_AGENT_MODEL", "gemini-live-2.5-flash-native-audio"),
+ tools=[google_search],
+ instruction="You are a helpful assistant that can search the web.",
+)
+
+runner = Runner(
+ app_name=APP_NAME,
+ agent=agent,
+ session_service=InMemorySessionService(),
+)
+```
+
+`InMemorySessionService` loses state when the process stops. For production, use
+`DatabaseSessionService` (SQLite, PostgreSQL, or MySQL) or `VertexAiSessionService` (managed
+on Google Cloud). See [Session services](../sessions/index.md).
+
+**Create per session:**
+
+- A [`Session`](#adk-session-vs-live-api-session), fetched or created before the loop runs.
+- A [`RunConfig`](configuration.md), which can differ per user (voice, transcription, limits).
+- A [`LiveRequestQueue`](#liverequestqueue), the channel you send user input through.
+
+```python
+from google.adk.agents.live_request_queue import LiveRequestQueue
+from google.adk.agents.run_config import RunConfig
+from google.genai import types
+
+# Get-or-create handles both new conversations and reconnections.
+session = await session_service.get_session(
+ app_name=APP_NAME, user_id=user_id, session_id=session_id
+)
+if not session:
+ await session_service.create_session(
+ app_name=APP_NAME, user_id=user_id, session_id=session_id
+ )
+
+run_config = RunConfig(
+ response_modalities=["AUDIO"],
+ session_resumption=types.SessionResumptionConfig(),
+)
+
+live_request_queue = LiveRequestQueue()
+```
+
+`user_id` and `session_id` are arbitrary strings you define; ADK generates a UUID if you
+pass `session_id=None`. The session must exist before you call `run_live()` with the same
+identifiers, or `run_live()` raises `ValueError: Session not found`.
+
+!!! warning "One queue per session"
+
+ Never reuse a `LiveRequestQueue` across sessions. The close signal persists in the queue
+ and would carry over, corrupting the next session. Create a fresh queue for every
+ `run_live()` call.
+
+## LiveRequestQueue
+
+`LiveRequestQueue` is your channel for sending messages to the agent. Every message is a
+`LiveRequest`, a single container for the different kinds of input:
+
+```python title='Reference:
LiveRequestQueue'
+class LiveRequest(BaseModel):
+ content: Optional[Content] = None # Text and structured data
+ blob: Optional[Blob] = None # Audio/video bytes
+ activity_start: Optional[ActivityStart] = None # Manual turn start
+ activity_end: Optional[ActivityEnd] = None # Manual turn end
+ close: bool = False # Graceful termination
+```
+
+`content` and `blob` are mutually exclusive. Use the convenience methods rather than building
+`LiveRequest` objects yourself; they set the right field and keep you within that constraint.
+
+| Method | Sends | Mode |
+|--------|-------|------|
+| `send_content(content)` | Text, as a discrete turn | Turn-by-turn; triggers a response |
+| `send_realtime(blob)` | Audio, image, or video bytes | Continuous streaming |
+| `send_activity_start()` / `send_activity_end()` | Manual turn boundaries | Only when automatic VAD is disabled |
+| `close()` | Termination signal | Ends the session |
+
+```python
+from google.genai import types
+
+# Text turn.
+live_request_queue.send_content(types.Content(parts=[types.Part(text=user_text)]))
+
+# Audio chunk (streamed continuously).
+live_request_queue.send_realtime(
+ types.Blob(mime_type="audio/pcm;rate=16000", data=audio_data)
+)
+```
+
+For audio, image, and video formats, see [Audio and video](audio-video.md). For manual turn
+control with activity signals, see
+[Voice activity detection](configuration.md#voice-activity-detection-vad).
+
+!!! note "Send one text Part per call"
+
+ Send a single text `Part` per `send_content()` call. Some Live models treat a multi-part
+ `Content` as conversation seeding (priming history) rather than a turn to respond to, so
+ one Part per call keeps behavior consistent across models.
+
+### Concurrency and ordering
+
+`LiveRequestQueue` wraps an `asyncio.Queue`, which has three consequences:
+
+- **Send methods are synchronous.** They call `put_nowait()` underneath, so they never block
+ and never need `await`.
+- **Delivery is FIFO and uncoalesced.** Requests reach the model in send order, one per call.
+- **The queue is unbounded.** Sending faster than the model consumes grows memory rather than
+ applying backpressure, so cap your own send rate for high-rate audio or video.
+
+Create the queue inside an async context so it binds to the event loop that runs
+`run_live()`. `asyncio.Queue` is safe for concurrent access within a single event loop
+thread; to feed it from another thread, use `loop.call_soon_threadsafe()`.
+
+## The run_live() loop
+
+`run_live()` is an async generator. It yields `Event` objects the moment they are generated,
+with no buffering or polling, while you send new input concurrently through the queue. That
+concurrency is what makes interruption work: the agent can be speaking while the user starts
+talking over it.
+
+```python title='Reference:
Runner.run_live()'
+async for event in runner.run_live(
+ user_id=user_id,
+ session_id=session_id,
+ live_request_queue=live_request_queue,
+ run_config=run_config,
+):
+ await websocket.send_text(event.model_dump_json(exclude_none=True, by_alias=True))
+```
+
+`run_live()` opens the Live API connection when you call it, streams both directions while
+the loop runs, and closes the connection when you call `live_request_queue.close()`. For the
+event types it yields and how to handle them, see [Events](events.md).
+
+### When run_live() exits
+
+| Exit condition | Trigger | Graceful |
+|---|---|---|
+| Manual close | `live_request_queue.close()` | Yes |
+| Workflow complete | Last agent in a live workflow calls `task_completed()` | Yes |
+| Session timeout | Live API duration limit reached (without compression) | Connection closed |
+| Early exit | `end_invocation` set by a tool or callback | Yes |
+| Error | Connection failure or unhandled exception | No |
+
+Always call `close()` when the session ends, even on error. Skipping it leaves the Live API
+without a graceful termination signal, which can strand "zombie" sessions that count against
+your [concurrent-session quota](#concurrent-sessions) until they time out.
+
+```python
+try:
+ await asyncio.gather(upstream_task(), downstream_task())
+except WebSocketDisconnect:
+ pass # Client disconnected normally.
+finally:
+ live_request_queue.close() # Always close the queue.
+```
+
+For error handling inside the loop, see [Error events](events.md#handling-errors). For the full
+upstream/downstream server pattern, see [Custom server](custom-server.md).
+
+### What gets saved to the session
+
+When `run_live()` exits, only some events persist to the ADK `Session`:
+
+- **Saved:** final (non-partial) transcriptions, usage metadata, function calls and
+ responses, and most control events. Audio files are saved only when
+ [`save_live_blob`](configuration.md#save_live_blob) is `True`.
+- **Ephemeral:** raw audio bytes (`inline_data`) and partial transcriptions, yielded for
+ real-time playback and display but not stored.
+
+## ADK Session vs Live API session
+
+Two different things share the word "session":
+
+- **ADK `Session`** (managed by `SessionService`) is persistent conversation storage. It
+ survives across many `run_live()` calls and application restarts.
+- **Live API session** (managed by the Live API backend) is a transient streaming context
+ that exists only while the loop runs.
+
+When `run_live()` starts, ADK loads history from the ADK `Session`, initializes a new Live
+API session with it, and updates the ADK `Session` as events occur. When the loop ends, the
+Live API session is destroyed and the ADK `Session` persists. The next call rebuilds a Live
+API session from the stored history. This separation is what lets conversations continue
+across network drops and restarts.
+
+At the transport layer, one more distinction matters for reliability:
+
+- A **connection** is the WebSocket link between ADK and the Live API. It can time out.
+- A **session** is the conversation context, which can span multiple connections through
+ [session resumption](#session-resumption).
+
+### Platform limits
+
+Both backends cap connection duration, session duration, and concurrent sessions. The exact
+numbers differ by backend and change over time, so
+[Supported models](models.md#platform-limits-and-quotas) tracks them in one place.
+
+Two of those caps change how you write the code.
+[Context window compression](#context-window-compression) lifts the session-duration limit,
+and the concurrent-session ceiling is what you design against in
+[Concurrent sessions](#concurrent-sessions).
+
+## Session resumption
+
+The Live API closes each WebSocket connection after about 10 minutes.
+[Session resumption](https://ai.google.dev/gemini-api/docs/live-api/session-management#session-resumption)
+migrates the conversation across connections so it continues past that limit. Enable it and
+**ADK handles all reconnection for you**, caching resumption handles, detecting closures,
+and reconnecting in the background. Your `run_live()` loop keeps yielding events without
+interruption.
+
+```python
+from google.genai import types
+
+run_config = RunConfig(session_resumption=types.SessionResumptionConfig())
+```
+
+ADK manages the ADK-to-Live-API connection only. Your application still owns its own client
+connections (for example, the user's WebSocket to your server) and any client-side reconnect
+logic.
+
+How ADK reconnects:
+
+1. The Live API sends `session_resumption_update` messages; ADK caches the latest handle.
+2. Before the limit, the Live API may send a `go_away` warning; ADK reconnects *before* the
+ drop, so the handover is invisible.
+3. When a connection closes gracefully, ADK's loop reconnects with the cached handle and the
+ session continues with full context.
+
+```mermaid
+sequenceDiagram
+ participant App as Your Application
+ participant ADK as ADK (run_live)
+ participant API as Live API
+
+ App->>ADK: run_live(run_config with session_resumption)
+ ADK->>API: WebSocket connect()
+ Note over ADK,API: Streaming (0-10 min)
+ API-->>ADK: session_resumption_update { handle }
+ ADK->>ADK: Cache handle
+ Note over API: ~10 min: connection closes gracefully
+ ADK->>API: reconnect(handle)
+ API-->>ADK: Session resumed with full context
+ Note over App,API: Loop continues, uninterrupted
+```
+
+!!! warning "Reconnection attempts are capped"
+
+ ADK retries a maximum of **5 consecutive** reconnections
+ ([`DEFAULT_MAX_RECONNECT_ATTEMPTS`](https://github.com/google/adk-python/blob/main/src/google/adk/flows/llm_flows/base_llm_flow.py)).
+ The counter resets on each successful reconnect, so a long conversation is limited only to
+ five *failures in a row*, not five reconnects total. ADK retries only when a resumption
+ handle exists; without `session_resumption` enabled, the first drop propagates straight
+ out of `run_live()`, and your application must handle it.
+
+Skip resumption only for short sessions (under 10 minutes), stateless request-response
+interactions, or development where a fresh session per run aids debugging.
+
+## Context window compression
+
+Long conversations hit two limits: the session duration caps, and the model's context window
+(varies by model).
+[Context window compression](https://ai.google.dev/gemini-api/docs/live-api/session-management#context-window-compression)
+addresses both. It compresses older conversation history with a sliding window when the token
+count crosses a threshold, keeping recent turns in full. **Enabling it removes the session
+duration limits.** The trade-off: older context becomes a summary, not verbatim history.
+
+```python
+from google.genai import types
+from google.adk.agents.run_config import RunConfig
+
+# For a 128k-context model.
+run_config = RunConfig(
+ context_window_compression=types.ContextWindowCompressionConfig(
+ trigger_tokens=100000, # Start compressing near ~78% of the window.
+ sliding_window=types.SlidingWindow(
+ target_tokens=80000, # Compress down to ~62%, keeping recent turns.
+ ),
+ )
+)
+```
+
+Set `trigger_tokens` to roughly 70-80% of the model's context window for headroom, and
+`target_tokens` to 60-70% so each compression frees enough room for several turns. Test with
+your own conversation patterns. Enable compression when sessions must run longer than the
+platform limits or may exceed the token limit; leave it off for short sessions or when
+precise recall of early turns is critical.
+
+## Concurrent sessions { #concurrent-sessions }
+
+Each user needs their own Live API session, and both backends cap concurrent sessions. Your
+concurrent-session ceiling is a hard cap on simultaneous users. For the current ceilings and
+how to request increases, see [Supported models](models.md#platform-limits-and-quotas).
+
+Design for the ceiling:
+
+- **One session per user** is the default and correct choice while peak concurrency fits
+ inside the quota.
+- **A session pool** (a fixed set of sessions handed out through a queue) keeps you inside
+ the quota when peak concurrency exceeds it, at the cost of wait time. Reset per-session
+ state on release so conversations do not leak between users.
+
+Either way, count active sessions yourself and queue or reject new connections before the
+platform does. A quota rejection surfaces as a connection failure, a worse experience than a
+visible queue position.
diff --git a/docs/live/streaming-tools.md b/docs/live/streaming-tools.md
deleted file mode 100644
index 9eeaafcdef..0000000000
--- a/docs/live/streaming-tools.md
+++ /dev/null
@@ -1,255 +0,0 @@
-# Streaming Tools
-
-
- Supported in ADKPython v0.5.0Java v0.2.0Experimental
-
-
-Streaming tools allows tools(functions) to stream intermediate results back to agents and agents can respond to those intermediate results.
-For example, we can use streaming tools to monitor the changes of the stock price and have the agent react to it. Another example is we can have the agent monitor the video stream, and when there is changes in video stream, the agent can report the changes.
-
-!!! info
-
- This is only supported in ADK Gemini Live APIs.
-
-To define a streaming tool, you must adhere to the following:
-
-1. **Asynchronous Function:** The tool must be an `async` Python function.
-2. **AsyncGenerator Return Type:** The function must be typed to return an `AsyncGenerator`. The first type parameter to `AsyncGenerator` is the type of the data you `yield` (e.g., `str` for text messages, or a custom object for structured data). The second type parameter is typically `None` if the generator doesn't receive values via `send()`.
-
-
-We support two types of streaming tools:
-- Simple type. This is a one type of streaming tools that only take non-video/-audio streams(the streams that you feed to adk web or adk runner) as input.
-- Video streaming tools. This only works in video streaming and the video stream(the streams that you feed to adk web or adk runner) will be passed into this function.
-
-Now let's define an agent that can monitor stock price changes and monitor the video stream changes.
-
-=== "Python"
-
- ```python
- import asyncio
- from typing import AsyncGenerator
-
- from google.adk.agents import LiveRequestQueue
- from google.adk.agents.llm_agent import Agent
- from google.adk.tools.function_tool import FunctionTool
- from google.genai import Client
- from google.genai import types as genai_types
-
-
- async def monitor_stock_price(stock_symbol: str) -> AsyncGenerator[str, None]:
- """This function will monitor the price for the given stock_symbol in a continuous, streaming and asynchronously way."""
- print(f"Start monitor stock price for {stock_symbol}!")
-
- # Let's mock stock price change.
- await asyncio.sleep(4)
- price_alert1 = f"the price for {stock_symbol} is 300"
- yield price_alert1
- print(price_alert1)
-
- await asyncio.sleep(4)
- price_alert1 = f"the price for {stock_symbol} is 400"
- yield price_alert1
- print(price_alert1)
-
- await asyncio.sleep(20)
- price_alert1 = f"the price for {stock_symbol} is 900"
- yield price_alert1
- print(price_alert1)
-
- await asyncio.sleep(20)
- price_alert1 = f"the price for {stock_symbol} is 500"
- yield price_alert1
- print(price_alert1)
-
-
- # for video streaming, `input_stream: LiveRequestQueue` is required and reserved key parameter for ADK to pass the video streams in.
- async def monitor_video_stream(
- input_stream: LiveRequestQueue,
- ) -> AsyncGenerator[str, None]:
- """Monitor how many people are in the video streams."""
- print("start monitor_video_stream!")
- client = Client(enterprise=False)
- prompt_text = (
- "Count the number of people in this image. Just respond with a numeric"
- " number."
- )
- last_count = None
- while True:
- last_valid_req = None
- print("Start monitoring loop")
-
- # use this loop to pull the latest images and discard the old ones
- while input_stream._queue.qsize() != 0:
- live_req = await input_stream.get()
-
- if live_req.blob is not None and live_req.blob.mime_type == "image/jpeg":
- last_valid_req = live_req
-
- # If we found a valid image, process it
- if last_valid_req is not None:
- print("Processing the most recent frame from the queue")
-
- # Create an image part using the blob's data and mime type
- image_part = genai_types.Part.from_bytes(
- data=last_valid_req.blob.data, mime_type=last_valid_req.blob.mime_type
- )
-
- contents = genai_types.Content(
- role="user",
- parts=[image_part, genai_types.Part.from_text(prompt_text)],
- )
-
- # Call the model to generate content based on the provided image and prompt
- response = client.models.generate_content(
- model="gemini-flash-latest",
- contents=contents,
- config=genai_types.GenerateContentConfig(
- system_instruction=(
- "You are a helpful video analysis assistant. You can count"
- " the number of people in this image or video. Just respond"
- " with a numeric number."
- )
- ),
- )
- if not last_count:
- last_count = response.candidates[0].content.parts[0].text
- elif last_count != response.candidates[0].content.parts[0].text:
- last_count = response.candidates[0].content.parts[0].text
- yield response
- print("response:", response)
-
- # Wait before checking for new images
- await asyncio.sleep(0.5)
-
-
- # Use this exact function to help ADK stop your streaming tools when requested.
- # for example, if we want to stop `monitor_stock_price`, then the agent will
- # invoke this function with stop_streaming(function_name=monitor_stock_price).
- def stop_streaming(function_name: str):
- """Stop the streaming
-
- Args:
- function_name: The name of the streaming function to stop.
- """
- pass
-
-
- root_agent = Agent(
- model="gemini-flash-latest",
- name="video_streaming_agent",
- instruction="""
- You are a monitoring agent. You can do video monitoring and stock price monitoring
- using the provided tools/functions.
- When users want to monitor a video stream,
- You can use monitor_video_stream function to do that. When monitor_video_stream
- returns the alert, you should tell the users.
- When users want to monitor a stock price, you can use monitor_stock_price.
- Don't ask too many questions. Don't be too talkative.
- """,
- tools=[
- monitor_video_stream,
- monitor_stock_price,
- FunctionTool(stop_streaming),
- ]
- )
- ```
-
-=== "Java"
-
- ```java
- import com.google.adk.agents.LiveRequestQueue;
- import com.google.adk.agents.LlmAgent;
- import com.google.adk.tools.Annotations.Schema;
- import com.google.adk.tools.FunctionTool;
- import com.google.genai.Client;
- import com.google.genai.types.Content;
- import com.google.genai.types.GenerateContentConfig;
- import com.google.genai.types.GenerateContentResponse;
- import com.google.genai.types.Part;
- import io.reactivex.rxjava3.core.Flowable;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.Map;
- import java.util.concurrent.TimeUnit;
-
- public class StreamingTools {
-
- @Schema(description = "This function will monitor the price for the given stock_symbol in a continuous, streaming and asynchronously way.")
- public static Flowable