Compare commits

..

86 Commits

Author SHA1 Message Date
Ankur Sharma 960b206752 chore: Bumps version to v1.20.0 and updates CHANGELOG.md
Co-authored-by: Ankur Sharma <ankusharma@google.com>
PiperOrigin-RevId: 839930279
2025-12-03 15:03:25 -08:00
Bo Yang 5947c41b55 chore: Update component owners
Co-authored-by: Bo Yang <ybo@google.com>
PiperOrigin-RevId: 839896507
2025-12-03 13:40:48 -08:00
Shangjie Chen 9d918d45df feat!: Rollback the DB migration as it is breaking
Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 839818479
2025-12-03 10:39:43 -08:00
George Weale 8c9105bf14 chore: Drop Python 3.9 support, set minimum to Python 3.10
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 839799108
2025-12-03 09:56:22 -08:00
George Weale e02b9fb608 fix: Add a warning when deploying with the ADK Web UI enabled
The warning message shows that ADK Web is for development purposes only and should not be used in production, as it has access to all data. This warning is displayed when the `--with-ui` flag is used with `adk deploy` and `adk deploy to-gke`

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 839795361
2025-12-03 09:47:13 -08:00
Rohit Yanamadala 76dc169a83 fix: Add editLimit parameter to GraphQL query
Merge https://github.com/google/adk-python/pull/3771

Co-authored-by: Xuan Yang <xygoogle@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3771 from google:ryanaiagent-patch-1 a169e728af223594febc39299e046f2a195d606a
PiperOrigin-RevId: 839620767
2025-12-03 00:16:14 -08:00
Kathy Wu b638a48357 fix: Update API Registry Toolset to prod cloudapiregistry URL now that it is available
Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 839547174
2025-12-02 20:17:38 -08:00
Faraaz Ahmed b807d62fe3 feat(bigquery): Add labels support to BigQueryToolConfig for job tracking and monitoring
Merge https://github.com/google/adk-python/pull/3583

**Please ensure you have read the [contribution guide](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) before creating a pull request.**

### Link to Issue or Description of Change

**1. Link to an existing issue (if applicable):**

- Closes: #3582

**2. Or, if no issue exists, describe the change:**

_If applicable, please follow the issue templates to provide as much detail as
possible._

**Problem:**
Currently, the BigQuery tool in ADK does not provide a way for developers to add custom labels to BigQuery jobs created by their agents. This makes it difficult to:

Track and monitor BigQuery costs associated with specific agents or use cases
Organize and filter BigQuery jobs in the Google Cloud Console
Implement billing attribution and resource organization strategies
Differentiate between jobs from different environments (dev, staging, production)
While the tool automatically adds an internal adk-bigquery-tool label with the caller_id, there's no mechanism for users to add their own custom labels for tracking and monitoring purposes.

**Solution:**
Add a labels configuration field to BigQueryToolConfig that allows users to specify custom key-value pairs to be applied to all BigQuery jobs executed by the agent. The solution should:

Configuration Option: Add an optional labels parameter to BigQueryToolConfig accepting a dictionary of string key-value pairs
Validation: Ensure labels follow BigQuery's requirements (non-empty string keys, string values)
Job Application: Automatically apply configured labels to all BigQuery jobs alongside the existing internal labels Documentation: Provide clear documentation on how to use labels for tracking and monitoring

### Testing Plan

_Please describe the tests that you ran to verify your changes. This is required
for all PRs that are not small documentation or typo fixes._

**Unit Tests:**

- [x] I have added or updated unit tests for my change.
- [x] All unit tests pass locally.

_Please include a summary of passed `pytest` results._

```
pytest tests/unittests/tools/bigquery/test_bigquery_tool_config.py -v --tb=line -W ignore::UserWarning
========================================= test session starts ==========================================
platform darwin -- Python 3.11.14, pytest-9.0.1, pluggy-1.6.0 -- *****redacted******
cachedir: .pytest_cache
rootdir: *****redacted******
configfile: pyproject.toml
plugins: mock-3.15.1, anyio-4.11.0, xdist-3.8.0, langsmith-0.4.43, asyncio-1.3.0
asyncio: mode=Mode.AUTO, debug=False, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function
collected 14 items

tests/unittests/tools/bigquery/test_bigquery_tool_config.py::test_bigquery_tool_config_experimental_warning PASSED                                                                                                   [  7%]
tests/unittests/tools/bigquery/test_bigquery_tool_config.py::test_bigquery_tool_config_invalid_property PASSED                                                                                                       [ 14%]
tests/unittests/tools/bigquery/test_bigquery_tool_config.py::test_bigquery_tool_config_invalid_application_name PASSED                                                                                               [ 21%]
tests/unittests/tools/bigquery/test_bigquery_tool_config.py::test_bigquery_tool_config_max_query_result_rows_default PASSED                                                                                          [ 28%]
tests/unittests/tools/bigquery/test_bigquery_tool_config.py::test_bigquery_tool_config_max_query_result_rows_custom PASSED                                                                                           [ 35%]
tests/unittests/tools/bigquery/test_bigquery_tool_config.py::test_bigquery_tool_config_valid_maximum_bytes_billed PASSED                                                                                             [ 42%]
tests/unittests/tools/bigquery/test_bigquery_tool_config.py::test_bigquery_tool_config_invalid_maximum_bytes_billed PASSED                                                                                           [ 50%]
tests/unittests/tools/bigquery/test_bigquery_tool_config.py::test_bigquery_tool_config_valid_labels PASSED                                                                                                           [ 57%]
tests/unittests/tools/bigquery/test_bigquery_tool_config.py::test_bigquery_tool_config_empty_labels PASSED                                                                                                           [ 64%]
tests/unittests/tools/bigquery/test_bigquery_tool_config.py::test_bigquery_tool_config_none_labels PASSED                                                                                                            [ 71%]
tests/unittests/tools/bigquery/test_bigquery_tool_config.py::test_bigquery_tool_config_invalid_labels_type PASSED                                                                                                    [ 78%]
tests/unittests/tools/bigquery/test_bigquery_tool_config.py::test_bigquery_tool_config_invalid_label_key_type PASSED                                                                                                 [ 85%]
tests/unittests/tools/bigquery/test_bigquery_tool_config.py::test_bigquery_tool_config_invalid_label_value_type PASSED                                                                                               [ 92%]
tests/unittests/tools/bigquery/test_bigquery_tool_config.py::test_bigquery_tool_config_empty_label_key PASSED                                                                                                        [100%]

==================================================================================================== 14 passed in 2.02s ====================================================================================================
```

**Manual End-to-End (E2E) Tests:**

_Please provide instructions on how to manually test your changes, including any
necessary setup or configuration. Please provide logs or screenshots to help
reviewers better understand the fix._

### Checklist

- [x] I have read the [CONTRIBUTING.md](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) document.
- [x] I have performed a self-review of my own code.
- [x] I have commented my code, particularly in hard-to-understand areas.
- [x] I have added tests that prove my fix is effective or that my feature works.
- [x] New and existing unit tests pass locally with my changes.
- [x] I have manually tested my changes end-to-end.
- [x] Any dependent changes have been merged and published in downstream modules.

### Additional context

_Add any other context or screenshots about the feature request here._

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3583 from Faraaz1994:feature/bq_label 0fd7fe6a3b1ee20a36f73562e425d007b8d7dc9d
PiperOrigin-RevId: 839523588
2025-12-02 19:15:20 -08:00
Xuan Yang 3aef9a18b1 docs: Update ADK issue triaging agent to add component label before planned
Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 839391092
2025-12-02 12:54:44 -08:00
Shangjie Chen 77401132d1 chore: Add migration guide for DatabaseSessionService
Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 839376632
2025-12-02 12:15:52 -08:00
Google Team Member 090711934f feat: add Spanner vector_store_similarity_search tool
The vector_store_similarity_search tool performs similarity search against data in a Spanner vector store table, using the provided Spanner tool settings for configuration.

PiperOrigin-RevId: 839352057
2025-12-02 11:18:57 -08:00
Hangfei Lin 8da61be45a fix: Flush pending transcriptions on turn/generation complete or interrupt for Gemini API
The Gemini API may not always send an explicit transcription finished signal. This change ensures that any buffered input or output transcription text is yielded as a finished transcription when a turn is completed, generation is complete, or the session is interrupted.

Also, refined the check for `event.partial` in runners.py to be more explicit.

Co-authored-by: Hangfei Lin <hangfei@google.com>
PiperOrigin-RevId: 839008606
2025-12-01 18:09:29 -08:00
George Weale 98d82935e6 fix: allow LlmAgent model to be provided via CodeConfig
LlmAgentConfig.model now accepts either a plain model string or a CodeConfig. This lets YAML configs pass a LiteLLM instance with managed API settings (e.g., api_base and fallbacks) so agents can hit KimiK2’s managed endpoint instead of only the default modelID.

Close #3579

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 838978654
2025-12-01 16:31:22 -08:00
George Weale 7e8eeca6aa fix: Add a FastAPI endpoint for saving artifacts
This change adds new `POST` endpoint `/apps/{app_name}/users/{user_id}/sessions/{session_id}/artifacts` to the ADK web server. This endpoint lets clients to save new artifacts associated with a specific session. The endpoint uses `SaveArtifactRequest` and returns `SaveArtifactResponse`, including the version and canonical URI of the saved artifact.

Close #1975

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 838977880
2025-12-01 16:29:18 -08:00
George Weale ed9da3fa45 feat!: Introduction of ADK folder for local session and artifact storage
Default CLI session storage to SQLite instead of in-memory

Previously, adk run and adk web used in-memory session storage by default, causing sessions to be lost on restart. Now sessions persist to .adk/session.db automatically. To use in-memory storage, pass --session-service-uri memory://

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 838975328
2025-12-01 16:22:35 -08:00
Keyur Joshi dd827af2ee chore: Move simulation related modules to a sub-package in evaluation
Co-authored-by: Keyur Joshi <keyurj@google.com>
PiperOrigin-RevId: 838904075
2025-12-01 13:16:12 -08:00
Rohit Yanamadala cb19d0714c fix: Optimize Stale Agent with GraphQL and Search API to resolve 429 Quota errors
Merge https://github.com/google/adk-python/pull/3700

### Description
This PR refactors the `adk_stale_agent` to address `429 RESOURCE_EXHAUSTED` errors encountered during workflow execution. The previous implementation was inefficient in fetching issue history (using pagination over the REST API) and lacked server-side filtering, causing excessive API calls and huge token consumption that breached Gemini API quotas.

The new implementation switches to a **GraphQL-first approach**, implements server-side filtering via the Search API, adds robust concurrency controls, and significantly improves code maintainability through modular refactoring.

### Root Cause of Failure
The previous workflow failed with the following error due to passing too much context to the LLM and processing too many irrelevant issues:
```text
google.genai.errors.ClientError: 429 RESOURCE_EXHAUSTED.
Quota exceeded for metric: generativelanguage.googleapis.com/generate_content_paid_tier_input_token_count
```
### Key Changes

#### 1. Optimization: REST → GraphQL (`agent.py`)
*   **Old:** Fetched issue comments and timeline events using multiple paginated REST API calls (`/timeline`).
*   **New:** Implemented `get_issue_state` using a single **GraphQL** query. This fetches comments, `userContentEdits`, and specific timeline events (Labels, Renames) in one network request.
*   **Refactoring:** The complex analysis logic has been decomposed into focused helper functions (_fetch_graphql_data, _build_history_timeline, _replay_history_to_find_state) for better readability and testing.
*   **Configurable:** Added GRAPHQL_COMMENT_LIMIT and GRAPHQL_TIMELINE_LIMIT settings to tune context depth
*   **Impact:** Drastically reduces the data payload size and eliminates multiple API round-trips, significantly lowering the token count sent to the LLM.

#### 2. Optimization: Server-Side Filtering (`utils.py`)
*   **Old:** Fetched *all* open issues via REST and filtered them in Python memory.
*   **New:** Uses the GitHub Search API (`get_old_open_issue_numbers`) with `created:<DATE` syntax.
*   **Impact:** Only fetches issue numbers that actually meet the age threshold, preventing the agent from wasting cycles and tokens on brand-new issues.

#### 3. Concurrency & Rate Limiting (`main.py` & `settings.py`)
*   **Old:** Sequential execution loop.
*   **New:** Implemented `asyncio.gather` with a configurable `CONCURRENCY_LIMIT` (set to 3).
*   **New:** Added `urllib3` retry strategies (exponential backoff) in `utils.py` to handle GitHub API rate limits (HTTP 429) gracefully.

#### 4. Logic Improvements ("Ghost Edits")
*   **New Feature:** The agent now detects "Ghost Edits" (where an author updates the issue description without posting a new comment).
*   **Action:** If a silent edit is detected on a stale candidate, the agent now alerts maintainers instead of marking it stale, preventing false positives.

### File Comparison Summary

| File | Change |
| :--- | :--- |
| `main.py` | Switched from `InMemoryRunner` loop to `asyncio` chunked processing. Added execution timing and API usage logging. |
| `agent.py` | Replaced REST logic with GraphQL query. Added logic to handle silent body edits. Decomposed giant get_issue_state into helper functions with docstrings. Added _format_days helper. |
| `utils.py` | Added `HTTPAdapter` with Retries. Added `get_old_open_issue_numbers` using Search API. |
| `settings.py` | Removed `ISSUES_PER_RUN`; added configuration for CONCURRENCY_LIMIT, SLEEP_BETWEEN_CHUNKS, and GraphQL limits. |
| `PROMPT_INSTRUCTIONS.txt` | Simplified decision tree; removed date calculation responsibility from LLM. |

### Verification
The new logic minimizes token usage by offloading date calculations to Python and strictly limiting the context passed to the LLM to semantic intent analysis (e.g., "Is this a question?").

*   **Metric Check:** The workflow now tracks API calls per issue to ensure we stay within limits.
*   **Safety:** Silent edits by users now correctly reset the "Stale" timer.
*   **Maintainability:** All complex logic is now isolated in typed helper functions with comprehensive docstrings.

Co-authored-by: Xuan Yang <xygoogle@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3700 from ryanaiagent:feat/improve-stale-agent 888064eff125ae74f7c3a9ad6c74f98de80243a2
PiperOrigin-RevId: 838885530
2025-12-01 12:25:51 -08:00
Ankur Sharma 2a1a41d3ec chore: Adding Eval Client label to model calls made during evals
Co-authored-by: Ankur Sharma <ankusharma@google.com>
PiperOrigin-RevId: 838857867
2025-12-01 11:20:46 -08:00
Eitan Yarmush 8e82838f1e fix: Refactor Anthropic integration to support both direct API and Vertex AI
This change introduces an `AnthropicLlm` base class for direct Anthropic API calls using `AsyncAnthropic`. The existing `Claude` class now inherits from `AnthropicLlm` and is specialized to use `AsyncAnthropicVertex` for models hosted on Vertex AI. The `messages.create` call is now properly awaited

Merge: https://github.com/google/adk-python/pull/2904

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 838851026
2025-12-01 11:04:51 -08:00
Ishan Raj Singh 7edd7ea9b7 chore: Add warning for full resource path in VertexAiMemoryBankService agent_engine_id
This change updates the docstring for `agent_engine_id` to clarify that only the resource ID is expected. It also adds a warning log if the provided `agent_engine_id` contains a '/' character, suggesting it might be a full resource path, and provides guidance on how to extract the ID. Unit tests are added to verify the warning behavior.

Merge: https://github.com/google/adk-python/pull/2941

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 838845022
2025-12-01 10:51:47 -08:00
Shangjie Chen 0094eea3ca feat!: Migrate DatabaseSessionService to use JSON serialization schema
Also provide a command line tool `adk migrate session` for DB migration

Addresses https://github.com/google/adk-python/discussions/3605
Addresses https://github.com/google/adk-python/issues/3681

To verify:
```
# Start one postgres DB
docker run --name my-postgres -d -e POSTGRES_DB=agent  -e POSTGRES_USER=agent  -e POSTGRES_PASSWORD=agent  -e PGDATA=/var/lib/postgresql/data/pgdata -v pgvolume:/var/lib/postgresql/data -p 5532:5432 postgres

# Connect to an old version of ADK and produce some query data
adk web --session_service_uri=postgresql://agent:agent@localhost:5532/agent

# Check out to the latest branch and restart ADK web
# You should see error log ask you to migrate the DB

# Start a new DB
docker run --name migration-test-db \
  -d \ --rm \ -e POSTGRES_DB=agent \ -e POSTGRES_USER=agent \ -e POSTGRES_PASSWORD=agent -e PGDATA=/var/lib/postgresql/data/pgdata -v migration_test_vol:/var/lib/postgresql/data -p 5533:5432 postgres

# DB Migration
adk migrate session \
  --source_db_url="postgresql://agent:agent@localhost:5532/agent" \
  --dest_db_url="postgresql://agent:agent@localhost:5533/agent"

# Run ADK web with the new DB
adk web --session_service_uri=postgresql+asyncpg://agent:agent@localhost:5533/agent

# You should see the data from old DB is migrated
```

Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 837341139
2025-11-26 19:49:08 -08:00
Xuan Yang 786aaed335 feat: Support streaming function call arguments in progressive SSE streaming feature
Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 837172244
2025-11-26 10:15:31 -08:00
George Weale 73e5687b9a fix: Remove 'per_agent' from kwargs when using remote session service URIs
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 837169299
2025-11-26 10:08:13 -08:00
Kathy Wu ec4ccd718f feat: Create APIRegistryToolset to add tools from Cloud API registry to agent
This calls the cloudapiregistry.googleapis.com API to get MCP tools from the project's registry, and adds them to ADK.

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 837166909
2025-11-26 10:01:58 -08:00
George Weale f283027e92 feat: expose service URI flags
Adds the shared adk_services_options decorator to adk run and other commands so developers can pass session/artifact URIs from the CLI

Has new warning for the unsupported memory service on adk run, and removes the legacy --session_db_url/--artifact_storage_uri flags with tests

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 836743358
2025-11-25 11:13:03 -08:00
George Weale 06e6fc9132 feat: wire runtime entrypoints to service factory defaults
This change routes adk run and the FastAPI server through the new session/artifact service factory, keeps the default experience backed by per-agent .adk storage

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 836733234
2025-11-25 10:47:44 -08:00
happyryan 5453b5bfde fix: Allow image parts in user messages for Anthropic Claude
Previously, image parts were always filtered out when converting content to Anthropic message parameters. This change updates the logic to only filter out image parts and log a warning when the content role is not "user". This enables sending image data as part of user prompts to Claude models

Merges: https://github.com/google/adk-python/pull/3286

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 836725196
2025-11-25 10:30:08 -08:00
Hangfei Lin 5cad8a7f58 fix: Throw warning when using transparent session resumption in ADK Live for Gemini API key
transparent session resumption is only supported in Vertex AI APIs

Co-authored-by: Hangfei Lin <hangfei@google.com>
PiperOrigin-RevId: 836715170
2025-11-25 10:04:57 -08:00
Virtuoso633 d29261a3dc feat(models): Enable multi-provider support for Claude and LiteLLM
Merges: https://github.com/google/adk-python/pull/2810

Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 836706608
2025-11-25 09:46:44 -08:00
Bastien Jacot-Guillarmod e6be5bc9c6 fix: Add type annotations to Runner.__aenter__
PiperOrigin-RevId: 836614561
2025-11-25 04:45:00 -08:00
Om Kute c6e7d6b16a feat(tools): Add debug logging to VertexAiSearchTool
Merge https://github.com/google/adk-python/pull/3284

**Problem:**
When debugging agents that utilize the `VertexAiSearchTool`, it's currently difficult to inspect the specific configuration parameters (datastore ID, engine ID, filter, max_results, etc.) being passed to the underlying Vertex AI Search API via the `LlmRequest`. This lack of visibility can hinder troubleshooting efforts related to tool configuration.

**Solution:**
This PR enhances the `VertexAiSearchTool` by adding a **debug-level log statement** within the `process_llm_request` method. This log precisely records the parameters being used for the Vertex AI Search configuration just before it's appended to the `LlmRequest`.

This provides developers with crucial visibility into the tool's runtime behavior when debug logging is enabled, significantly improving the **debuggability** of agents using this tool. Corresponding unit tests were updated to rigorously verify this new logging output using `caplog`. Additionally, minor fixes were made to the tests to resolve Pydantic validation errors.

Co-authored-by: Xuan Yang <xygoogle@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3284 from omkute10:feat/add-logging-vertex-search-tool 199c12bf00a57abe202401591088c0423b39b928
PiperOrigin-RevId: 836419886
2025-11-24 17:29:50 -08:00
Xuan Yang b331d97dfb docs: Remove the list_unlabeled_issues tool from the issue triaging agent
Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 836416597
2025-11-24 17:14:51 -08:00
qieqieplus 4a42d0d9d8 feat: Add enum constraint to agent_name for transfer_to_agent
Merge https://github.com/google/adk-python/pull/2437

Current implementation of `transfer_to_agent` doesn't enforce strict constraints on agent names, we could use JSON Schema's enum definition to implement stricter constraints.

Co-authored-by: Xuan Yang <xygoogle@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2437 from qieqieplus:main 052e8e73b9d61c0998573a2077f15864873d0dd7
PiperOrigin-RevId: 836410397
2025-11-24 16:51:36 -08:00
Shangjie Chen 728abe4d81 feat(agents): Add warning for duplicate sub-agent names
Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 836409638
2025-11-24 16:48:45 -08:00
Google Team Member 4eb2a11403 fix: fix bug where remote a2a agent wasn't using its a2a part converter
PiperOrigin-RevId: 836399603
2025-11-24 16:18:18 -08:00
Kristen Pereira a1c09b724b fix: Windows Path Handling and Normalize Cross-Platform Path Resolution in AgentLoader
Merge https://github.com/google/adk-python/pull/3609

Co-authored-by: George Weale <gweale@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3609 from p-kris10:fix/windows-cmd 8cb0310bd4450097a0a7714eaac6521b6a447442
PiperOrigin-RevId: 836395714
2025-11-24 16:07:40 -08:00
AlexeyChernenkoPlato cf21ca3584 fix: double function response processing issue
Merge https://github.com/google/adk-python/pull/2588

## Description
Fixes an issue in `base_llm_flow.py` where, in Bidi-streaming (live) mode, the multi-agent structure causes duplicated responses after tool calling.

## Problem
In Bidi-streaming (live) mode, when utilizing a multi-agent structure, the leaf-level sub-agent and its parent agent both process the same function call response, leading to duplicate replies. This duplication occurs because the parent agent's live connection remains open while initiating a new connection with the child agent.

## Root Cause
The issue originated from the placement of agent transfer logic in the `_postprocess_live` method at lines 547-557. When a `transfer_to_agent` function call was made:

1. The function response was processed in `_postprocess_live`
2. A recursive call to `agent_to_run.run_live` was initiated
3. This prevented the closure of the parent agent's connection at line 175 of the `run_live` method, as that code path was never reached
4. Both the parent and child agents remained active, causing both to process subsequent function responses

## Solution
This PR addresses the issue by ensuring the parent agent's live connection is closed before initiating a new one with the child agent. Changes made:

**Connection Management**: Moved the agent transfer logic from `_postprocess_live` method to the `run_live` method, specifically:
- Removed agent transfer handling from lines 547-557 in `_postprocess_live`
- Added agent transfer handling after connection closure at lines 176-184 in `run_live`

**Code Refactoring**: The agent transfer now occurs in the proper sequence:
1. Parent agent processes the `transfer_to_agent` function response
2. Parent agent's live connection is properly closed (line 175)
3. New connection with child agent is initiated (line 182)
4. Child agent handles subsequent function calls without duplication

**Improved Flow Control**: This ensures that each agent processes function call responses without duplication, maintaining proper connection lifecycle management in multi-agent structures.

## Testing
To verify this fix works correctly:

1. **Multi-Agent Structure Test**: Set up a multi-agent structure with a parent agent that transfers to a child agent via `transfer_to_agent` function call
2. **Bidi-Streaming Mode**: Enable Bidi-streaming (live) mode in the configuration
3. **Function Call Verification**: Trigger a function call that results in agent transfer
4. **Response Monitoring**: Verify that only one response is generated (not duplicated)
5. **Connection Management**: Confirm that parent agent's connection is properly closed before child agent starts

**Expected Behavior**:
- Single function response per call
- Clean agent handoffs without connection leaks
- Proper connection lifecycle management

## Backward Compatibility
This change is **fully backward compatible**:
- No changes to public APIs or method signatures
- Existing single-agent flows remain unaffected
- Non-live (regular async) flows continue to work as before
- Only affects the internal flow control in live multi-agent scenarios

Co-authored-by: Hangfei Lin <hangfei@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2588 from AlexeyChernenkoPlato:fix/double-function-response-processing-issue 3339260a4e007251137d199bdcef0ddef4487b03
PiperOrigin-RevId: 835619170
2025-11-22 09:34:05 -08:00
Shangjie Chen a9a418ba87 fix: Remove distructive validation
Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 835466120
2025-11-21 20:56:39 -08:00
George Weale 2e1f730c3b fix: Update LiteLLM system instruction role from "developer" to "system"
This change replaces the use of `ChatCompletionDeveloperMessage` with `ChatCompletionSystemMessage` and sets the role to "system" for providing system instructions to LiteLLM models

Close #3657

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 835388738
2025-11-21 15:56:04 -08:00
Xuan Yang 52674e7fac fix: Update AgentTool to use Agent's description when input_schema is provided in FunctionDeclaration
Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 835379243
2025-11-21 15:25:27 -08:00
davidkl97 777dba3033 feat(tools): Add an option to disallow propagating runner plugins to AgentTool runner
Merge https://github.com/google/adk-python/pull/2779

Fixes #2780

### testing plan
not available as is doesn't introduce new functionality

Co-authored-by: Wei Sun (Jack) <weisun@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2779 from davidkl97:feature/agent-tool-plugins a602c808789f3daeed6244e352a6fb8fb6972de3
PiperOrigin-RevId: 835366974
2025-11-21 14:49:36 -08:00
saroj rout 2247a45922 feat(agents): add validation for unique sub-agent names (#3557)
Merge https://github.com/google/adk-python/pull/3576

**Please ensure you have read the [contribution guide](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) before creating a pull request.**

### Link to Issue or Description of Change

**1. Link to an existing issue (if applicable):**

- Closes: #3557
- Related: #_issue_number_

**2. Or, if no issue exists, describe the change:**

_If applicable, please follow the issue templates to provide as much detail as
possible._

**Problem:**
When creating a BaseAgent with multiple sub-agents, there was no validation to ensure that all sub-agents have unique names. This could lead to confusion when trying to find or reference specific sub-agents by name, as duplicate names would make it ambiguous which agent is being referenced.
**Solution:**
Added a @field_validator for the sub_agents field in BaseAgent that validates all sub-agents have unique names. The validator:
Checks for duplicate names in the sub-agents list
Raises a ValueError with a clear error message listing all duplicate names found
Returns the validated list if all names are unique
Handles edge cases like empty lists gracefully

### Testing Plan

_Please describe the tests that you ran to verify your changes. This is required
for all PRs that are not small documentation or typo fixes._

**Unit Tests:**

- [x] I have added or updated unit tests for my change.
- [x] All unit tests pass locally.

_Please include a summary of passed `pytest` results._
Added 4 new test cases in tests/unittests/agents/test_base_agent.py:
test_validate_sub_agents_unique_names_single_duplicate: Verifies that a single duplicate name raises ValueError
test_validate_sub_agents_unique_names_multiple_duplicates: Verifies that multiple duplicate names are all reported in the error message
test_validate_sub_agents_unique_names_no_duplicates: Verifies that unique names pass validation successfully
test_validate_sub_agents_unique_names_empty_list: Verifies that empty sub-agents list passes validation
All tests pass locally. You can run with:
pytest tests/unittests/agents/test_base_agent.py::test_validate_sub_agents_unique_names_single_duplicate tests/unittests/agents/test_base_agent.py::test_validate_sub_agents_unique_names_multiple_duplicates tests/unittests/agents/test_base_agent.py::test_validate_sub_agents_unique_names_no_duplicates tests/unittests/agents/test_base_agent.py::test_validate_sub_agents_unique_names_empty_list -v
**Manual End-to-End (E2E) Tests:**

_Please provide instructions on how to manually test your changes, including any
necessary setup or configuration. Please provide logs or screenshots to help
reviewers better understand the fix._

Test Case 1: Duplicate names should raise error
from google.adk.agents import Agent

agent1 = Agent(name="sub_agent", model="gemini-2.5-flash")
agent2 = Agent(name="sub_agent", model="gemini-2.5-flash")  # Same name

# This should raise ValueError
try:
    parent = Agent(
        name="parent",
        model="gemini-2.5-flash",
        sub_agents=[agent1, agent2]
    )
except ValueError as e:
    print(f"Expected error: {e}")
    # Output: Found duplicate sub-agent names: `sub_agent`. All sub-agents must have unique names.

Test Case 2: Unique names should work
from google.adk.agents import Agent

agent1 = Agent(name="agent1", model="gemini-2.5-flash")
agent2 = Agent(name="agent2", model="gemini-2.5-flash")

# This should work without error
parent = Agent(
    name="parent",
    model="gemini-2.5-flash",
    sub_agents=[agent1, agent2]
)
print("Success: Unique names validated correctly")

### Checklist

- [x] I have read the [CONTRIBUTING.md](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) document.
- [x] I have performed a self-review of my own code.
- [x] I have commented my code, particularly in hard-to-understand areas.
- [x] I have added tests that prove my fix is effective or that my feature works.
- [x] New and existing unit tests pass locally with my changes.
- [x] I have manually tested my changes end-to-end.
- [x] Any dependent changes have been merged and published in downstream modules.

### Additional context

This change adds validation at the BaseAgent level, so it automatically applies to all agent types that inherit from BaseAgent (e.g., LlmAgent, LoopAgent, etc.). The validation uses Pydantic's field validator system, which runs during object initialization, ensuring the constraint is enforced early and consistently.
The error message clearly identifies which names are duplicated, making it easy for developers to fix the issue:

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3576 from sarojrout:feat/validate-unique-sub-agent-names 07adf1f9a5fc935389eb9dfa3cbc1311f551ebe3
PiperOrigin-RevId: 835358118
2025-11-21 14:24:07 -08:00
Adrian Altermatt 609c6172d9 docs: too many E(inv=2, role=user) plus reformatting
Merge https://github.com/google/adk-python/pull/3538

Main change from:
E(inv=2, role=user), E(inv=2, role=model), E(inv=2, role=user),

To:
E(inv=2, role=user), E(inv=2, role=model)

I think the last E(inv=2, role=user) was wrong. Also reformatted.

Co-authored-by: Hangfei Lin <hangfei@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3538 from adrianad:patch-1 627b933bdc3e00e45f704edf95448281e32d127c
PiperOrigin-RevId: 835346467
2025-11-21 13:49:01 -08:00
Giorgio Boa 9d331abb4e ci: bump action scripts versions
Merge https://github.com/google/adk-python/pull/3638

Thanks for this great project 👏
This PR updates the GitHub actions dependencies to the latest version.

### Checklist

- [X] I have read the [CONTRIBUTING.md](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) document.
- [X] I have performed a self-review of my own code.

Co-authored-by: Hangfei Lin <hangfei@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3638 from gioboa:ci/actions-versions f7d6f3b5233e8cb135c8af88d5b6e0ead8382055
PiperOrigin-RevId: 835343177
2025-11-21 13:39:46 -08:00
Rohit Yanamadala 23f1d8914a docs(agent): Implement stale issue bot
Merge https://github.com/google/adk-python/pull/3546

Co-authored-by: Xuan Yang <xygoogle@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3546 from ryanaiagent:feat/stale-issue-agent bcf45098c1c6406b4a42228e4a8ef02f12840425
PiperOrigin-RevId: 835327931
2025-11-21 12:54:20 -08:00
Shangjie Chen 5583bb819b chore: Update MCP requirement to >1.10.0
Resolves https://github.com/google/adk-python/issues/3644

Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 835302097
2025-11-21 11:43:32 -08:00
Shangjie Chen 89aee16f16 chore: Allow google-cloud-storage >=2.18.0
Resolves https://github.com/google/adk-python/issues/3641

Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 835292931
2025-11-21 11:18:40 -08:00
Max Ind a4453c884c fix: adk deploy agent_engine uses correct URI during an update
Co-authored-by: Max Ind <maxind@google.com>
PiperOrigin-RevId: 835269976
2025-11-21 10:17:57 -08:00
Google Team Member 11df1e886d fix: Change pass to yield in BaseLlmConnection.receive
PiperOrigin-RevId: 835268090
2025-11-21 10:12:19 -08:00
Austin Wise 59eba96ea4 fix: Remove unused, incorrect import
PiperOrigin-RevId: 835247166
2025-11-21 09:10:04 -08:00
Google Team Member 631b58336d fix: Content is marked non empty if its first part contains text or inline_data or file_data or func call/response
PiperOrigin-RevId: 835063599
2025-11-20 22:19:28 -08:00
George Weale a3e4ad3cd1 fix: Update session last update time when appending events
The `database_session_service` now updates the `update_time` of a session to the event's timestamp when an event is appended

Close #2721

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 834994070
2025-11-20 18:27:02 -08:00
George Weale 848fdbef7c fix: Filter out None values from enum lists in schema generation
Close #3552

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 834967220
2025-11-20 17:02:45 -08:00
George Weale 31cfa3b82b feat: Capture thinking output, forward raw payloads, and fix exec locals
LlmResponse/Event now keep both provider reasoning output and the raw vendor payload so callbacks and loggers can inspect hidden “thoughts” or trace bugs without rewriting adapters.

LiteLLM’s adapter and streaming loop emit reasoning chunks alongside text and aggregate them into final events -> all responses now carry a JSON-safe copy of the source payload for debug. UnsafeLocalCodeExecutor uses the documented exec(code, globals, globals) form, letting helper functions defined inside snippets call each other.

Close #1749

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 834956847
2025-11-20 16:30:19 -08:00
Dylan b57fe5f459 feat(web): add list-apps-detailed endpoint
Merge https://github.com/google/adk-python/pull/3430

**Please ensure you have read the [contribution guide](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) before creating a pull request.**

### Link to Issue or Description of Change

**1. Link to an existing issue (if applicable):**

- Closes: #3429

**2. Or, if no issue exists, describe the change:**

_If applicable, please follow the issue templates to provide as much detail as
possible._

**Problem:**
The existing `/list-apps` endpoint only returns the name of the folder that each agent is in

**Solution:**
This adds a new endpoint `/list-apps-detailed` which will load each agent using the existing `AgentLoader.load_agent` method, and then return the folder name, display name (with underscores replaced with spaces for a more readable version), description, and the agent type.

This does introduce overhead if you had multiple agents since they all need to be loaded, but by maintaining the existing `/list-apps` endpoint, users can choose which one to hit if they don't want to load all agents. Since the existing `load_agents` method will cache results, there's only a penalty on the first hit.

### Testing Plan

Created a unit test for this, similar to the `/list-apps`. Also tested this with my own ADK instance to verify it loaded correctly.
```
curl --location "localhost:8000/list-apps-detailed"
```
```json
{
    "apps": [
        {
            "name": "agent_1",
            "displayName": "Agent 1",
            "description": "A test description for a test agent",
            "agentType": "package"
        },
        {
            "name": "agent_2",
            "displayName": "Agent 2",
            "description": "A test description for a test agent ",
            "agentType": "package"
        },
        {
            "name": "agent_3",
            "displayName": "Agent 3",
            "description": "A test description for a test agent",
            "agentType": "package"
        }
    ]
}

```

**Unit Tests:**

- [X] I have added or updated unit tests for my change.
- [X] All unit tests pass locally.

 3054 passed, 2383 warnings in 46.96s

### Checklist

- [X] I have read the [CONTRIBUTING.md](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) document.
- [X] I have performed a self-review of my own code.
- [X] I have commented my code, particularly in hard-to-understand areas.
- [X] I have added tests that prove my fix is effective or that my feature works.
- [X] New and existing unit tests pass locally with my changes.
- [X] I have manually tested my changes end-to-end.
- [X] Any dependent changes have been merged and published in downstream modules.

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3430 from dylan-apex:more-detailed-list-apps e6864fd61a673da5fd2fb28d2d7d72cb90f5af0a
PiperOrigin-RevId: 834907771
2025-11-20 14:15:06 -08:00
Issac cd54f48fed fix: fix paths for public docs
Merge https://github.com/google/adk-python/pull/3572

**Please ensure you have read the [contribution guide](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) before creating a pull request.**

### Link to Issue or Description of Change

**1. Link to an existing issue (if applicable):**

N/A

**2. Or, if no issue exists, describe the change:**

**Problem:**
Docs fix

### Checklist

- [ ] I have read the [CONTRIBUTING.md](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) document.
- [ ] I have performed a self-review of my own code.
- [ ] I have commented my code, particularly in hard-to-understand areas.
- [ ] I have added tests that prove my fix is effective or that my feature works.
- [ ] New and existing unit tests pass locally with my changes.
- [ ] I have manually tested my changes end-to-end.
- [ ] Any dependent changes have been merged and published in downstream modules.

Co-authored-by: Hangfei Lin <hangfei@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3572 from issacg:patch-1 b7c7ed46ff0fb018f4da1537535eff27c323daf5
PiperOrigin-RevId: 834864431
2025-11-20 12:26:43 -08:00
George Weale 084c2de0da fix: Make sure request bodies without explicit names are named 'body'
The `Parameter` class now provides default Python names based on the parameter location when the original name is empty. This prevents parameters from having an empty string as their Python name, especially for request bodies defined without a top-level name.

Close #2213

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 834850255
2025-11-20 11:48:46 -08:00
George Weale bf8b85da52 fix: save sessions with camelCase aliases
Make sure that the adk run --save_session writes session JSON using the Pydantic camelCase aliases (by_alias=True), matching ADK Web and keeping session files consistent

Close #3558

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 834847209
2025-11-20 11:41:41 -08:00
Holt Skinner caf23ac49f docs: Add Code Wiki badge to README
Merge https://github.com/google/adk-python/pull/3603

**Please ensure you have read the [contribution guide](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) before creating a pull request.**

### Link to Issue or Description of Change

**1. Link to an existing issue (if applicable):**

- Closes: #_issue_number_
- Related: #_issue_number_

**2. Or, if no issue exists, describe the change:**

_If applicable, please follow the issue templates to provide as much detail as
possible._

**Problem:**
_A clear and concise description of what the problem is._

**Solution:**
_A clear and concise description of what you want to happen and why you choose
this solution._

### Testing Plan

_Please describe the tests that you ran to verify your changes. This is required
for all PRs that are not small documentation or typo fixes._

**Unit Tests:**

- [ ] I have added or updated unit tests for my change.
- [ ] All unit tests pass locally.

_Please include a summary of passed `pytest` results._

**Manual End-to-End (E2E) Tests:**

_Please provide instructions on how to manually test your changes, including any
necessary setup or configuration. Please provide logs or screenshots to help
reviewers better understand the fix._

### Checklist

- [ ] I have read the [CONTRIBUTING.md](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) document.
- [ ] I have performed a self-review of my own code.
- [ ] I have commented my code, particularly in hard-to-understand areas.
- [ ] I have added tests that prove my fix is effective or that my feature works.
- [ ] New and existing unit tests pass locally with my changes.
- [ ] I have manually tested my changes end-to-end.
- [ ] Any dependent changes have been merged and published in downstream modules.

### Additional context

_Add any other context or screenshots about the feature request here._

Co-authored-by: Hangfei Lin <hangfei@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3603 from holtskinner:patch-1 833405898b747438e3f8a1b8c34095e25135fca3
PiperOrigin-RevId: 834805195
2025-11-20 10:00:29 -08:00
Kathy Wu a3aa07722a fix: Update the retry_on_closed_resource decorator to retry on all errors
Retrying only on closed_resource error is not enough to be reliable for production environments due to the other network errors that may occur -- remote protocol error, read timeout, etc. We will update this to retry on all errors. Since it is only a one-time retry, it should not affect latency significantly. Fixes https://github.com/google/adk-python/issues/2561.

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 834514264
2025-11-19 17:50:49 -08:00
Shangjie Chen a6e4d6c0d9 chore: Bumps version to v1.19.0 and updates CHANGELOG.md
Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 834503873
2025-11-19 17:12:56 -08:00
Divyansh Shukla 8fc6128b62 fix: Fix out of bounds error in _run_async_impl
PiperOrigin-RevId: 834492696
2025-11-19 16:36:35 -08:00
Xuan Yang 679d543f8e docs: Update ADK triaging agent to only triage planned issues
It also enables the ADK triaging agent to run periodically on planned but not triaged issues.

Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 834489103
2025-11-19 16:26:55 -08:00
Google Team Member f13a11e1dc feat: Propagate application_name set for the BigQuery Tools as BigQuery job labels
This change will help the tools user identify per agent job usage in BQ console and INFORMATION_SCHEMA views. This change fulfills the feature request #3582. Here is a demo after change: screen/C6YB4ge2FM2ZREi.

PiperOrigin-RevId: 834480140
2025-11-19 16:02:32 -08:00
George Weale 131d39c3db fix: Change name for builder agent
When this agent was built before the agent name was changed in the build script and accepted like that from the VB so changed to set into that form

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 834479335
2025-11-19 16:00:51 -08:00
George Weale 12db84f5cd fix: Remove app name from FileArtifactService directory structure
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 834462462
2025-11-19 15:12:48 -08:00
Ankur Sharma dc3f60cc93 chore: Plumb memory service from LocalEvalService to EvaluationGenerator
Co-authored-by: Ankur Sharma <ankusharma@google.com>
PiperOrigin-RevId: 834398581
2025-11-19 12:28:52 -08:00
Yifan Wang 14e3802643 chore: update adk web to match main branch
Co-authored-by: Yifan Wang <wanyif@google.com>
PiperOrigin-RevId: 834378696
2025-11-19 11:37:29 -08:00
Shangjie Chen ffbab4cf4e chore: Add BigQuery related label handling
Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 834375503
2025-11-19 11:29:40 -08:00
George Weale b2b7f2d6aa chore: Move adk_agent_builder_assistant to built_in_agents
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 834371390
2025-11-19 11:18:37 -08:00
Hangfei Lin 3ad30a58f9 fix: Add transcription fields to session events
This change introduces `input_transcription` and `output_transcription` fields to session events, enabling the storage and retrieval of transcription data in both the database and Vertex AI session services.

Closes #3172

Co-authored-by: Hangfei Lin <hangfei@google.com>
PiperOrigin-RevId: 834366848
2025-11-19 11:08:38 -08:00
George Weale 0ac35b23dc docs: Add path sanitization for model-generated file paths
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 834344352
2025-11-19 10:13:16 -08:00
Mimi Sun 857de04deb feat: update save_files_as_artifacts_plugin to never keep inline data
PiperOrigin-RevId: 834328794
2025-11-19 09:36:42 -08:00
Max Ind e15e19da05 fix: remove hardcoded google-cloud-aiplatform version in agent engine requirements
This fixes e.g. `--trace_to_cloud flag`

Co-authored-by: Max Ind <maxind@google.com>
PiperOrigin-RevId: 834190152
2025-11-19 02:01:34 -08:00
Michael Jones 0cc3d6d6d5 Feat/expose mcps streamable http custom httpx factory parameter (#2997)
* feat: Add support for custom HTTPX client factory in StreamableHTTPConnectionParams

* Update src/google/adk/tools/mcp_tool/mcp_session_manager.py

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* unit tested mock

* provide default - httpx client factory can't be none

* feat: Enhance StreamableHTTPConnectionParams with httpx_client_factory attribute

* fmt

* fmt

* refactor: Rename test_init_with_streamable_http_none_httpx_factory to test_init_with_streamable_http_default_httpx_factory for clarity

* isort

* fmt

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Kathy Wu <108756731+wukath@users.noreply.github.com>
2025-11-18 11:09:48 -08:00
Hangfei Lin b5f5df9fa8 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
2025-11-18 10:57:50 -08:00
George Weale a12ae812d3 feat: Add service factory for configurable session and artifact backends
this creates service_factory to handle .adk folder changes (including per-agent .adk defaults and in-memory/custom URI handling)

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 833875524
2025-11-18 10:35:31 -08:00
Shangjie Chen 8eb1bdbc58 chore: Add demo for rewind
Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 833871446
2025-11-18 10:26:15 -08:00
George Weale 236f562cd2 fix: Load agent/app before creating session
This change loads the agent or app from the specified directory before creating the session. This allows using the correct application name (from the `App` object if applicable) when initializing the session, rather than always defaulting to the folder name. The variable `root_agent` is also renamed to `agent_or_app` to better reflect that it can be either an Agent or an App

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 833839070
2025-11-18 09:09:22 -08:00
Google Team Member 4dd28a3970 feat: Add id and custom_metadata fields to MemoryEntry
PiperOrigin-RevId: 833581243
2025-11-17 18:34:03 -08:00
Ankur Sharma b2c45f8d91 chore: Enhance the messaging with possible fixes for RESOURCE_EXHAUSTED errors from Gemini
Co-authored-by: Ankur Sharma <ankusharma@google.com>
PiperOrigin-RevId: 833538475
2025-11-17 16:15:56 -08:00
Google Team Member 5ac5129fb0 feat: Enhance BQ Plugin Schema, Error Handling, and Logging
This update enhances the BigQuery agent analytics plugin:

*   **Enhanced Error Logging:** Improved error messages for schema mismatches.
*   **Reordered Logging Content:** Prioritized metadata in `before_model_callback`.

PiperOrigin-RevId: 833508755
2025-11-17 15:00:38 -08:00
Google Team Member c642f13f21 feat: Thread custom_metadata through forwarding artifact service
PiperOrigin-RevId: 833496193
2025-11-17 14:25:30 -08:00
Kathy Wu a48a1a9e88 fix: Improve logic for checking if a MCP session is disconnected
Currently logic to check for a disconnected session only checks for certain headers but doesn't detect all cases, leading to situations where it tries to connect to a session that is down. This adds logic so that we ping the server to check if it is disconnected. Fixes https://github.com/google/adk-python/issues/3321.

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 833487825
2025-11-17 14:05:02 -08:00
Xuan Yang a5ac1d5e14 feat: Add progressive SSE streaming feature
Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 833483804
2025-11-17 13:55:38 -08:00
George Weale 0ec01956e8 fix: keep vertex session event history intact
Close #3504

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 833417574
2025-11-17 11:13:08 -08:00
208 changed files with 13623 additions and 6338 deletions
@@ -16,10 +16,10 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.11'
+1 -1
View File
@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 2
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
steps:
- name: Check for Copybara commits and close PRs
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
github-token: ${{ secrets.ADK_TRIAGE_AGENT }}
script: |
+2 -2
View File
@@ -15,10 +15,10 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.11'
+2 -2
View File
@@ -26,12 +26,12 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 2
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.x'
+2 -2
View File
@@ -20,10 +20,10 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.11'
+2 -2
View File
@@ -26,12 +26,12 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 2
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.x'
+6 -14
View File
@@ -25,14 +25,14 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
@@ -48,14 +48,6 @@ jobs:
- name: Run unit tests with pytest
run: |
source .venv/bin/activate
if [[ "${{ matrix.python-version }}" == "3.9" ]]; then
pytest tests/unittests \
--ignore=tests/unittests/a2a \
--ignore=tests/unittests/tools/mcp_tool \
--ignore=tests/unittests/artifacts/test_artifact_service.py \
--ignore=tests/unittests/tools/google_api_tool/test_googleapi_to_openapi_converter.py
else
pytest tests/unittests \
--ignore=tests/unittests/artifacts/test_artifact_service.py \
--ignore=tests/unittests/tools/google_api_tool/test_googleapi_to_openapi_converter.py
fi
pytest tests/unittests \
--ignore=tests/unittests/artifacts/test_artifact_service.py \
--ignore=tests/unittests/tools/google_api_tool/test_googleapi_to_openapi_converter.py
+43
View File
@@ -0,0 +1,43 @@
name: ADK Stale Issue Auditor
on:
workflow_dispatch:
schedule:
# This runs at 6:00 AM UTC (10 PM PST)
- cron: '0 6 * * *'
jobs:
audit-stale-issues:
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
issues: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests google-adk
- name: Run Auditor Agent Script
env:
GITHUB_TOKEN: ${{ secrets.ADK_TRIAGE_AGENT }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
OWNER: ${{ github.repository_owner }}
REPO: adk-python
CONCURRENCY_LIMIT: 3
LLM_MODEL_NAME: "gemini-2.5-flash"
PYTHONPATH: contributing/samples
run: python -m adk_stale_agent.main
+16 -5
View File
@@ -2,21 +2,32 @@ name: ADK Issue Triaging Agent
on:
issues:
types: [opened, reopened]
types: [opened, labeled]
schedule:
# Run every 6 hours to triage untriaged issues
- cron: '0 */6 * * *'
jobs:
agent-triage-issues:
runs-on: ubuntu-latest
# Run for:
# - Scheduled runs (batch processing)
# - New issues (need component labeling)
# - Issues labeled with "planned" (need owner assignment)
if: >-
github.event_name == 'schedule' ||
github.event.action == 'opened' ||
github.event.label.name == 'planned'
permissions:
issues: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.11'
@@ -30,8 +41,8 @@ jobs:
GITHUB_TOKEN: ${{ secrets.ADK_TRIAGE_AGENT }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
GOOGLE_GENAI_USE_VERTEXAI: 0
OWNER: 'google'
REPO: 'adk-python'
OWNER: ${{ github.repository_owner }}
REPO: ${{ github.event.repository.name }}
INTERACTIVE: 0
EVENT_NAME: ${{ github.event_name }} # 'issues', 'schedule', etc.
ISSUE_NUMBER: ${{ github.event.issue.number }}
@@ -13,7 +13,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Clone adk-docs repository
run: git clone https://github.com/google/adk-docs.git /tmp/adk-docs
@@ -22,7 +22,7 @@ jobs:
run: git clone https://github.com/google/adk-python.git /tmp/adk-python
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.11'
+252 -200
View File
File diff suppressed because it is too large Load Diff
+141
View File
@@ -1,5 +1,146 @@
# Changelog
## [1.20.0](https://github.com/google/adk-python/compare/v1.19.0...v1.20.0) (2025-12-01)
### Features
* **[Core]**
* Add enum constraint to `agent_name` for `transfer_to_agent` ([4a42d0d](https://github.com/google/adk-python/commit/4a42d0d9d81b7aab98371427f70a7707dbfb8bc4))
* Add validation for unique sub-agent names ([#3557](https://github.com/google/adk-python/issues/3557)) ([2247a45](https://github.com/google/adk-python/commit/2247a45922afdf0a733239b619f45601d9b325ec))
* Support streaming function call arguments in progressive SSE streaming feature ([786aaed](https://github.com/google/adk-python/commit/786aaed335e1ce64b7e92dff2f4af8316b2ef593))
* **[Models]**
* Enable multi-provider support for Claude and LiteLLM ([d29261a](https://github.com/google/adk-python/commit/d29261a3dc9c5a603feef27ea657c4a03bb8a089))
* **[Tools]**
* Create APIRegistryToolset to add tools from Cloud API registry to agent ([ec4ccd7](https://github.com/google/adk-python/commit/ec4ccd718feeadeb6b2b59fcc0e9ff29a4fd0bac))
* Add an option to disallow propagating runner plugins to AgentTool runner ([777dba3](https://github.com/google/adk-python/commit/777dba3033a9a14667fb009ba017f648177be41d))
* **[Web]**
* Added an endpoint to list apps with details ([b57fe5f](https://github.com/google/adk-python/commit/b57fe5f4598925ec7592917bb32c7f0d6eca287a))
### Bug Fixes
* Allow image parts in user messages for Anthropic Claude ([5453b5b](https://github.com/google/adk-python/commit/5453b5bfdedc91d9d668c9eac39e3bb009a7bbbf))
* Mark the Content as non-empty if its first part contains text or inline_data or file_data or func call/response ([631b583](https://github.com/google/adk-python/commit/631b58336d36bfd93e190582be34069613d38559))
* Fixes double response processing issue in `base_llm_flow.py` where, in Bidi-streaming (live) mode, the multi-agent structure causes duplicated responses after tool calling. ([cf21ca3](https://github.com/google/adk-python/commit/cf21ca358478919207049695ba6b31dc6e0b2673))
* Fix out of bounds error in _run_async_impl ([8fc6128](https://github.com/google/adk-python/commit/8fc6128b62ba576480d196d4a2597564fd0a7006))
* Fix paths for public docs ([cd54f48](https://github.com/google/adk-python/commit/cd54f48fed0c87b54fb19743c9c75e790c5d9135))
* Ensure request bodies without explicit names are named 'body' ([084c2de](https://github.com/google/adk-python/commit/084c2de0dac84697906e2b4beebf008bbd9ae8e1)), closes [#2213](https://github.com/google/adk-python/issues/2213)
* Optimize Stale Agent with GraphQL and Search API to resolve 429 Quota errors ([cb19d07](https://github.com/google/adk-python/commit/cb19d0714c90cd578551753680f39d8d6076c79b))
* Update AgentTool to use Agent's description when input_schema is provided in FunctionDeclaration ([52674e7](https://github.com/google/adk-python/commit/52674e7fac6b7689f0e3871d41c4523e13471a7e))
* Update LiteLLM system instruction role from "developer" to "system" ([2e1f730](https://github.com/google/adk-python/commit/2e1f730c3bc0eb454b76d7f36b7b9f1da7304cfe)), closes [#3657](https://github.com/google/adk-python/issues/3657)
* Update session last update time when appending events ([a3e4ad3](https://github.com/google/adk-python/commit/a3e4ad3cd130714affcaa880f696aeb498cd93af)), closes [#2721](https://github.com/google/adk-python/issues/2721)
* Update the retry_on_closed_resource decorator to retry on all errors ([a3aa077](https://github.com/google/adk-python/commit/a3aa07722a7de3e08807e86fd10f28938f0b267d))
* Windows Path Handling and Normalize Cross-Platform Path Resolution in AgentLoader ([a1c09b7](https://github.com/google/adk-python/commit/a1c09b724bb37513eaabaff9643eeaa68014f14d))
### Documentation
* Add Code Wiki badge to README ([caf23ac](https://github.com/google/adk-python/commit/caf23ac49fe08bc7f625c61eed4635c26852c3ba))
## [1.19.0](https://github.com/google/adk-python/compare/v1.18.0...v1.19.0) (2025-11-19)
### Features
* **[Core]**
* Add `id` and `custom_metadata` fields to `MemoryEntry` ([4dd28a3](https://github.com/google/adk-python/commit/4dd28a3970d0f76c571caf80b3e1bea1b79e9dde))
* Add progressive SSE streaming feature ([a5ac1d5](https://github.com/google/adk-python/commit/a5ac1d5e14f5ce7cd875d81a494a773710669dc1))
* Add a2a_request_meta_provider to RemoteAgent init ([d12468e](https://github.com/google/adk-python/commit/d12468ee5a2b906b6699ccdb94c6a5a4c2822465))
* Add feature decorator for the feature registry system ([871da73](https://github.com/google/adk-python/commit/871da731f1c09c6a62d51b137d9d2e7c9fb3897a))
* Breaking: Raise minimum Python version to 3_10 ([8402832](https://github.com/google/adk-python/commit/840283228ee77fb3dbd737cfe7eb8736d9be5ec8))
* Refactor and rename BigQuery agent analytics plugin ([6b14f88](https://github.com/google/adk-python/commit/6b14f887262722ccb85dcd6cef9c0e9b103cfa6e))
* Pass custom_metadata through forwarding artifact service ([c642f13](https://github.com/google/adk-python/commit/c642f13f216fb64bc93ac46c1c57702c8a2add8c))
* Update save_files_as_artifacts_plugin to never keep inline data ([857de04](https://github.com/google/adk-python/commit/857de04debdeba421075c2283c9bd8518d586624))
* **[Evals]**
* Add support for InOrder and AnyOrder match in ToolTrajectoryAvgScore Metric ([e2d3b2d](https://github.com/google/adk-python/commit/e2d3b2d862f7fc93807d16089307d4df25367a24))
* **[Integrations]**
* Enhance BQ Plugin Schema, Error Handling, and Logging ([5ac5129](https://github.com/google/adk-python/commit/5ac5129fb01913516d6f5348a825ca83d024d33a))
* Schema Enhancements with Descriptions, Partitioning, and Truncation Indicator ([7c993b0](https://github.com/google/adk-python/commit/7c993b01d1b9d582b4e2348f73c0591d47bf2f3a))
* **[Services]**
* Add file-backed artifact service ([99ca6aa](https://github.com/google/adk-python/commit/99ca6aa6e6b4027f37d091d9c93da6486def20d7))
* Add service factory for configurable session and artifact backends ([a12ae81](https://github.com/google/adk-python/commit/a12ae812d367d2d00ab246f85a73ed679dd3828a))
* Add SqliteSessionService and a migration script to migrate existing DB using DatabaseSessionService to SqliteSessionService ([e218254](https://github.com/google/adk-python/commit/e2182544952c0174d1a8307fbba319456dca748b))
* Add transcription fields to session events ([3ad30a5](https://github.com/google/adk-python/commit/3ad30a58f95b8729f369d00db799546069d7b23a))
* Full async implementation of DatabaseSessionService ([7495941](https://github.com/google/adk-python/commit/74959414d8ded733d584875a49fb4638a12d3ce5))
* **[Models]**
* Add experimental feature to use `parameters_json_schema` and `response_json_schema` for McpTool ([1dd97f5](https://github.com/google/adk-python/commit/1dd97f5b45226c25e4c51455c78ebf3ff56ab46a))
* Add support for parsing inline JSON tool calls in LiteLLM responses ([22eb7e5](https://github.com/google/adk-python/commit/22eb7e5b06c9e048da5bb34fe7ae9135d00acb4e))
* Expose artifact URLs to the model when available ([e3caf79](https://github.com/google/adk-python/commit/e3caf791395ce3cc0b10410a852be6e7b0d8d3b1))
* **[Tools]**
* Add BigQuery related label handling ([ffbab4c](https://github.com/google/adk-python/commit/ffbab4cf4ed6ceb313241c345751214d3c0e11ce))
* Allow setting max_billed_bytes in BigQuery tools config ([ffbb0b3](https://github.com/google/adk-python/commit/ffbb0b37e128de50ebf57d76cba8b743a8b970d5))
* Propagate `application_name` set for the BigQuery Tools as BigQuery job labels ([f13a11e](https://github.com/google/adk-python/commit/f13a11e1dc27c5aa46345154fbe0eecfe1690cbb))
* Set per-tool user agent in BQ calls and tool label in BQ jobs ([c0be1df](https://github.com/google/adk-python/commit/c0be1df0521cfd4b84585f404d4385b80d08ba59))
* **[Observability]**
* Migrate BigQuery logging to Storage Write API ([a2ce34a](https://github.com/google/adk-python/commit/a2ce34a0b9a8403f830ff637d0e2094e82dee8e7))
### Bug Fixes
* Add `jsonschema` dependency for Agent Builder config validation ([0fa7e46](https://github.com/google/adk-python/commit/0fa7e4619d589dc834f7508a18bc2a3b93ec7fd9))
* Add None check for `event` in `remote_a2a_agent.py` ([744f94f](https://github.com/google/adk-python/commit/744f94f0c8736087724205bbbad501640b365270))
* Add vertexai initialization for code being deployed to AgentEngine ([b8e4aed](https://github.com/google/adk-python/commit/b8e4aedfbf0eb55b34599ee24e163b41072a699c))
* Change LiteLLM content and tool parameter handling ([a19be12](https://github.com/google/adk-python/commit/a19be12c1f04bb62a8387da686499857c24b45c0))
* Change name for builder agent ([131d39c](https://github.com/google/adk-python/commit/131d39c3db1ae25e3911fa7f72afbe05e24a1c37))
* Ensure event compaction completes by awaiting task ([b5f5df9](https://github.com/google/adk-python/commit/b5f5df9fa8f616b855c186fcef45bade00653c77))
* Fix deploy to cloud run on Windows ([29fea7e](https://github.com/google/adk-python/commit/29fea7ec1fb27989f07c90494b2d6acbe76c03d8))
* Fix error handling when MCP server is unreachable ([ee8106b](https://github.com/google/adk-python/commit/ee8106be77f253e3687e72ae0e236687d254965c))
* Fix error when query job destination is None ([0ccc43c](https://github.com/google/adk-python/commit/0ccc43cf49dc0882dc896455d6603a602d8a28e7))
* Fix Improve logic for checking if a MCP session is disconnected ([a754c96](https://github.com/google/adk-python/commit/a754c96d3c4fd00f9c2cd924fc428b68cc5115fb))
* Fix McpToolset crashing with anyio.BrokenResourceError ([8e0648d](https://github.com/google/adk-python/commit/8e0648df23d0694afd3e245ec4a3c41aa935120a))
* Fix Safely handle `FunctionDeclaration` without a `required` attribute ([93aad61](https://github.com/google/adk-python/commit/93aad611983dc1daf415d3a73105db45bbdd1988))
* Fix status code in error message in RestApiTool ([9b75456](https://github.com/google/adk-python/commit/9b754564b3cc5a06ad0c6ae2cd2d83082f9f5943))
* Fix Use `async for` to loop through event iterator to get all events in vertex_ai_session_service ([9211f4c](https://github.com/google/adk-python/commit/9211f4ce8cc6d918df314d6a2ff13da2e0ef35fa))
* Fix: Fixes DeprecationWarning when using send method ([2882995](https://github.com/google/adk-python/commit/28829952890c39dbdb4463b2b67ff241d0e9ef6d))
* Improve logic for checking if a MCP session is disconnected ([a48a1a9](https://github.com/google/adk-python/commit/a48a1a9e889d4126e6f30b56c93718dfbacef624))
* Improve handling of partial and complete transcriptions in live calls ([1819ecb](https://github.com/google/adk-python/commit/1819ecb4b8c009d02581c2d060fae49cd7fdf653))
* Keep vertex session event after the session update time ([0ec0195](https://github.com/google/adk-python/commit/0ec01956e86df6ae8e6553c70e410f1f8238ba88))
* Let part converters also return multiple parts so they can support more usecases ([824ab07](https://github.com/google/adk-python/commit/824ab072124e037cc373c493f43de38f8b61b534))
* Load agent/app before creating session ([236f562](https://github.com/google/adk-python/commit/236f562cd275f84837be46f7dfb0065f85425169))
* Remove app name from FileArtifactService directory structure ([12db84f](https://github.com/google/adk-python/commit/12db84f5cd6d8b6e06142f6f6411f6b78ff3f177))
* Remove hardcoded `google-cloud-aiplatform` version in agent engine requirements ([e15e19d](https://github.com/google/adk-python/commit/e15e19da05ee1b763228467e83f6f73e0eced4b5))
* Stop updating write mode in the global settings during tool execution ([5adbf95](https://github.com/google/adk-python/commit/5adbf95a0ab0657dd7df5c4a6bac109d424d436e))
* Update description for `load_artifacts` tool ([c485889](https://github.com/google/adk-python/commit/c4858896ff085bedcfbc42b2010af8bd78febdd0))
### Improvements
* Add BigQuery related label handling ([ffbab4c](https://github.com/google/adk-python/commit/ffbab4cf4ed6ceb313241c345751214d3c0e11ce))
* Add demo for rewind ([8eb1bdb](https://github.com/google/adk-python/commit/8eb1bdbc58dc709006988f5b6eec5fda25bd0c89))
* Add debug logging for live connection ([5d5708b](https://github.com/google/adk-python/commit/5d5708b2ab26cb714556311c490b4d6f0a1f9666))
* Add debug logging for missing function call events ([f3d6fcf](https://github.com/google/adk-python/commit/f3d6fcf44411d07169c14ae12189543f44f96c27))
* Add default retry options as fall back to llm_request that are made during evals ([696852a](https://github.com/google/adk-python/commit/696852a28095a024cbe76413ee7617356e19a9e3))
* Add plugin for returning GenAI Parts from tools into the model request ([116b26c](https://github.com/google/adk-python/commit/116b26c33e166bf1a22964e2b67013907fbfcb80))
* Add support for abstract types in AFC ([2efc184](https://github.com/google/adk-python/commit/2efc184a46173529bdfc622db0d6f3866e7ee778))
* Add support for structured output schemas in LiteLLM models ([7ea4aed](https://github.com/google/adk-python/commit/7ea4aed35ba70ec5a38dc1b3b0a9808183c2bab1))
* Add tests for `max_query_result_rows` in BigQuery tool config ([fd33610](https://github.com/google/adk-python/commit/fd33610e967ad814bc02422f5d14dae046bee833))
* Add type hints in `cleanup_unused_files.py` ([2dea573](https://github.com/google/adk-python/commit/2dea5733b759a7a07d74f36a4d6da7b081afc732))
* Add util to build our llms.txt and llms-full.txt files
* ADK changes ([f1f4467](https://github.com/google/adk-python/commit/f1f44675e4a86b75e72cfd838efd8a0399f23e24))
* Defer import of `google.cloud.storage` in `GCSArtifactService` ([999af55](https://github.com/google/adk-python/commit/999af5588005e7b29451bdbf9252265187ca992d))
* Defer import of `live`, `Client` and `_transformers` in `google.genai` ([22c6dbe](https://github.com/google/adk-python/commit/22c6dbe83cd1a8900d0ac6fd23d2092f095189fa))
* Enhance the messaging with possible fixes for RESOURCE_EXHAUSTED errors from Gemini ([b2c45f8](https://github.com/google/adk-python/commit/b2c45f8d910eb7bca4805c567279e65aff72b58a))
* Improve gepa tau-bench colab for external use ([e02f177](https://github.com/google/adk-python/commit/e02f177790d9772dd253c9102b80df1a9418aa7f))
* Improve gepa voter agent demo colab ([d118479](https://github.com/google/adk-python/commit/d118479ccf3a970ce9b24ac834b4b6764edb5de4))
* Lazy import DatabaseSessionService in the adk/sessions/ module ([5f05749](https://github.com/google/adk-python/commit/5f057498a274d3b3db0be0866f04d5225334f54a))
* Move adk_agent_builder_assistant to built_in_agents ([b2b7f2d](https://github.com/google/adk-python/commit/b2b7f2d6aa5b919a00a92abaf2543993746e939e))
* Plumb memory service from LocalEvalService to EvaluationGenerator ([dc3f60c](https://github.com/google/adk-python/commit/dc3f60cc939335da49399a69c0b4abc0e7f25ea4))
* Removes the unrealistic todo comment of visibility management ([e511eb1](https://github.com/google/adk-python/commit/e511eb1f70f2a3fccc9464ddaf54d0165db22feb))
* Returns agent state regardless if ctx.is_resumable ([d6b928b](https://github.com/google/adk-python/commit/d6b928bdf7cdbf8f1925d4c5227c7d580093348e))
* Stop logging the full content of LLM blobs ([0826755](https://github.com/google/adk-python/commit/082675546f501a70f4bc8969b9431a2e4808bd13))
* Update ADK web to match main branch ([14e3802](https://github.com/google/adk-python/commit/14e3802643a2d8ce436d030734fafd163080a1ad))
* Update agent instructions and retry limit in `plugin_reflect_tool_retry` sample ([01bac62](https://github.com/google/adk-python/commit/01bac62f0c14cce5d454a389b64a9f44a03a3673))
* Update conformance test CLI to handle long-running tool calls ([dd706bd](https://github.com/google/adk-python/commit/dd706bdc4563a2a815459482237190a63994cb6f))
* Update Gemini Live model names in live bidi streaming sample ([aa77834](https://github.com/google/adk-python/commit/aa77834e2ecd4b77dfb4e689ef37549b3ebd6134))
## [1.18.0](https://github.com/google/adk-python/compare/v1.17.0...v1.18.0) (2025-11-05)
### Features
+1 -1
View File
@@ -4,7 +4,7 @@
[![PyPI](https://img.shields.io/pypi/v/google-adk)](https://pypi.org/project/google-adk/)
[![Python Unit Tests](https://github.com/google/adk-python/actions/workflows/python-unit-tests.yml/badge.svg)](https://github.com/google/adk-python/actions/workflows/python-unit-tests.yml)
[![r/agentdevelopmentkit](https://img.shields.io/badge/Reddit-r%2Fagentdevelopmentkit-FF4500?style=flat&logo=reddit&logoColor=white)](https://www.reddit.com/r/agentdevelopmentkit/)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/google/adk-python)
<a href="https://codewiki.google/github.com/google/adk-python"><img src="https://www.gstatic.com/_/boq-sdlc-agents-ui/_/r/Mvosg4klCA4.svg" alt="Ask Code Wiki" height="20"></a>
<html>
<h2 align="center">
@@ -0,0 +1,68 @@
You are a highly intelligent repository auditor for '{OWNER}/{REPO}'.
Your job is to analyze a specific issue and report findings before taking action.
**Primary Directive:** Ignore any events from users ending in `[bot]`.
**Reporting Directive:** Output a concise summary starting with "Analysis for Issue #[number]:".
**THRESHOLDS:**
- Stale Threshold: {stale_threshold_days} days.
- Close Threshold: {close_threshold_days} days.
**WORKFLOW:**
1. **Context Gathering**: Call `get_issue_state`.
2. **Decision**: Follow this strict decision tree using the data returned by the tool.
--- **DECISION TREE** ---
**STEP 1: CHECK IF ALREADY STALE**
- **Condition**: Is `is_stale` (from tool) **True**?
- **Action**:
- **Check Role**: Look at `last_action_role`.
- **IF 'author' OR 'other_user'**:
- **Context**: The user has responded. The issue is now ACTIVE.
- **Action 1**: Call `remove_label_from_issue` with '{STALE_LABEL_NAME}'.
- **Action 2 (ALERT CHECK)**: Look at `maintainer_alert_needed`.
- **IF True**: User edited description silently.
-> **Action**: Call `alert_maintainer_of_edit`.
- **IF False**: User commented normally. No alert needed.
- **Report**: "Analysis for Issue #[number]: ACTIVE. User activity detected. Removed stale label."
- **IF 'maintainer'**:
- **Check Time**: Check `days_since_stale_label`.
- **If `days_since_stale_label` > {close_threshold_days}**:
- **Action**: Call `close_as_stale`.
- **Report**: "Analysis for Issue #[number]: STALE. Close threshold met. Closing."
- **Else**:
- **Report**: "Analysis for Issue #[number]: STALE. Waiting for close threshold. No action."
**STEP 2: CHECK IF ACTIVE (NOT STALE)**
- **Condition**: `is_stale` is **False**.
- **Action**:
- **Check Role**: If `last_action_role` is 'author' or 'other_user':
- **Context**: The issue is Active.
- **Action (ALERT CHECK)**: Look at `maintainer_alert_needed`.
- **IF True**: The user edited the description silently, and we haven't alerted yet.
-> **Action**: Call `alert_maintainer_of_edit`.
-> **Report**: "Analysis for Issue #[number]: ACTIVE. Silent update detected (Description Edit). Alerted maintainer."
- **IF False**:
-> **Report**: "Analysis for Issue #[number]: ACTIVE. Last action was by user. No action."
- **Check Role**: If `last_action_role` is 'maintainer':
- **Proceed to STEP 3.**
**STEP 3: ANALYZE MAINTAINER INTENT**
- **Context**: The last person to act was a Maintainer.
- **Action**: Read the text in `last_comment_text`.
- **Question Check**: Does the text ask a question, request clarification, ask for logs, or suggest trying a fix?
- **Time Check**: Is `days_since_activity` > {stale_threshold_days}?
- **DECISION**:
- **IF (Question == YES) AND (Time == YES)**:
- **Action**: Call `add_stale_label_and_comment`.
- **Check**: If '{REQUEST_CLARIFICATION_LABEL}' is not in `current_labels`, call `add_label_to_issue` for it.
- **Report**: "Analysis for Issue #[number]: STALE. Maintainer asked question [days_since_activity] days ago. Marking stale."
- **IF (Question == YES) BUT (Time == NO)**:
- **Report**: "Analysis for Issue #[number]: PENDING. Maintainer asked question, but threshold not met yet. No action."
- **IF (Question == NO)** (e.g., "I am working on this"):
- **Report**: "Analysis for Issue #[number]: ACTIVE. Maintainer gave status update (not a question). No action."
@@ -0,0 +1,89 @@
# ADK Stale Issue Auditor Agent
This directory contains an autonomous, **GraphQL-powered** agent designed to audit a GitHub repository for stale issues. It maintains repository hygiene by ensuring all open items are actionable and responsive.
Unlike traditional "Stale Bots" that only look at timestamps, this agent uses a **Unified History Trace** and an **LLM (Large Language Model)** to understand the *context* of a conversation. It distinguishes between a maintainer asking a question (stale candidate) vs. a maintainer providing a status update (active).
---
## Core Logic & Features
The agent operates as a "Repository Auditor," proactively scanning open issues using a high-efficiency decision tree.
### 1. Smart State Verification (GraphQL)
Instead of making multiple expensive API calls, the agent uses a single **GraphQL** query per issue to reconstruct the entire history of the conversation. It combines:
* **Comments**
* **Description/Body Edits** ("Ghost Edits")
* **Title Renames**
* **State Changes** (Reopens)
It sorts these events chronologically to determine the **Last Active Actor**.
### 2. The "Last Actor" Rule
The agent follows a precise logic flow based on who acted last:
* **If Author/User acted last:** The issue is **ACTIVE**.
* This includes comments, title changes, and *silent* description edits.
* **Action:** The agent immediately removes the `stale` label.
* **Silent Update Alert:** If the user edited the description but *did not* comment, the agent posts a specific alert: *"Notification: The author has updated the issue description..."* to ensure maintainers are notified (since GitHub does not trigger notifications for body edits).
* **Spam Prevention:** The agent checks if it has already alerted about a specific silent edit to avoid spamming the thread.
* **If Maintainer acted last:** The issue is **POTENTIALLY STALE**.
* The agent passes the text of the maintainer's last comment to the LLM.
### 3. Semantic Intent Analysis (LLM)
If the maintainer was the last person to speak, the LLM analyzes the comment text to determine intent:
* **Question/Request:** "Can you provide logs?" / "Please try v2.0."
* **Verdict:** **STALE** (Waiting on Author).
* **Action:** If the time threshold is met, the agent adds the `stale` label. It also checks for the `request clarification` label and adds it if missing.
* **Status Update:** "We are working on a fix." / "Added to backlog."
* **Verdict:** **ACTIVE** (Waiting on Maintainer).
* **Action:** No action taken. The issue remains open without stale labels.
### 4. Lifecycle Management
* **Marking Stale:** After `STALE_HOURS_THRESHOLD` (default: 7 days) of inactivity following a maintainer's question.
* **Closing:** After `CLOSE_HOURS_AFTER_STALE_THRESHOLD` (default: 7 days) of continued inactivity while marked stale.
---
## Performance & Safety
* **GraphQL Optimized:** Fetches comments, edits, labels, and timeline events in a single network request to minimize latency and API quota usage.
* **Search API Filtering:** Uses the GitHub Search API to pre-filter issues created recently, ensuring the bot doesn't waste cycles analyzing brand-new issues.
* **Rate Limit Aware:** Includes intelligent sleeping and retry logic (exponential backoff) to handle GitHub API rate limits (HTTP 429) gracefully.
* **Execution Metrics:** Logs the time taken and API calls consumed for every issue processed.
---
## Configuration
The agent is configured via environment variables, typically set as secrets in GitHub Actions.
### Required Secrets
| Secret Name | Description |
| :--- | :--- |
| `GITHUB_TOKEN` | A GitHub Personal Access Token (PAT) or Service Account Token with `repo` scope. |
| `GOOGLE_API_KEY` | An API key for the Google AI (Gemini) model used for reasoning. |
### Optional Configuration
These variables control the timing thresholds and model selection.
| Variable Name | Description | Default |
| :--- | :--- | :--- |
| `STALE_HOURS_THRESHOLD` | Hours of inactivity after a maintainer's question before marking as `stale`. | `168` (7 days) |
| `CLOSE_HOURS_AFTER_STALE_THRESHOLD` | Hours after being marked `stale` before the issue is closed. | `168` (7 days) |
| `LLM_MODEL_NAME`| The specific Gemini model version to use. | `gemini-2.5-flash` |
| `OWNER` | Repository owner (auto-detected in Actions). | (Environment dependent) |
| `REPO` | Repository name (auto-detected in Actions). | (Environment dependent) |
---
## Deployment
To deploy this agent, a GitHub Actions workflow file (`.github/workflows/stale-bot.yml`) is recommended.
### Directory Structure Note
Because this agent resides within the `adk-python` package structure, the workflow must ensure the script is executed correctly to handle imports.
@@ -0,0 +1,15 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from . import agent
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,195 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import asyncio
import logging
import time
from typing import Tuple
from adk_stale_agent.agent import root_agent
from adk_stale_agent.settings import CONCURRENCY_LIMIT
from adk_stale_agent.settings import OWNER
from adk_stale_agent.settings import REPO
from adk_stale_agent.settings import SLEEP_BETWEEN_CHUNKS
from adk_stale_agent.settings import STALE_HOURS_THRESHOLD
from adk_stale_agent.utils import get_api_call_count
from adk_stale_agent.utils import get_old_open_issue_numbers
from adk_stale_agent.utils import reset_api_call_count
from google.adk.cli.utils import logs
from google.adk.runners import InMemoryRunner
from google.genai import types
logs.setup_adk_logger(level=logging.INFO)
logger = logging.getLogger("google_adk." + __name__)
APP_NAME = "stale_bot_app"
USER_ID = "stale_bot_user"
async def process_single_issue(issue_number: int) -> Tuple[float, int]:
"""
Processes a single GitHub issue using the AI agent and logs execution metrics.
Args:
issue_number (int): The GitHub issue number to audit.
Returns:
Tuple[float, int]: A tuple containing:
- duration (float): Time taken to process the issue in seconds.
- api_calls (int): The number of API calls made during this specific execution.
Raises:
Exception: catches generic exceptions to prevent one failure from stopping the batch.
"""
start_time = time.perf_counter()
start_api_calls = get_api_call_count()
logger.info(f"Processing Issue #{issue_number}...")
logger.debug(f"#{issue_number}: Initializing runner and session.")
try:
runner = InMemoryRunner(agent=root_agent, app_name=APP_NAME)
session = await runner.session_service.create_session(
user_id=USER_ID, app_name=APP_NAME
)
prompt_text = f"Audit Issue #{issue_number}."
prompt_message = types.Content(
role="user", parts=[types.Part(text=prompt_text)]
)
logger.debug(f"#{issue_number}: Sending prompt to agent.")
async for event in runner.run_async(
user_id=USER_ID, session_id=session.id, new_message=prompt_message
):
if (
event.content
and event.content.parts
and hasattr(event.content.parts[0], "text")
):
text = event.content.parts[0].text
if text:
clean_text = text[:150].replace("\n", " ")
logger.info(f"#{issue_number} Decision: {clean_text}...")
except Exception as e:
logger.error(f"Error processing issue #{issue_number}: {e}", exc_info=True)
duration = time.perf_counter() - start_time
end_api_calls = get_api_call_count()
issue_api_calls = end_api_calls - start_api_calls
logger.info(
f"Issue #{issue_number} finished in {duration:.2f}s "
f"with ~{issue_api_calls} API calls."
)
return duration, issue_api_calls
async def main():
"""
Main entry point to run the stale issue bot concurrently.
Fetches old issues and processes them in batches to respect API rate limits
and concurrency constraints.
"""
logger.info(f"--- Starting Stale Bot for {OWNER}/{REPO} ---")
logger.info(f"Concurrency level set to {CONCURRENCY_LIMIT}")
reset_api_call_count()
filter_days = STALE_HOURS_THRESHOLD / 24
logger.debug(f"Fetching issues older than {filter_days:.2f} days...")
try:
all_issues = get_old_open_issue_numbers(OWNER, REPO, days_old=filter_days)
except Exception as e:
logger.critical(f"Failed to fetch issue list: {e}", exc_info=True)
return
total_count = len(all_issues)
search_api_calls = get_api_call_count()
if total_count == 0:
logger.info("No issues matched the criteria. Run finished.")
return
logger.info(
f"Found {total_count} issues to process. "
f"(Initial search used {search_api_calls} API calls)."
)
total_processing_time = 0.0
total_issue_api_calls = 0
processed_count = 0
# Process the list in chunks of size CONCURRENCY_LIMIT
for i in range(0, total_count, CONCURRENCY_LIMIT):
chunk = all_issues[i : i + CONCURRENCY_LIMIT]
current_chunk_num = i // CONCURRENCY_LIMIT + 1
logger.info(
f"--- Starting chunk {current_chunk_num}: Processing issues {chunk} ---"
)
tasks = [process_single_issue(issue_num) for issue_num in chunk]
results = await asyncio.gather(*tasks)
for duration, api_calls in results:
total_processing_time += duration
total_issue_api_calls += api_calls
processed_count += len(chunk)
logger.info(
f"--- Finished chunk {current_chunk_num}. Progress:"
f" {processed_count}/{total_count} ---"
)
if (i + CONCURRENCY_LIMIT) < total_count:
logger.debug(
f"Sleeping for {SLEEP_BETWEEN_CHUNKS}s to respect rate limits..."
)
await asyncio.sleep(SLEEP_BETWEEN_CHUNKS)
total_api_calls_for_run = search_api_calls + total_issue_api_calls
avg_time_per_issue = (
total_processing_time / total_count if total_count > 0 else 0
)
logger.info("--- Stale Agent Run Finished ---")
logger.info(f"Successfully processed {processed_count} issues.")
logger.info(f"Total API calls made this run: {total_api_calls_for_run}")
logger.info(
f"Average processing time per issue: {avg_time_per_issue:.2f} seconds."
)
if __name__ == "__main__":
start_time = time.perf_counter()
try:
asyncio.run(main())
except KeyboardInterrupt:
logger.warning("Bot execution interrupted manually.")
except Exception as e:
logger.critical(f"Unexpected fatal error: {e}", exc_info=True)
duration = time.perf_counter() - start_time
logger.info(f"Full audit finished in {duration/60:.2f} minutes.")
@@ -0,0 +1,63 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
from dotenv import load_dotenv
# Load environment variables from a .env file for local testing
load_dotenv(override=True)
# --- GitHub API Configuration ---
GITHUB_BASE_URL = "https://api.github.com"
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
if not GITHUB_TOKEN:
raise ValueError("GITHUB_TOKEN environment variable not set")
OWNER = os.getenv("OWNER", "google")
REPO = os.getenv("REPO", "adk-python")
LLM_MODEL_NAME = os.getenv("LLM_MODEL_NAME", "gemini-2.5-flash")
STALE_LABEL_NAME = "stale"
REQUEST_CLARIFICATION_LABEL = "request clarification"
# --- THRESHOLDS IN HOURS ---
# Default: 168 hours (7 days)
# The number of hours of inactivity after a maintainer comment before an issue is marked as stale.
STALE_HOURS_THRESHOLD = float(os.getenv("STALE_HOURS_THRESHOLD", 168))
# Default: 168 hours (7 days)
# The number of hours of inactivity after an issue is marked 'stale' before it is closed.
CLOSE_HOURS_AFTER_STALE_THRESHOLD = float(
os.getenv("CLOSE_HOURS_AFTER_STALE_THRESHOLD", 168)
)
# --- Performance Configuration ---
# The number of issues to process concurrently.
# Higher values are faster but increase the immediate rate of API calls
CONCURRENCY_LIMIT = int(os.getenv("CONCURRENCY_LIMIT", 3))
# --- GraphQL Query Limits ---
# The number of most recent comments to fetch for context analysis.
GRAPHQL_COMMENT_LIMIT = int(os.getenv("GRAPHQL_COMMENT_LIMIT", 30))
# The number of most recent description edits to fetch.
GRAPHQL_EDIT_LIMIT = int(os.getenv("GRAPHQL_EDIT_LIMIT", 10))
# The number of most recent timeline events (labels, renames, reopens) to fetch.
GRAPHQL_TIMELINE_LIMIT = int(os.getenv("GRAPHQL_TIMELINE_LIMIT", 20))
# --- Rate Limiting ---
# Time in seconds to wait between processing chunks.
SLEEP_BETWEEN_CHUNKS = float(os.getenv("SLEEP_BETWEEN_CHUNKS", 1.5))

Some files were not shown because too many files have changed in this diff Show More