fix(runners): Ensure event compaction completes by awaiting task

Fixes https://github.com/google/adk-python/issues/3174

The event compaction process, configured via `EventsCompactionConfig`, was
previously scheduled as a background task using `asyncio.create_task`.
Because Python's `asyncio.create_task` only holds a weak reference to
the created task and no strong reference was maintained by ADK, the
compaction task could be garbage-collected before it finished executing.
This resulted in event compaction failing silently or only partially
running, preventing session history from being summarized.

### Approaches Considered

Two approaches were considered to fix this:

1.  **`asyncio.create_task` + Reference:** Create the task with `create_task` and store a strong reference to it (e.g., in a `set` on the [Runner](http://_vscodecontentref_/0) instance), removing it only when complete via `task.add_done_callback()`.
    *   **Pros:** The [run_async](http://_vscodecontentref_/1) async generator finishes immediately after yielding the last agent event.
    *   **Cons:** Adds complexity to the Runner state; background task failures are silent to the [run_async](http://_vscodecontentref_/2) caller; requires enhancement to `runner.close()` to correctly manage pending tasks on shutdown.
2.  **`await`:** Directly `await` the compaction coroutine at the end of [run_async](http://_vscodecontentref_/3) after all agent events have been yielded.
    *   **Pros:** Simple to implement; ensures compaction runs to completion; failures during compaction propagate immediately to the [run_async](http://_vscodecontentref_/4) caller, making them visible and easier to debug.
    *   **Cons:** The `async for` loop iterating over [run_async](http://_vscodecontentref_/5) will not terminate until compaction finishes.

### Decision

This change implements the `await` approach. Although it means the `async for` loop takes longer to terminate when compaction occurs, it was chosen for its **simplicity and robustness**. Ensuring that compaction either succeeds or fails visibly is preferable to silent background failures.

All agent response events are yielded *before* compaction starts, so there is **no user-perceived delay in receiving the agent's answer** for the current turn.

### Integration Note for Users

Because compaction is now awaited, code consuming events via `async for event in runner.run_async(...)` will only finish iterating *after* compaction is complete (if compaction is triggered for that invocation).

If your application only needs the agent's response to proceed (e.g., displaying a message in a UI and allowing the user to reply), you can process events as they arrive and initiate the next turn without waiting for the `async for` loop to fully terminate. A new call to [run_async](http://_vscodecontentref_/6) for the next user query can be made immediately and will execute concurrently.

**Example:**

```python
async def handle_agent_turn(runner, message):
    print("Agent is thinking...")
    async for event in runner.run_async(user_id='...', session_id='...', new_message=message):
        # Stream events to UI, log, etc.
        if event.author == 'model' and event.content and event.content.parts[0].text:
            print(f"Agent response: {event.content.parts[0].text}")
            # The agent has provided a text response.
            # The application can now enable user input for the next turn,
            # even though this async for loop might not finish immediately
            # if compaction is running.
    print("Invocation complete (including compaction if any).")

# In your application:
# A new call to handle_agent_turn(runner, next_message) can be made
# as soon as the user provides the next input, without waiting for
# the previous call's generator to be exhausted.

Co-authored-by: Hangfei Lin <hangfei@google.com>
PiperOrigin-RevId: 833885375
This commit is contained in:
Hangfei Lin
2025-11-18 10:57:21 -08:00
committed by Copybara-Service
parent a12ae812d3
commit b5f5df9fa8
2 changed files with 268 additions and 209 deletions
+252 -200
View File
@@ -1,22 +1,80 @@
# AI Coding Assistant Context
This document provides context for AI coding assistants (Claude Code, Gemini CLI, GitHub Copilot, Cursor, etc.) to understand the ADK Python project and assist with development.
This document provides context for AI coding assistants (Claude Code, Gemini
CLI, GitHub Copilot, Cursor, etc.) to understand the ADK Python project and
assist with development.
## Project Overview
The Agent Development Kit (ADK) is an open-source, code-first Python toolkit for building, evaluating, and deploying sophisticated AI agents with flexibility and control. While optimized for Gemini and the Google ecosystem, ADK is model-agnostic, deployment-agnostic, and is built for compatibility with other frameworks. ADK was designed to make agent development feel more like software development, to make it easier for developers to create, deploy, and orchestrate agentic architectures that range from simple tasks to complex workflows.
The Agent Development Kit (ADK) is an open-source, code-first Python toolkit for
building, evaluating, and deploying sophisticated AI agents with flexibility and
control. While optimized for Gemini and the Google ecosystem, ADK is
model-agnostic, deployment-agnostic, and is built for compatibility with other
frameworks. ADK was designed to make agent development feel more like software
development, to make it easier for developers to create, deploy, and orchestrate
agentic architectures that range from simple tasks to complex workflows.
### Key Components
- **Agent** - Blueprint defining identity, instructions, and tools (`LlmAgent`, `LoopAgent`, `ParallelAgent`, `SequentialAgent`, etc.)
- **Runner** - Execution engine that orchestrates the "Reason-Act" loop, manages LLM calls, executes tools, and handles multi-agent coordination
- **Tool** - Functions/capabilities agents can call (Python functions, OpenAPI specs, MCP tools, Google API tools)
- **Session** - Conversation state management (in-memory, Vertex AI, Spanner-backed)
- **Memory** - Long-term recall across sessions
- **Agent** - Blueprint defining identity, instructions, and tools
(`LlmAgent`, `LoopAgent`, `ParallelAgent`, `SequentialAgent`, etc.)
- **Runner** - Execution engine that orchestrates agent execution. It manages
the 'Reason-Act' loop, processes messages within a session, generates
events, calls LLMs, executes tools, and handles multi-agent coordination. It
interacts with various services like session management, artifact storage,
and memory, and integrates with application-wide plugins. The runner
provides different execution modes: `run_async` for asynchronous execution
in production, `run_live` for bi-directional streaming interaction, and
`run` for synchronous execution suitable for local testing and debugging. At
the end of each invocation, it can perform event compaction to manage
session history size.
- **Tool** - Functions/capabilities agents can call (Python functions, OpenAPI
specs, MCP tools, Google API tools)
- **Session** - Conversation state management (in-memory, Vertex AI,
Spanner-backed)
- **Memory** - Long-term recall across sessions
### How the Runner Works
The Runner is the stateless orchestration engine that manages agent execution.
It does not hold conversation history in memory; instead, it relies on services
like `SessionService`, `ArtifactService`, and `MemoryService` for persistence.
**Invocation Lifecycle:**
Each call to `runner.run_async()` or `runner.run()` processes a single user
turn, known as an **invocation**.
1. **Session Retrieval:** When `run_async()` is called with a `session_id`, the
runner fetches the session state, including all conversation events, from
the `SessionService`.
2. **Context Creation:** It creates an `InvocationContext` containing the
session, the new user message, and references to persistence services.
3. **Agent Execution:** The runner calls `agent.run_async()` with this context.
The agent then enters its reason-act loop, which may involve:
* Calling an LLM for reasoning.
* Executing tools (function calling).
* Generating text or audio responses.
* Transferring control to sub-agents.
4. **Event Streaming & Persistence:** Each step in the agent's execution (LLM
call, tool call, tool response, model response) generates `Event` objects.
The runner streams these events back to the caller and simultaneously
appends them to the session via `SessionService`.
5. **Invocation Completion:** Once the agent has produced its final response
for the turn (e.g., a text response to the user), the agent's execution loop
finishes.
6. **Event Compaction:** If event compaction is configured, the runner may
summarize older events in the session to manage context window limits,
appending a `CompactedEvent` to the session.
7. **Next Turn:** When the user sends another message, a new `run_async()`
invocation begins, repeating the cycle by loading the session, which now
includes the events from all prior turns.
## Project Architecture
Please refer to [ADK Project Overview and Architecture](https://github.com/google/adk-python/blob/main/contributing/adk_project_overview_and_architecture.md) for details.
Please refer to
[ADK Project Overview and Architecture](https://github.com/google/adk-python/blob/main/contributing/adk_project_overview_and_architecture.md)
for details.
### Source Structure
@@ -56,63 +114,54 @@ tests/
### ADK Live (Bidi-streaming)
- ADK live feature can be accessed from runner.run_live(...) and corresponding FAST api endpoint.
- ADK live feature is built on top of [Gemini Live API](https://cloud.google.com/vertex-ai/generative-ai/docs/live-api). We integrate Gemini Live API through [GenAI SDK](https://github.com/googleapis/python-genai).
- ADK live related configs are in [run_config.py](https://github.com/google/adk-python/blob/main/src/google/adk/agents/run_config.py).
- ADK live under multi-agent scenario: we convert the audio into text. This text will be passed to next agent as context.
- Most logics are in [base_llm_flow.py](https://github.com/google/adk-python/blob/main/src/google/adk/flows/llm_flows/base_llm_flow.py) and [gemini_llm_connection.py](https://github.com/google/adk-python/blob/main/src/google/adk/models/gemini_llm_connection.py).
- Input transcription and output transcription should be added to session as Event.
- User audio or model audio should be saved into artifacts with a reference in Event to it.
- Tests are in [tests/unittests/streaming](https://github.com/google/adk-python/tree/main/tests/unittests/streaming).
- ADK live feature can be accessed from runner.run_live(...) and corresponding
FAST api endpoint.
- ADK live feature is built on top of
[Gemini Live API](https://cloud.google.com/vertex-ai/generative-ai/docs/live-api).
We integrate Gemini Live API through
[GenAI SDK](https://github.com/googleapis/python-genai).
- ADK live related configs are in
[run_config.py](https://github.com/google/adk-python/blob/main/src/google/adk/agents/run_config.py).
- ADK live under multi-agent scenario: we convert the audio into text. This
text will be passed to next agent as context.
- Most logics are in
[base_llm_flow.py](https://github.com/google/adk-python/blob/main/src/google/adk/flows/llm_flows/base_llm_flow.py)
and
[gemini_llm_connection.py](https://github.com/google/adk-python/blob/main/src/google/adk/models/gemini_llm_connection.py).
- Input transcription and output transcription should be added to session as
Event.
- User audio or model audio should be saved into artifacts with a reference in
Event to it.
- Tests are in
[tests/unittests/streaming](https://github.com/google/adk-python/tree/main/tests/unittests/streaming).
### Agent Structure Convention (Required)
**All agent directories must follow this structure:**
```
my_agent/
├── __init__.py # MUST contain: from . import agent
└── agent.py # MUST define: root_agent = Agent(...) OR app = App(...)
```
**All agent directories must follow this structure:** `my_agent/ ├──
__init__.py # MUST contain: from . import agent └── agent.py # MUST define:
root_agent = Agent(...) OR app = App(...)`
**Choose one pattern based on your needs:**
**Option 1 - Simple Agent (for basic agents without plugins):**
```python
from google.adk.agents import Agent
from google.adk.tools import google_search
**Option 1 - Simple Agent (for basic agents without plugins):** ```python from
google.adk.agents import Agent from google.adk.tools import google_search
root_agent = Agent(
name="search_assistant",
model="gemini-2.5-flash",
instruction="You are a helpful assistant.",
description="An assistant that can search the web.",
tools=[google_search]
)
```
root_agent = Agent( name="search_assistant", model="gemini-2.5-flash",
instruction="You are a helpful assistant.", description="An assistant that can
search the web.", tools=[google_search] ) ```
**Option 2 - App Pattern (when you need plugins, event compaction, custom configuration):**
```python
from google.adk import Agent
from google.adk.apps import App
from google.adk.plugins import ContextFilterPlugin
**Option 2 - App Pattern (when you need plugins, event compaction, custom
configuration):** ```python from google.adk import Agent from google.adk.apps
import App from google.adk.plugins import ContextFilterPlugin
root_agent = Agent(
name="my_agent",
model="gemini-2.5-flash",
instruction="You are a helpful assistant.",
tools=[...],
)
root_agent = Agent( name="my_agent", model="gemini-2.5-flash", instruction="You
are a helpful assistant.", tools=[...], )
app = App(
name="my_app",
root_agent=root_agent,
plugins=[
ContextFilterPlugin(num_invocations_to_keep=3),
],
)
```
app = App( name="my_app", root_agent=root_agent, plugins=[
ContextFilterPlugin(num_invocations_to_keep=3), ], ) ```
**Rationale:** This structure allows the ADK CLI (`adk web`, `adk run`, etc.) to automatically discover and load agents without additional configuration.
**Rationale:** This structure allows the ADK CLI (`adk web`, `adk run`, etc.) to
automatically discover and load agents without additional configuration.
## Development Setup
@@ -123,34 +172,31 @@ app = App(
- Python 3.10+ (**Python 3.11+ strongly recommended** for best performance)
- `uv` package manager (**required** - faster than pip/venv)
**Install uv if not already installed:**
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```
**Install uv if not already installed:** `bash curl -LsSf
https://astral.sh/uv/install.sh | sh`
### Setup Instructions
**Standard setup for development:**
```bash
**Standard setup for development:** ```bash
# Create virtual environment with Python 3.11
uv venv --python "python3.11" ".venv"
source .venv/bin/activate
uv venv --python "python3.11" ".venv" source .venv/bin/activate
# Install all dependencies for development
uv sync --all-extras
```
**Minimal setup for testing only (matches CI):**
```bash
uv sync --extra test --extra eval --extra a2a
```
uv sync --all-extras ```
**Virtual Environment Usage (Required):**
- **Always use** `.venv/bin/python` or `.venv/bin/pytest` directly
- **Or activate** with `source .venv/bin/activate` before running commands
- **Never use** `python -m venv` - always create with `uv venv` if missing
**Minimal setup for testing only (matches CI):** `bash uv sync --extra test
--extra eval --extra a2a`
**Rationale:** `uv` is significantly faster and ensures consistent dependency resolution across the team.
**Virtual Environment Usage (Required):** - **Always use** `.venv/bin/python` or
`.venv/bin/pytest` directly - **Or activate** with `source .venv/bin/activate`
before running commands - **Never use** `python -m venv` - always create with
`uv venv` if missing
**Rationale:** `uv` is significantly faster and ensures consistent dependency
resolution across the team.
### Building
@@ -164,35 +210,37 @@ pip install dist/google_adk-<version>-py3-none-any.whl
### Running Agents Locally
**For interactive development and debugging:**
```bash
**For interactive development and debugging:** ```bash
# Launch web UI (recommended for development)
adk web path/to/agents_dir
```
**For CLI-based testing:**
```bash
adk web path/to/agents_dir ```
**For CLI-based testing:** ```bash
# Interactive CLI (prompts for user input)
adk run path/to/my_agent
```
**For API/production mode:**
```bash
adk run path/to/my_agent ```
**For API/production mode:** ```bash
# Start FastAPI server
adk api_server path/to/agents_dir
```
**For running evaluations:**
```bash
adk api_server path/to/agents_dir ```
**For running evaluations:** ```bash
# Run evaluation set against agent
adk eval path/to/my_agent path/to/eval_set.json
```
adk eval path/to/my_agent path/to/eval_set.json ```
## ADK: Style Guides
### Python Style Guide
The project follows the Google Python Style Guide. Key conventions are enforced using `pylint` with the provided `pylintrc` configuration file. Here are some of the key style points:
The project follows the Google Python Style Guide. Key conventions are enforced
using `pylint` with the provided `pylintrc` configuration file. Here are some of
the key style points:
* **Indentation**: 2 spaces.
* **Line Length**: Maximum 80 characters.
@@ -200,44 +248,41 @@ The project follows the Google Python Style Guide. Key conventions are enforced
* `function_and_variable_names`: `snake_case`
* `ClassNames`: `CamelCase`
* `CONSTANTS`: `UPPERCASE_SNAKE_CASE`
* **Docstrings**: Required for all public modules, functions, classes, and methods.
* **Docstrings**: Required for all public modules, functions, classes, and
methods.
* **Imports**: Organized and sorted.
* **Error Handling**: Specific exceptions should be caught, not general ones like `Exception`.
* **Error Handling**: Specific exceptions should be caught, not general ones
like `Exception`.
### Autoformat (Required Before Committing)
**Always run** before committing code:
```bash
./autoformat.sh
```
**Always run** before committing code: `bash ./autoformat.sh`
**Manual formatting** (if needed): ```bash
**Manual formatting** (if needed):
```bash
# Format imports
isort src/ tests/ contributing/
# Format code style
pyink --config pyproject.toml src/ tests/ contributing/
```
**Check formatting** without making changes:
```bash
pyink --check --diff --config pyproject.toml src/
isort --check src/
```
pyink --config pyproject.toml src/ tests/ contributing/ ```
**Formatting Standards (Enforced by CI):**
- **Formatter:** `pyink` (Google-style Python formatter)
- **Line length:** 80 characters maximum
- **Indentation:** 2 spaces (never tabs)
- **Import sorter:** `isort` with Google profile
- **Linter:** `pylint` with Google Python Style Guide
**Check formatting** without making changes: `bash pyink --check --diff --config
pyproject.toml src/ isort --check src/`
**Rationale:** Consistent formatting eliminates style debates and makes code reviews focus on logic rather than style.
**Formatting Standards (Enforced by CI):** - **Formatter:** `pyink`
(Google-style Python formatter) - **Line length:** 80 characters maximum -
**Indentation:** 2 spaces (never tabs) - **Import sorter:** `isort` with Google
profile - **Linter:** `pylint` with Google Python Style Guide
**Rationale:** Consistent formatting eliminates style debates and makes code
reviews focus on logic rather than style.
### In ADK source
Below styles applies to the ADK source code (under `src/` folder of the GitHub repo).
Below styles applies to the ADK source code (under `src/` folder of the GitHub
repo).
#### Use relative imports (Required)
@@ -249,7 +294,8 @@ from ..agents.llm_agent import LlmAgent
from google.adk.agents.llm_agent import LlmAgent
```
**Rationale:** Relative imports make the code more maintainable and avoid circular import issues in large codebases.
**Rationale:** Relative imports make the code more maintainable and avoid
circular import issues in large codebases.
#### Import from module, not from `__init__.py` (Required)
@@ -261,11 +307,13 @@ from ..agents.llm_agent import LlmAgent
from ..agents import LlmAgent
```
**Rationale:** Direct module imports make dependencies explicit and improve IDE navigation and refactoring.
**Rationale:** Direct module imports make dependencies explicit and improve IDE
navigation and refactoring.
#### Always do `from __future__ import annotations` (Required)
**Rule:** Every source file must include `from __future__ import annotations` immediately after the license header, before any other imports.
**Rule:** Every source file must include `from __future__ import annotations`
immediately after the license header, before any other imports.
```python
# Copyright 2025 Google LLC
@@ -287,13 +335,15 @@ from __future__ import annotations # REQUIRED - Always include this
# ... rest of imports ...
```
**Rationale:** This enables forward-referencing classes without quotes, improving code readability and type hint support (PEP 563).
**Rationale:** This enables forward-referencing classes without quotes,
improving code readability and type hint support (PEP 563).
### In ADK tests
#### Use absolute imports (Required)
**Rule:** Test code must use absolute imports (`google.adk.*`) to match how users import ADK.
**Rule:** Test code must use absolute imports (`google.adk.*`) to match how
users import ADK.
```python
# DO - Use absolute imports
@@ -303,59 +353,63 @@ from google.adk.agents.llm_agent import LlmAgent
from ..agents.llm_agent import LlmAgent
```
**Rationale:** Tests should exercise the same import paths that users will use, catching issues with the public API.
**Rationale:** Tests should exercise the same import paths that users will use,
catching issues with the public API.
## ADK: Local Testing
### Unit Tests
**Quick start:** Run all tests with:
```bash
pytest tests/unittests
```
**Quick start:** Run all tests with: `bash pytest tests/unittests`
**Recommended:** Match CI configuration before submitting PRs:
```bash
uv sync --extra test --extra eval --extra a2a && pytest tests/unittests
```
**Recommended:** Match CI configuration before submitting PRs: `bash uv sync
--extra test --extra eval --extra a2a && pytest tests/unittests`
**Additional options:** ```bash
**Additional options:**
```bash
# Run tests in parallel for faster execution
pytest tests/unittests -n auto
# Run a specific test file during development
pytest tests/unittests/agents/test_llm_agent.py
```
### Testing Philosophy
**Use real code over mocks:** ADK tests should use real implementations as much as possible instead of mocking. Only mock external dependencies like network calls or cloud services.
**Use real code over mocks:** ADK tests should use real implementations as much
as possible instead of mocking. Only mock external dependencies like network
calls or cloud services.
**Test interface behavior, not implementation details:** Tests should verify that the public API behaves correctly, not how it's implemented internally. This makes tests resilient to refactoring and ensures the contract with users remains intact.
**Test interface behavior, not implementation details:** Tests should verify
that the public API behaves correctly, not how it's implemented internally. This
makes tests resilient to refactoring and ensures the contract with users remains
intact.
**Test Requirements:**
- Fast and isolated tests where possible
- Use real ADK components; mock only external dependencies (LLM APIs, cloud services, etc.)
- Focus on testing public interfaces and behavior, not internal implementation
- Descriptive test names that explain what behavior is being tested
- High coverage for new features, edge cases, and error conditions
- Location: `tests/unittests/` following source structure
**Test Requirements:** - Fast and isolated tests where possible - Use real ADK
components; mock only external dependencies (LLM APIs, cloud services, etc.) -
Focus on testing public interfaces and behavior, not internal implementation -
Descriptive test names that explain what behavior is being tested - High
coverage for new features, edge cases, and error conditions - Location:
`tests/unittests/` following source structure
## Docstring and comments
### Comments - Explaining the Why, Not the What
Philosophy: Well-written code should be largely self-documenting. Comments
serve a different purpose: they should explain the complex algorithms,
non-obvious business logic, or the rationale behind a particular implementation
choice—the things the code cannot express on its own. Avoid comments that
merely restate what the code does (e.g., # increment i above i += 1).
Philosophy: Well-written code should be largely self-documenting. Comments serve
a different purpose: they should explain the complex algorithms, non-obvious
business logic, or the rationale behind a particular implementation choice—the
things the code cannot express on its own. Avoid comments that merely restate
what the code does (e.g., # increment i above i += 1).
Style: Comments should be written as complete sentences. Block comments must
begin with a # followed by a single space.
## Versioning
ADK adherence to Semantic Versioning 2.0.0
Core Principle: The adk-python project strictly adheres to the Semantic
@@ -369,103 +423,101 @@ changes to the public API. In the context of the ADK, this means a change that
could force a developer using the framework to alter their existing code to
upgrade to the new version. The public API is not limited to just the Python
function and class signatures; it also encompasses data schemas for stored
information (like evaluation datasets), the command-line interface (CLI),
and the data format used for server communications.
information (like evaluation datasets), the command-line interface (CLI), and
the data format used for server communications.
### Public API Surface Definition
The "public API" of ADK is a broad contract that extends beyond its Python
function signatures. A breaking change in any of the following areas can
disrupt user workflows and the wider ecosystem of agents and tools built with
ADK. The analysis of the breaking changes introduced in v1.0.0 demonstrates the
expansive nature of this contract. For the purposes of versioning, the ADK
Public API Surface is defined as:
function signatures. A breaking change in any of the following areas can disrupt
user workflows and the wider ecosystem of agents and tools built with ADK. The
analysis of the breaking changes introduced in v1.0.0 demonstrates the expansive
nature of this contract. For the purposes of versioning, the ADK Public API
Surface is defined as:
- All public classes, methods, and functions in the google.adk namespace.
- All public classes, methods, and functions in the google.adk namespace.
- The names, required parameters, and expected behavior of all built-in Tools
(e.g., google_search, BuiltInCodeExecutor).
- The names, required parameters, and expected behavior of all built-in Tools
(e.g., google_search, BuiltInCodeExecutor).
- The structure and schema of persisted data, including Session data, Memory,
and Evaluation datasets.
- The structure and schema of persisted data, including Session data, Memory,
and Evaluation datasets.
- The JSON request/response format of the ADK API server(FastAPI server)
used by adk web, including field casing conventions.
- The JSON request/response format of the ADK API server(FastAPI server) used
by adk web, including field casing conventions.
- The command-line interface (CLI) commands, arguments, and flags (e.g., adk deploy).
- The command-line interface (CLI) commands, arguments, and flags (e.g., adk
deploy).
- The expected file structure for agent definitions that are loaded by the
framework (e.g., the agent.py convention).
- The expected file structure for agent definitions that are loaded by the
framework (e.g., the agent.py convention).
#### Checklist for Breaking Changes:
The following changes are considered breaking and necessitate a MAJOR version
bump.
bump.
- API Signature Change: Renaming, removing, or altering the required parameters
of any public class, method, or function (e.g., the removal of the list_events
method from BaseSessionService).
- API Signature Change: Renaming, removing, or altering the required
parameters of any public class, method, or function (e.g., the removal of
the list_events method from BaseSessionService).
- Architectural Shift: A fundamental change to a core component's behavior
(e.g., making all service methods async, which requires consumers to use await).
- Architectural Shift: A fundamental change to a core component's behavior
(e.g., making all service methods async, which requires consumers to use
await).
- Data Schema Change: A non-additive change to a persisted data schema that
renders old data unreadable or invalid (e.g., the redesign of the
MemoryService and evaluation dataset schemas).
- Data Schema Change: A non-additive change to a persisted data schema that
renders old data unreadable or invalid (e.g., the redesign of the
MemoryService and evaluation dataset schemas).
- Tool Interface Change: Renaming a built-in tool, changing its required
parameters, or altering its fundamental purpose (e.g., replacing
BuiltInCodeExecutionTool with BuiltInCodeExecutor and moving it from the tools
parameter to the code_executor parameter of an Agent).
- Tool Interface Change: Renaming a built-in tool, changing its required
parameters, or altering its fundamental purpose (e.g., replacing
BuiltInCodeExecutionTool with BuiltInCodeExecutor and moving it from the
tools parameter to the code_executor parameter of an Agent).
- Configuration Change: Altering the required structure of configuration files
or agent definition files that the framework loads (e.g., the simplification
of the agent.py structure for MCPToolset).
- Configuration Change: Altering the required structure of configuration files
or agent definition files that the framework loads (e.g., the simplification
of the agent.py structure for MCPToolset).
- Wire Format Change: Modifying the data format for API server interactions
(e.g., the switch from snake_case to camelCase for all JSON payloads).
- Wire Format Change: Modifying the data format for API server interactions
(e.g., the switch from snake_case to camelCase for all JSON payloads).
- Dependency Removal: Removing support for a previously integrated third-party
library or tool type.
- Dependency Removal: Removing support for a previously integrated third-party
library or tool type.
## Commit Message Format (Required)
**All commits must** follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) format.
**All commits must** follow
[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) format.
**Format:**
```
<type>(<scope>): <description>
**Format:** ``` <type>(<scope>): <description>
[optional body]
[optional footer]
```
[optional footer] ```
**Common types:** `feat`, `fix`, `refactor`, `docs`, `test`, `chore`
**Examples:**
```
feat(agents): Add support for App pattern with plugins
**Examples:** ``` feat(agents): Add support for App pattern with plugins
fix(sessions): Prevent memory leak in session cleanup
refactor(tools): Unify environment variable enabled checks
```
refactor(tools): Unify environment variable enabled checks ```
**Rationale:** Conventional commits enable automated changelog generation and version management.
**Rationale:** Conventional commits enable automated changelog generation and
version management.
## Key Files and Locations
Quick reference to important project files:
- **Main config:** `pyproject.toml` (uses `flit_core` build backend)
- **Dependencies:** `uv.lock` (managed by `uv`)
- **Linting:** `pylintrc` (Google Python Style Guide)
- **Auto-format:** `autoformat.sh` (runs isort + pyink)
- **CLI entry point:** `src/google/adk/cli/cli_tools_click.py`
- **Web UI backend:** `src/google/adk/cli/adk_web_server.py`
- **Main exports:** `src/google/adk/__init__.py` (exports Agent, Runner)
- **Examples:** `contributing/samples/` (100+ agent implementations)
- **Main config:** `pyproject.toml` (uses `flit_core` build backend)
- **Dependencies:** `uv.lock` (managed by `uv`)
- **Linting:** `pylintrc` (Google Python Style Guide)
- **Auto-format:** `autoformat.sh` (runs isort + pyink)
- **CLI entry point:** `src/google/adk/cli/cli_tools_click.py`
- **Web UI backend:** `src/google/adk/cli/adk_web_server.py`
- **Main exports:** `src/google/adk/__init__.py` (exports Agent, Runner)
- **Examples:** `contributing/samples/` (100+ agent implementations)
## Additional Resources
+16 -9
View File
@@ -292,6 +292,11 @@ class Runner:
This sync interface is only for local testing and convenience purpose.
Consider using `run_async` for production usage.
If event compaction is enabled in the App configuration, it will be
performed after all agent events for the current invocation have been
yielded. The generator will only finish iterating after event
compaction is complete.
Args:
user_id: The user ID of the session.
session_id: The session ID of the session.
@@ -350,6 +355,12 @@ class Runner:
) -> AsyncGenerator[Event, None]:
"""Main entry method to run the agent in this runner.
If event compaction is enabled in the App configuration, it will be
performed after all agent events for the current invocation have been
yielded. The async generator will only finish iterating after event
compaction is complete. However, this does not block new `run_async`
calls for subsequent user queries, which can be started concurrently.
Args:
user_id: The user ID of the session.
session_id: The session ID of the session.
@@ -431,16 +442,12 @@ class Runner:
async for event in agen:
yield event
# Run compaction after all events are yielded from the agent.
# (We don't compact in the middle of an invocation, we only compact at the end of an invocation.)
# (We don't compact in the middle of an invocation, we only compact at
# the end of an invocation.)
if self.app and self.app.events_compaction_config:
logger.info('Running event compactor.')
# Run compaction in a separate task to avoid blocking the main thread.
# So the users can still finish the event loop from the agent while the
# compaction is running.
asyncio.create_task(
_run_compaction_for_sliding_window(
self.app, session, self.session_service
)
logger.debug('Running event compactor.')
await _run_compaction_for_sliding_window(
self.app, session, self.session_service
)
async with Aclosing(_run_with_trace(new_message, invocation_id)) as agen: