Compare commits

...

79 Commits

Author SHA1 Message Date
Wei Sun (Jack) 93a085ad87 chore(version): Bumps version to 1.24.1 patch
Co-authored-by: Wei Sun (Jack) <weisun@google.com>
PiperOrigin-RevId: 866107525
2026-02-05 14:13:02 -08:00
Google Team Member 6645aa07fd feat: Add experimental agent tool simulator
PiperOrigin-RevId: 866100611
2026-02-05 13:57:57 -08:00
Yifan Wang 3686a3a98f chore: update adk web files, updated eval dialog colors, and fixed a2ui component types
Co-authored-by: Yifan Wang <wanyif@google.com>
PiperOrigin-RevId: 866057812
2026-02-05 12:16:54 -08:00
Yifan Wang ae993e884f fix: adding back deprecated eval endpoint for web until we migrate
Co-authored-by: Yifan Wang <wanyif@google.com>
PiperOrigin-RevId: 866049699
2026-02-05 11:58:04 -08:00
George Weale bb89466623 chore: Improve type hints and handle None values in ADK utils
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 866025998
2026-02-05 11:04:46 -08:00
Xuan Yang adbc37fea1 feat: Add progress_callback support to MCPTool and MCPToolset
Fixes: https://github.com/google/adk-python/issues/3811

Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 866025995
2026-02-05 11:04:36 -08:00
George Weale 9b112e2d13 fix: Refactor context filtering to better handle multi-turn invocations
The definition of an "invocation" for context filtering has been updated. An invocation now starts with a user message and can include multiple model turns (like the tool calls and responses) until the next user message. The filtering logic has been rewritten to identify invocation start points based on human user messages

Close #4296

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 866023290
2026-02-05 10:57:56 -08:00
Google Team Member a08bf62b95 feat(otel): add extra attributes to span generated with opentelemetry-instrumentation-google-genai
PiperOrigin-RevId: 865825792
2026-02-05 01:58:30 -08:00
Liang Wu e752bbb756 chore: Remove unused tzlocal dependency and logging
The `tzlocal` library and the logging of the local timezone were not used in the `DatabaseSessionService` logic.

Co-authored-by: Liang Wu <wuliang@google.com>
PiperOrigin-RevId: 865677320
2026-02-04 18:49:55 -08:00
Wei Sun (Jack) fb941f9011 chore(version): Bump version and update changelog for 1.24.0
Co-authored-by: Wei Sun (Jack) <weisun@google.com>
PiperOrigin-RevId: 865651579
2026-02-04 17:26:50 -08:00
Yifan Wang de44bbc17c chore: update adk web
Co-authored-by: Yifan Wang <wanyif@google.com>
PiperOrigin-RevId: 865625039
2026-02-04 16:13:51 -08:00
Kathy Wu 7deffb16fd fix: pass tool context into require_confirmation function in McpTool
Aligns with the FunctionTool implementation of require_confirmation. This fixes https://github.com/google/adk-python/issues/4327.

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 865566362
2026-02-04 13:55:39 -08:00
nikkie ac1401bd44 fix: Escape Click’s Wrapping in bulled lists of adk web options
Merge https://github.com/google/adk-python/pull/4338

### Link to Issue or Description of Change

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

**Problem:**
Click collapses intended bullet lists in the service URI options.

<img width="709" height="267" alt="image" src="https://github.com/user-attachments/assets/5f74d8a6-f343-41a4-ba6d-570ed0076932" />

**Solution:**
Add `\b` on a line by itself before the formatted block to preserve blank lines.

### Testing Plan

This is format improvement of help message, so I think there is no need to add test case.

**Unit Tests:**

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

```
% pytest tests/unittests/cli  # Python 3.13.8
===================================== 260 passed, 140 warnings in 9.30s ======================================
```

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

`adk web --help`

```
  --session_service_uri TEXT      Optional. The URI of the session service. If set, ADK uses this service.

                                  If unset, ADK chooses a default session service (see
                                  --use_local_storage).
                                  - Use 'agentengine://<agent_engine>' to connect to Agent Engine
                                    sessions. <agent_engine> can either be the full qualified resource
                                    name 'projects/abc/locations/us-central1/reasoningEngines/123' or
                                    the resource id '123'.
                                  - Use 'memory://' to run with the in-memory session service.
                                  - Use 'sqlite://<path_to_sqlite_file>' to connect to a SQLite DB.
                                  - See https://docs.sqlalchemy.org/en/20/core/engines.html#backend-specific-urls
                                    for supported database URIs.
```

### 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/4338 from ftnext:escape-wrapping-web-options-bullet-list 9466731aff293d241cc7ae5728bc3c34c5c89dd6
PiperOrigin-RevId: 865533252
2026-02-04 12:40:13 -08:00
Gabriel Bengo 3c63c2ad39 fix(docs): fix grammar and remove duplicate words in documentation and source
Merge https://github.com/google/adk-python/pull/4335

Fixed 'the the' typos and grammatical errors in README, docstrings, and sample agents.

**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: N/A
- Related: N/A

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

**Problem:**
While exploring the repository, I noticed a few minor writing errors that impact professional readability:
1.  **Grammar:** A subject-verb agreement error in `README.md` and `llms.txt` ("guide the agents works together").
2.  **Duplicate Words:** Several instances of stuttering typos (repeating "the the" or "but the the") in docstrings within `src/` and the `contributing/samples/` directories.

**Solution:**
I have applied the following fixes to improve documentation quality:
*   Corrected phrasing to "guide the agents to work together" in the README.
*   Removed redundant instances of "the" in `eval_metrics.py`, `spanner/settings.py`, and the sample agent docstrings.
*   **No functional code or logic was altered.**

### Testing Plan

**Unit Tests:**

- [x] All unit tests pass locally.

_Summary:_
Since this PR is strictly limited to documentation, comments, and docstrings, no new tests were required. I ran the standard test suite to ensure no syntax errors were accidentally introduced, and everything passed successfully.

**Manual End-to-End (E2E) Tests:**
N/A — This is a static documentation 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.
- [ ] I have commented my code, particularly in hard-to-understand areas. (N/A)
- [ ] I have added tests that prove my fix is effective or that my feature works. (N/A - Doc fix only)
- [x] New and existing unit tests pass locally with my changes.
- [ ] I have manually tested my changes end-to-end. (N/A)
- [x] Any dependent changes have been merged and published in downstream modules.

### Additional context

_None._

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/4335 from 88448844:fix-typos-and-docs 200668dad0c216b81d7008d9d6625ac135c9006e
PiperOrigin-RevId: 865530036
2026-02-04 12:31:54 -08:00
George Weale f90adff8c5 fix: Add query parameters to /run_live for advanced run configurations
The /run_live websocket endpoint now accepts proactive_audio, enable_affective_dialog, and enable_session_resumption as query parameters. These parameters control the corresponding settings within the RunConfig used for the live session

Close #4263

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 865441673
2026-02-04 09:24:01 -08:00
Google Team Member 1de65cf314 No public description
PiperOrigin-RevId: 865138065
2026-02-03 18:26:30 -08:00
Xuan Yang 2220d885cd fix: Check will_continue for streaming function calls
Related: https://github.com/google/adk-python/issues/4311

Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 865074872
2026-02-03 15:33:44 -08:00
Yifan Wang 37e6507ce4 chore: update adk_web, re-work events, and adding A2UI capabilities
Co-authored-by: Yifan Wang <wanyif@google.com>
PiperOrigin-RevId: 865027256
2026-02-03 13:42:44 -08:00
Yeesian Ng 004e15ccb7 feat: Allow passthrough of GOOGLE_CLOUD_LOCATION for AgentEngine deployments
Co-authored-by: Yeesian Ng <ysian@google.com>
PiperOrigin-RevId: 865013321
2026-02-03 13:11:45 -08:00
George Weale 574ec43a17 chore: Improve error handling for LiteLlm import in gemma_llm.py
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 865010258
2026-02-03 13:04:27 -08:00
Liang Wu ce07cd8144 fix(cli): ignore session_db_kwargs for sqlite session services
Close issue #4317

Co-authored-by: Liang Wu <wuliang@google.com>
PiperOrigin-RevId: 864979145
2026-02-03 11:49:13 -08:00
George Weale 706a6dda81 fix: Update OpenTelemetry dependency versions
Relax version constraints for opentelemetry-api and opentelemetry-sdk to allow versions between 1.36.0 and 1.40.0

Close #4229

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 864896212
2026-02-03 08:41:39 -08:00
George Weale da73e718ef fix: Enable pool_pre_ping by default for non-SQLite database engines
This change sets `pool_pre_ping=True` in SQLAlchemy engine kwargs for database backends other than SQLite. This helps ensure that connections from the pool are still valid before being used, preventing issues with stale or disconnected connections. Tests are added to verify the default behavior and that explicit overrides are respected

Close #4211

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 864886767
2026-02-03 08:16:55 -08:00
Google Team Member 125bc85ac5 fix: Resolving MutualTLSChannelError 'OpenSSL' module error on by adding pyopenssl dependency in pyproject.toml
- Add 'pyopenssl' to project dependencies to fix MutualTLSChannelError  encountered during mTLS channel configuration.

PiperOrigin-RevId: 864874340
2026-02-03 07:43:09 -08:00
Google Team Member 6ff10b23be chore: Replace proxy methods with utils implementation
PiperOrigin-RevId: 864802559
2026-02-03 04:01:55 -08:00
Google Team Member f82ceb0ce7 chore: Replace proxy methods with utils implementation
PiperOrigin-RevId: 864773118
2026-02-03 02:27:46 -08:00
George Weale 33012e6dda fix: Make credential key generation stable and prevent cross-user credential leaks
This change updates the credential key generation to use a stable hash (SHA256) instead of Python's built-in hash, which can vary based on PYTHONHASHSEED. It also makes sure that temporary or exchanged OAuth2 fields are excluded from the key calculation. I also added when saving credentials, a copy of the AuthConfig is used to avoid modifying the original shared AuthConfig instance with user-specific exchanged credentials.

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 864599326
2026-02-02 17:51:19 -08:00
George Weale 666cebe369 fix: Add update_timestamp_tz property to StorageSession
This property is a compatibility alias that returns the update timestamp as a POSIX timestamp. It infers whether the database is SQLite using sqlalchemy.inspect to call get_update_timestamp correctly

Close #4334

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 864595914
2026-02-02 17:39:42 -08:00
George Weale dd8cd27b2c chore: Replace print statements with logging in ADK
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 864588921
2026-02-02 17:16:27 -08:00
Max Ind e87a8437fb feat(otel): add extra attributes to span generated with opentelemetry-instrumentation-google-genai
Co-authored-by: Max Ind <maxind@google.com>
PiperOrigin-RevId: 864272319
2026-02-02 03:17:08 -08:00
Google Team Member 7d58e0d2f3 feat: Mark Vertex calls made from non-gemini models
PiperOrigin-RevId: 864253424
2026-02-02 02:23:56 -08:00
Vasilii Novikov 9290b96626 feat: Make OpenAPI tool async
Merge https://github.com/google/adk-python/pull/2872

Closes https://github.com/google/adk-python/issues/787

The OpenAPI tool has been ported to the httpx client to make requests truly asynchronous.

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2872 from condorcet:async_openapi_tool bf83f73af93f624126462fb0bd41fef27c53a0b6
PiperOrigin-RevId: 864250822
2026-02-02 02:15:52 -08:00
Xiang (Sean) Zhou 2770012cec chore: Add sample agent that need to go through oauth flow during mcp tool listing
Co-authored-by: Xiang (Sean) Zhou <seanzhougoogle@google.com>
PiperOrigin-RevId: 864133951
2026-02-01 20:22:25 -08:00
Xiang (Sean) Zhou 131fbd3948 fix: Fix Pydantic Schema Generation Error for ClientSession
Problem: The test test_openapi_json_schema_accessible was failing because Pydantic couldn't generate a JSON schema for mcp.client.session.ClientSession, which is part of genai_types.ToolListUnion.

Root Cause: The AgentDetails model in src/google/adk/evaluation/app_details.py:37 had tool_declarations: genai_types.ToolListUnion, and ToolListUnion includes mcp.client.session.ClientSession which doesn't have Pydantic core schema support. This model is used in the FastAPI app through EvalCase → Invocation → AppDetails → AgentDetails, causing OpenAPI schema generation to fail.

Solution: Changed the type annotation from genai_types.ToolListUnion to list[Any] in two places:

AgentDetails.tool_declarations
_ToolDeclarations.tool_declarations

This allows Pydantic to generate the OpenAPI schema while maintaining runtime compatibility (the field still accepts the same values).

Co-authored-by: Xiang (Sean) Zhou <seanzhougoogle@google.com>
PiperOrigin-RevId: 863765002
2026-01-31 16:26:30 -08:00
Xiang (Sean) Zhou 798f65df86 feat(tools): Implement toolset auth for McpToolset, OpenAPIToolset, and others
Update existing toolsets to utilize the new toolset authentication framework. Key changes:
        - McpToolset: Add _auth_config instance variable, _get_auth_headers()
          method to build auth headers from exchanged credentials, and
          get_auth_config() override. Auth headers are now included when
          creating MCP sessions.
        - OpenAPIToolset: Add _auth_config and get_auth_config() to expose
          auth configuration to the framework.
        - ApplicationIntegrationToolset: Add _auth_config and get_auth_config().
        - APIHubToolset: Add _auth_config and get_auth_config().

When ADK resolves toolset auth before calling get_tools(), it populates exchanged_auth_credential on the auth_config. Toolsets can then use this credential when making authenticated requests.

Also update test fixtures in test_apihub_toolset.py to use real auth objects instead of mocks that fail pydantic validation.

Co-authored-by: Xiang (Sean) Zhou <seanzhougoogle@google.com>
PiperOrigin-RevId: 863764941
2026-01-31 16:25:46 -08:00
Xiang (Sean) Zhou ee873cae2e feat(auth): Add framework support for toolset authentication before get_tools
This change adds framework-level support for resolving toolset authentication before calling get_tools(). Key changes:
    - Add _resolve_toolset_auth() method in BaseLlmFlow that iterates
      through toolsets, checks for auth config, and resolves credentials
      via CredentialManager before tool listing
    - Add TOOLSET_AUTH_CREDENTIAL_ID_PREFIX constant for identifying
      toolset auth requests
    - Add skip logic in auth_preprocessor to not resume function calls
      for toolset auth (they do not need it)
    - Add get_auth_response() method to CallbackContext for retrieving
      auth credentials from session state
    - Update CredentialManager to accept CallbackContext instead of
      requiring ToolContext

When a toolset needs authentication but credentials are not available, the flow yields an adk_request_credential event and interrupts the invocation, allowing the user to complete the OAuth flow before retrying.

Co-authored-by: Xiang (Sean) Zhou <seanzhougoogle@google.com>
PiperOrigin-RevId: 863543036
2026-01-30 22:36:13 -08:00
Xiang (Sean) Zhou fe82f3cde8 fix!: Make credential manager to accept tool_context instead of callback_context
This seems a breaking change, but actually credential manager is used internally only and also it won't work if some one call it using callback context

Co-authored-by: Xiang (Sean) Zhou <seanzhougoogle@google.com>
PiperOrigin-RevId: 863534476
2026-01-30 21:59:43 -08:00
George Weale 798d0053c8 fix: Stream errors as simple JSON objects in ADK web server SSE
The ADK web server's /run_sse endpoint now yields a JSON object like {"error": "..."} when an exception occurs during event generation. The adk_web_server_client is updated to detect this error payload and raise a RuntimeError.

Close #4291

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 863475838
2026-01-30 18:18:31 -08:00
George Weale d0102ecea3 fix: Recognize function responses as non-empty parts in LiteLLM
The `_part_has_payload` helper now returns False for parts containing a function response, preventing the addition of fallback user content when a user turn consists solely of tool outputs.

Close #4249

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 863319497
2026-01-30 11:20:50 -08:00
Liang Wu 63a8eba53f fix: Ensure database sessions are always rolled back on errors
Fixes issue#3328

Co-authored-by: Liang Wu <wuliang@google.com>
PiperOrigin-RevId: 863314939
2026-01-30 11:10:02 -08:00
George Weale 47221cd5c1 fix: Handle HTTP/HTTPS URLs for media files in LiteLLM content conversion
For providers that typically require file IDs (like OpenAI and Azure), if a file URI is an HTTP/HTTPS URL and the MIME type is image, video, or audio, convert it to the corresponding URL-based content type (e.g., "image_url") instead of using the generic "file" type

Close #4112

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 863311703
2026-01-30 11:02:29 -08:00
George Weale 2ac468ea7e fix: Add pre-deployment validation for agent module imports
This change introduces a new function, _validate_agent_import, which attempts to import the user's agent.py file before the deployment process begins. This helps catch common issues such as missing dependencies, incorrect relative imports, or syntax errors in the agent code early, providing more informative error messages to the user.

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 863310033
2026-01-30 10:58:15 -08:00
Google Team Member 00aba2d884 feat: Improve asyncio loop handling and test cleanup
This CL enhances asyncio event loop management and test isolation.

-   **BigQuery Analytics Plugin:** Ensure the asyncio event loop is consistently closed within the BigQuery analytics plugin. This prevents potential resource leaks. Add checks to handle potential deadlocks in Python 3.13+ when creating loops during interpreter shutdown.
-   **Test Thread Pool Cleanup:** Introduce a pytest fixture (`cleanup_thread_pools`) to automatically shut down and clear all tool-related thread pools after each test run in `test_functions_thread_pool.py`. This improves test isolation and prevents order-dependent test failures.
-   **Streaming Test Loop Restoration:** Refactor event loop handling in `test_streaming.py`. A new `_run_with_loop` method is introduced in the custom test runners to create a temporary event loop for each test execution, run the coroutine, and crucially, restore the original event loop afterwards. This prevents tests from interfering with each other's loop state.
-   **Resource Closure:** Ensure services are closed properly in tests by adding `await service.close()` in `test_service_factory.py` and using `async with session_service` in `test_session_service.py`.

PiperOrigin-RevId: 863305565
2026-01-30 10:47:09 -08:00
Xuan Yang 585ebfdac7 feat: Support dynamic config for VertexAiSearchTool
Close: https://github.com/google/adk-python/issues/4067

Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 862965769
2026-01-29 17:09:49 -08:00
Google Team Member c0c98d94b3 test: Ensure BigQueryAgentAnalyticsPlugin is shut down after each test
Improves test reliability by guaranteeing the BigQueryAgentAnalyticsPlugin is shut down after each test execution. This is achieved by wrapping test logic in try/finally blocks, using a yield fixture, or employing a new asynccontextmanager to ensure the plugin.shutdown() method is always called, preventing potential resource leaks between tests.

PiperOrigin-RevId: 862951816
2026-01-29 16:31:04 -08:00
Xuan Yang 288c2c448d chore: Add ADK logger in RestApiTool
Fixes: https://github.com/google/adk-python/issues/3780

Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 862897383
2026-01-29 14:17:52 -08:00
Kathy Wu ecce7e54a6 fix: Change MCP read_resource to return the original contents
Since we will save MCP resource contents to the artifact service, no need to do post-processing based on mime type. Also fixed the implementation; the correct method to call is session.read_resource(uri).

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 862885513
2026-01-29 13:51:45 -08:00
Xiang (Sean) Zhou 381d44cab4 feat: Add get_auth_config method to toolset to expose auth requirement of the toolset
Co-authored-by: Xiang (Sean) Zhou <seanzhougoogle@google.com>
PiperOrigin-RevId: 862597425
2026-01-29 00:23:24 -08:00
Kathy Wu 4341839420 chore: Convert MCPToolset to McpToolset in unittests
MCPToolset has been deprecated, use McpToolset instead.

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 862457868
2026-01-28 17:04:14 -08:00
Sara Robinson 32f9f92042 chore: Add GitHub Action check to run mypy
Merge https://github.com/google/adk-python/pull/4276

This adds 2 mypy GitHub Actions checks:

* `mypy.yml` runs mypy on every PR. This will list all errors, so it shouldn't block until errors are resolved.
* `mypy-new-errors.yml` compares the PR branch to `main` to ensure no new mypy errors are introduced. This check can be made blocking now and can be removed once all errors are resolved in favor of `mypy.yml`.

### 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/4276 from sararob:mypy-gh-actions 63130773c4862e90e352d4cb79221d78a6b9f5fd
PiperOrigin-RevId: 862226296
2026-01-28 07:27:52 -08:00
Google Team Member a16e3cc67e fix: Fix cases where execution_result_delimiters have None type element due to code_execution_result.output is None
PiperOrigin-RevId: 861933883
2026-01-27 16:29:51 -08:00
Kathy Wu 8f7d9659cf feat: Add methods in MCPToolset for users to access MCP resources (read_resource, list_resources, get_resource_info)
This allows users to access MCP resources on their own within agent logic / using custom tools. I plan on also later adding it to the agent state.

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 861910520
2026-01-27 15:26:59 -08:00
Google Team Member 3480b3b82d feat: improve error message when failing to get tools from MCP
Include the underlying exception message in the ConnectionError raised when session.list_tools() fails.

PiperOrigin-RevId: 861889733
2026-01-27 14:33:47 -08:00
Liang Wu 65cbf4b35c fix: Make OpenAPI /docs path work again
* The main fix is in eval_metrics.py
* Removed some deprecated paths in the FastAPI server
* Added a unit test to catch future breakages
* Bump fastapi version to be 0.124.1 to capture the fix in https://github.com/fastapi/fastapi/pull/14482
* Removed the upper-bound restriction on fastapi version which was used to temporarily fix the issue

Fixes #3173

Co-authored-by: Liang Wu <wuliang@google.com>
PiperOrigin-RevId: 861867708
2026-01-27 13:41:26 -08:00
Keyur Joshi 4ee125a038 feat: Add interface for agent optimizers
Details:
* The Sampler allows ADK agent optimizers to request evals and receive detailed eval results that can be used to guide agent optimization.
* This interface allows developers to run custom evaluations if needed for their agent. An implementation to interface with the inbuilt LocalEvalService shall be published shortly in a follow-up PR.
* The AgentOptimizer interface describes the general framework for an ADK agent optimizer that uses the Sampler interface to optimize an ADK agent.
* data_types.py contains generic types and base classes to allow intercommunication between the AgentOptimizer, Sampler, and developer code.

Co-authored-by: Keyur Joshi <keyurj@google.com>
PiperOrigin-RevId: 861849691
2026-01-27 12:56:21 -08:00
Xuan Yang 2155a35c51 docs: Update ADK release analyzer to use Gemini 3 Pro with retry and improve file filtering
Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 861784867
2026-01-27 10:31:03 -08:00
nikkie 3bcd8f7f7a fix: EscapieClick’s Wrapping in adk eval help (#4258)
Fix https://github.com/google/adk-python/issues/4257

https://click.palletsprojects.com/en/stable/documentation/#escaping-click-s-wrapping

Co-authored-by: Ankur <ankusharma@google.com>
2026-01-27 09:13:55 -08:00
Google Team Member c34615ecf3 fix: disable save_input_blobs_as_artifacts deprecation warning message for users that don't set it
PiperOrigin-RevId: 861738532
2026-01-27 08:38:46 -08:00
Shangjie Chen 1063fa532c fix: Reload the stale session in database_session_service when the update_time of storage is later than the current in memory session object
This changes the behavior of how we handle stale session, instead of reject the transaction entirely, we aggresively refresh the session.

Removed synchronous inspect(self).session calls within StorageSession.update_timestamp_tz and _dialect_name. This introspection was causing deadlocks/hangs when used with sqlalchemy.ext.asyncio in Python 3.13.

Closes issue: https://github.com/google/adk-python/issues/1733

Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 861353058
2026-01-26 14:19:14 -08:00
Ankur Sharma 43d6075ea7 feat: allow Vertex AI Client initialization with API Key
Co-authored-by: Ankur Sharma <ankusharma@google.com>
PiperOrigin-RevId: 861335865
2026-01-26 13:36:41 -08:00
Joseph Pagadora 553e376718 feat: Remove overall_eval_status calculation from _CustomMetricEvaluator and add threshold to custom metric function expected signature
Co-authored-by: Joseph Pagadora <jcpagadora@google.com>
PiperOrigin-RevId: 861268984
2026-01-26 11:01:32 -08:00
Jinhyuk Kim 85434e293f feat: Pass event id as a metadata when it is converted into a message
PiperOrigin-RevId: 860929058
2026-01-25 16:05:45 -08:00
Google Team Member 324796b4fe feat: This change restructures the bug report template as per intake process
PiperOrigin-RevId: 860304319
2026-01-23 16:43:56 -08:00
Xuan Yang 853a3b0e14 fix: Do not treat FCs and FRs as invisible when marked as thoughts
Related: https://github.com/google/adk-python/issues/4159

Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 860254198
2026-01-23 14:25:42 -08:00
Xiang (Sean) Zhou 714c3ad047 feat: Support run tools in separate thread for live mode
Co-authored-by: Xiang (Sean) Zhou <seanzhougoogle@google.com>
PiperOrigin-RevId: 860242861
2026-01-23 13:55:31 -08:00
George Weale 801233902b fix: fix agent config path handling in generated deployment script
The generated agent deployment script now uses os.path.join and os.path.dirname(__file__) to construct the path to root_agent.yaml. This prevents issues where backslashes in Windows paths could be misinterpreted as Python escape sequences within the f-string, leading to syntax error

Close #4223

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 860236820
2026-01-23 13:39:32 -08:00
Liang Wu 025b42c836 chore: Add unittests.sh script and update CONTRIBUTING.md
This change introduces a new `unittests.sh` script to simplify running unit tests. The script automatically sets up a minimal test environment, runs `pytest`, and then restores the previous development environment. The CONTRIBUTING.md has been updated to include instructions on using this new script.

Co-authored-by: Liang Wu <wuliang@google.com>
PiperOrigin-RevId: 860222798
2026-01-23 13:01:06 -08:00
Xiang (Sean) Zhou 753084fd46 refactor: Extract helper function for llm request building and response processing
Co-authored-by: Xiang (Sean) Zhou <seanzhougoogle@google.com>
PiperOrigin-RevId: 860212868
2026-01-23 12:34:04 -08:00
Google Team Member 2380afd287 feat: Refine Feature Request Issue Template
This change updates the feature request issue template to include required and recommended sections. New questions are added to gather more details on the problem, impact, willingness to contribute, and potential API/implementation ideas. Existing questions are rephrased for clarity

PiperOrigin-RevId: 860199885
2026-01-23 12:00:38 -08:00
Kathy Wu 316463be86 docs: Move mcp session feature out of breaking changes
Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 860199237
2026-01-23 11:58:54 -08:00
Xuan Yang 83c24d2e74 docs: Fix ADK release analyzer agent for large releases
Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 860187312
2026-01-23 11:29:42 -08:00
Shangjie Chen e8f7aa3140 fix: Add pypika>=0.50.0 to project.toml since crewai depends on it and the default version does not work with py 3.12+
Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 860181604
2026-01-23 11:15:19 -08:00
Google Team Member 7edfb2701c docs: update data agent README to match the behavior
PiperOrigin-RevId: 860180180
2026-01-23 11:11:56 -08:00
Max Ind 0d38a3683f fix: remove print debugging artifact
Co-authored-by: Max Ind <maxind@google.com>
PiperOrigin-RevId: 860038888
2026-01-23 03:59:44 -08:00
Kathy Wu 7cf1e447d7 chore: Bumps version to v1.23.0 and updates CHANGELOG.md
Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 859790616
2026-01-22 15:03:52 -08:00
Xuan Yang 3f1b0d0957 chore: minor fix for DebugLoggingPlugin example
Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 859762221
2026-01-22 14:00:17 -08:00
Max Ind 935c279f82 feat(otel): add minimal generate_content {model.name} spans and logs for non-gemini inference and when opentelemetry-inference-google-genai dependency is missing
Co-authored-by: Max Ind <maxind@google.com>
PiperOrigin-RevId: 859667045
2026-01-22 10:18:06 -08:00
Google Team Member 82fa10b71e feat: add new conversational analytics api tool set
PiperOrigin-RevId: 859449435
2026-01-21 23:31:37 -08:00
Xuan Yang bf2b56de6d fix: recursively extract input/output schema for AgentTool
Fixes: https://github.com/google/adk-python/issues/4154

Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 859440231
2026-01-21 23:02:33 -08:00
176 changed files with 16481 additions and 5862 deletions
+51 -16
View File
@@ -7,34 +7,69 @@ assignees: ''
---
** Please make sure you read the contribution guide and file the issues in the right place. **
[Contribution guide.](https://google.github.io/adk-docs/contributing-guide/)
## đź”´ Required Information
*Please ensure all items in this section are completed to allow for efficient
triaging. Requests without complete information may be rejected / deprioritized.
If an item is not applicable to you - please mark it as N/A*
**Describe the bug**
**Describe the Bug:**
A clear and concise description of what the bug is.
**To Reproduce**
Please share a minimal code and data to reproduce your problem.
Steps to reproduce the behavior:
**Steps to Reproduce:**
Please provide a numbered list of steps to reproduce the behavior:
1. Install '...'
2. Run '....'
3. Open '....'
4. Provide error or stacktrace
**Expected behavior**
**Expected Behavior:**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Observed Behavior:**
What actually happened? Include error messages or crash stack traces here.
**Desktop (please complete the following information):**
- OS: [e.g. macOS, Linux, Windows]
- Python version(python -V):
- ADK version(pip show google-adk):
**Environment Details:**
- ADK Library Version (pip show google-adk):
- Desktop OS:** [e.g., macOS, Linux, Windows]
- Python Version (python -V):
**Model Information:**
**Model Information:**
- Are you using LiteLLM: Yes/No
- Which model is being used(e.g. gemini-2.5-pro)
- Which model is being used: (e.g., gemini-2.5-pro)
**Additional context**
---
## 🟡 Optional Information
*Providing this information greatly speeds up the resolution process.*
**Regression:**
Did this work in a previous version of ADK? If so, which one?
**Logs:**
Please attach relevant logs. Wrap them in code blocks (```) or attach a
text file.
```text
// Paste logs here
```
**Screenshots / Video:**
If applicable, add screenshots or screen recordings to help explain
your problem.
**Additional Context:**
Add any other context about the problem here.
**Minimal Reproduction Code:**
Please provide a code snippet or a link to a Gist/repo that isolates the issue.
```python
// Code snippet here
```
**How often has this issue occurred?:**
- Always (100%)
- Often (50%+)
- Intermittently (<50%)
- Once / Rare
+33 -8
View File
@@ -10,14 +10,39 @@ assignees: ''
** Please make sure you read the contribution guide and file the issues in the right place. **
[Contribution guide.](https://google.github.io/adk-docs/contributing-guide/)
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
## đź”´ Required Information
*Please ensure all items in this section are completed to allow for efficient
triaging. Requests without complete information may be rejected / deprioritized.
If an item is not applicable to you - please mark it as N/A*
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
### Is your feature request related to a specific problem?
Please describe the problem you are trying to solve. (Ex: "I'm always frustrated
when I have to manually handle X...")
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
### Describe the Solution You'd Like
A clear and concise description of the feature or API change you want.
Be specific about input/outputs if this involves an API change.
**Additional context**
Add any other context or screenshots about the feature request here.
### Impact on your work
How does this feature impact your work and what are you trying to achieve?
If this is critical for you, tell us if there is a timeline by when you need
this feature.
### Willingness to contribute
Are you interested in implementing this feature yourself or submitting a PR?
(Yes/No)
---
## 🟡 Recommended Information
### Describe Alternatives You've Considered
A clear and concise description of any alternative solutions or workarounds
you've considered and why they didn't work for you.
### Proposed API / Implementation
If you have ideas on how this should look in code, please share a
pseudo-code example.
### Additional Context
Add any other context or screenshots about the feature request here.
+74
View File
@@ -0,0 +1,74 @@
name: Mypy New Error Check
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
mypy-diff:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12', '3.13',]
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Generate Baseline (Main)
run: |
# Switch to main branch to generate baseline
git checkout origin/main
git checkout ${{ github.sha }} -- pyproject.toml
# Install dependencies for main
uv venv .venv
source .venv/bin/activate
uv sync --all-extras
# Run mypy, filter for errors only, remove line numbers (file:123: -> file::), and sort
# We ignore exit code (|| true) because we expect errors on main
uv run mypy . | grep "error:" | sed 's/:\([0-9]\+\):/::/g' | sort > main_errors.txt || true
echo "Found $(wc -l < main_errors.txt) errors on main."
- name: Check PR Branch
run: |
# Switch back to the PR commit
git checkout ${{ github.sha }}
# Re-sync dependencies in case the PR changed them
source .venv/bin/activate
uv sync --all-extras
# Run mypy on PR code, apply same processing
uv run mypy . | grep "error:" | sed 's/:\([0-9]\+\):/::/g' | sort > pr_errors.txt || true
echo "Found $(wc -l < pr_errors.txt) errors on PR branch."
- name: Compare and Fail on New Errors
run: |
# 'comm -13' suppresses unique lines in file1 (main) and common lines,
# leaving only lines unique to file2 (PR) -> The new errors.
comm -13 main_errors.txt pr_errors.txt > new_errors.txt
if [ -s new_errors.txt ]; then
echo "::error::The following NEW mypy errors were introduced:"
cat new_errors.txt
exit 1
else
echo "Great job! No new mypy errors introduced."
fi
+32
View File
@@ -0,0 +1,32 @@
name: Mypy Type Check
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
mypy:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12', '3.13',]
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v1
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: uv sync --all-extras
- name: Run mypy
run: uv run mypy . --strict
+175
View File
@@ -1,5 +1,180 @@
# Changelog
## [1.24.1](https://github.com/google/adk-python/compare/v1.24.0...v1.24.1) (2026-02-06)
### Bug Fixes
* Add back deprecated eval endpoint for web until we migrate([ae993e8](https://github.com/google/adk-python/commit/ae993e884f44db276a4116ebb7a11a2fb586dbfe))
* Update eval dialog colors, and fix a2ui component types ([3686a3a](https://github.com/google/adk-python/commit/3686a3a98f46738549cd7a999f3773b7a6fd1182))
## [1.24.0](https://github.com/google/adk-python/compare/v1.23.0...v1.24.0) (2026-02-04)
### âš  BREAKING CHANGES
* Breaking: Make credential manager accept `tool_context` instead of `callback_context` ([fe82f3c](https://github.com/google/adk-python/commit/fe82f3cde854e49be13d90b4c02d786d82f8a202))
### Highlights
* **[Web]**
* **Consolidated Event View**: Replaced the Event tab with a more intuitive "click-to-expand" interaction on message rows, enabling faster debugging within the chat context
* **Enhanced Accessibility**: Added full support for arrow-key navigation for a more seamless, keyboard-centric experience
* **Rich Developer Tooling**: Introduced detailed tooltips for function calls, providing instant visibility into arguments, responses, and state changes
* **A2UI Integration**: Integrated the **A2UI v0.8** standard catalog to automatically render spec-compliant ADK parts as native UI components directly in the chat
### Features
* **[Core]**
* Allow passthrough of `GOOGLE_CLOUD_LOCATION` for Agent Engine deployments ([004e15c](https://github.com/google/adk-python/commit/004e15ccb7c7f683623f8e7d2e77a9d12558c545))
* Add interface for agent optimizers ([4ee125a](https://github.com/google/adk-python/commit/4ee125a03856fdb9ed28245bf7f5917c2d9038db))
* Pass event ID as metadata when converted into a message ([85434e2](https://github.com/google/adk-python/commit/85434e293f7bd1e3711f190f84d5a36804e4462b))
* Restructure the bug report template as per the intake process ([324796b](https://github.com/google/adk-python/commit/324796b4fe05bec3379bfef67071a29552ef355a))
* **[Models]**
* Mark Vertex calls made from non-Gemini models ([7d58e0d](https://github.com/google/adk-python/commit/7d58e0d2f375bc80bdfac9cffea2926fd2344b8a))
* **[Evals]**
* Allow Vertex AI Client initialization with API Key ([43d6075](https://github.com/google/adk-python/commit/43d6075ea7aa49ddb358732f2219ca9598dd286f))
* Remove overall evaluation status calculation from `_CustomMetricEvaluator` and add threshold to custom metric function expected signature ([553e376](https://github.com/google/adk-python/commit/553e376718ceb3d7fb1403231bb720836d71f42c))
* **[Tools]**
* Make OpenAPI tool asynchronous ([9290b96](https://github.com/google/adk-python/commit/9290b966267dc02569786f95aab2a3cb78c7004f))
* Implement toolset authentication for `McpToolset`, `OpenAPIToolset`, and other toolsets ([798f65d](https://github.com/google/adk-python/commit/798f65df86b1bbe33d864e30c5b1f9e155e89810))
* Add framework support for toolset authentication before `get_tools` calls ([ee873ca](https://github.com/google/adk-python/commit/ee873cae2e2df960910d264a4340ce6c0489eb7a))
* Support dynamic configuration for `VertexAiSearchTool` ([585ebfd](https://github.com/google/adk-python/commit/585ebfdac7f1b8007b4e4a7e4258ec5de72c78b1))
* Add `get_auth_config` method to toolset to expose authentication requirements ([381d44c](https://github.com/google/adk-python/commit/381d44cab437cac027af181ae627e7b260b7561e))
* Add methods in `McpToolset` for users to access MCP resources ([8f7d965](https://github.com/google/adk-python/commit/8f7d9659cfc19034af29952fbca765d012169b38))
* Improve error message when failing to get tools from MCP ([3480b3b](https://github.com/google/adk-python/commit/3480b3b82d89de69f77637d7ad034827434df45a))
* **[Services]**
* Improve `asyncio` loop handling and test cleanup ([00aba2d](https://github.com/google/adk-python/commit/00aba2d884d24fb5244d1de84f8dba9cbc3c07e8))
* **[Live]**
* Support running tools in separate threads for live mode ([714c3ad](https://github.com/google/adk-python/commit/714c3ad0477e775fba6696a919a366a293197268))
* **[Observability]**
* Add extra attributes to spans generated with `opentelemetry-instrumentation-google-genai` ([e87a843](https://github.com/google/adk-python/commit/e87a8437fb430e0d4c42c73948e3ba1872040a15))
### Bug Fixes
* Ignore `session_db_kwargs` for SQLite session services ([ce07cd8](https://github.com/google/adk-python/commit/ce07cd8144c8498434f68e61ebeb519bf329f778))
* Resolve `MutualTLSChannelError` by adding `pyopenssl` dependency ([125bc85](https://github.com/google/adk-python/commit/125bc85ac5e1400bc38f7c681f76fa82626c9911))
* Add `update_timestamp_tz` property to `StorageSession` ([666cebe](https://github.com/google/adk-python/commit/666cebe3693d2981fd5fea6e9e4c65e56dcd3f2b))
* Do not treat Function Calls and Function Responses as invisible when marked as thoughts ([853a3b0](https://github.com/google/adk-python/commit/853a3b0e143ce27516f0de51e0e0df2af6ecf465))
* Add pre-deployment validation for agent module imports (credit to @ppgranger, [2ac468e](https://github.com/google/adk-python/commit/2ac468ea7e30ef30c1324ffc86f67dbf32ab7ede))
* Fix cases where `execution_result_delimiters` have `None` type element ([a16e3cc](https://github.com/google/adk-python/commit/a16e3cc67e1cb391228ba78662547672404ae550))
* Disable `save_input_blobs_as_artifacts` deprecation warning message for users not setting it ([c34615e](https://github.com/google/adk-python/commit/c34615ecf3c7bbe0f4275f72543774f258c565b4))
* Fix agent config path handling in generated deployment script ([8012339](https://github.com/google/adk-python/commit/801233902bbd6c0cca63b6fc8c1b0b2531f3f11e))
* Add `pypika>=0.50.0` to `project.toml` to support `crewai` on Python 3.12+ ([e8f7aa3](https://github.com/google/adk-python/commit/e8f7aa3140d2585ac38ebfe31c5b650383499a20))
* Update OpenTelemetry dependency versions to relax version constraints for `opentelemetry-api` and `opentelemetry-sdk` ([706a6dd](https://github.com/google/adk-python/commit/706a6dda8144da147bd9fa42ef85bbfa58fec5d3))
* Enable `pool_pre_ping` by default for non-SQLite database engines ([da73e71](https://github.com/google/adk-python/commit/da73e718efa9557ed33e2fb579de68fcbcf4c7f0))
* Ensure database sessions are always rolled back on errors ([63a8eba](https://github.com/google/adk-python/commit/63a8eba53f2cb07625eb7cd111ff767e8e0030fa))
* Reload stale session in `DatabaseSessionService` when storage update time is later than the in-memory session object ([1063fa5](https://github.com/google/adk-python/commit/1063fa532cad59d8e9f7421ce2f523724d49d541))
* Make credential key generation stable and prevent cross-user credential leaks ([33012e6](https://github.com/google/adk-python/commit/33012e6dda9ef20c7a1dae66a84717de5d782097))
* Change MCP `read_resource` to return original contents ([ecce7e5](https://github.com/google/adk-python/commit/ecce7e54a688a915a1b9d742c39e4684186729be))
* Recognize function responses as non-empty parts in LiteLLM ([d0102ec](https://github.com/google/adk-python/commit/d0102ecea331e062190dbb7578a4ef7f4044306e))
* Handle HTTP/HTTPS URLs for media files in LiteLLM content conversion ([47221cd](https://github.com/google/adk-python/commit/47221cd5c1e778cd4b92ed8d382c639435f5728c))
* Fix Pydantic schema generation error for `ClientSession` ([131fbd3](https://github.com/google/adk-python/commit/131fbd39482980572487a30fea13236d2badd543))
* Fix Click’s Wrapping in `adk eval` help message ([3bcd8f7](https://github.com/google/adk-python/commit/3bcd8f7f7a0683f838005bc209f7d39dc93f850b))
* Stream errors as simple JSON objects in ADK web server SSE endpoint ([798d005](https://github.com/google/adk-python/commit/798d0053c832e7ed52e2e104f8a14f789ba8b17f))
* Remove print debugging artifact ([0d38a36](https://github.com/google/adk-python/commit/0d38a3683f13bc12dc5d181164b6cd5d72fc260c))
### Improvements
* Check `will_continue` for streaming function calls ([2220d88](https://github.com/google/adk-python/commit/2220d885cda875144b52338b5becf6e5546f3f51))
* Update ADK web, rework events, and add A2UI capabilities ([37e6507](https://github.com/google/adk-python/commit/37e6507ce4d8750100d914eb1a62014350ef1795))
* Improve error handling for LiteLLM import in `gemma_llm.py` ([574ec43](https://github.com/google/adk-python/commit/574ec43a175e3bf3a05e73114e8db7196fae7040))
* Replace proxy methods with utils implementation ([6ff10b2](https://github.com/google/adk-python/commit/6ff10b23be01c1f7dd79d13ac8c679c079140f76), [f82ceb0](https://github.com/google/adk-python/commit/f82ceb0ce75d3efed7c046835ddac76c28210013))
* Replace print statements with logging in ADK evaluation components ([dd8cd27](https://github.com/google/adk-python/commit/dd8cd27b2ce505ecca50cdfbb1469db01c82b0af))
* Add sample agent that requires OAuth flow during MCP tool listing, and convert `MCPToolset` to `McpToolset` in unit tests ([2770012](https://github.com/google/adk-python/commit/2770012cecdfc71628a818a75b21faabe828b4e5), [4341839](https://github.com/google/adk-python/commit/43418394202c2d01b0d37f6424bd601148077e27))
* Ensure `BigQueryAgentAnalyticsPlugin` is shut down after each test ([c0c98d9](https://github.com/google/adk-python/commit/c0c98d94b3161d6bf9fff731e0abfc985b53e653))
* Add ADK logger in `RestApiTool` ([288c2c4](https://github.com/google/adk-python/commit/288c2c448d77c574dafadf7851a49e6ff59fa7f4))
* Add GitHub Action check to run `mypy` ([32f9f92](https://github.com/google/adk-python/commit/32f9f92042ab530220ac9d159045c91d311affa7))
* Add `unittests.sh` script and update `CONTRIBUTING.md` ([025b42c](https://github.com/google/adk-python/commit/025b42c8361ad2078593e3e7fc5301df88a532c7))
* Extract helper function for LLM request building and response processing ([753084f](https://github.com/google/adk-python/commit/753084fd46c9637488f33b0a05b4d270f6e03a39))
## [1.23.0](https://github.com/google/adk-python/compare/v1.22.1...v1.23.0) (2026-01-22)
### âš  BREAKING CHANGES
* Breaking: Use OpenTelemetry for BigQuery plugin tracing, replacing custom `ContextVar` implementation ([ab89d12](https://github.com/google/adk-python/commit/ab89d1283430041afb303834749869e9ee331721))
### Features
* **[Core]**
* Add support to automatically create a session if one does not exist ([8e69a58](https://github.com/google/adk-python/commit/8e69a58df4eadeccbb100b7264bb518a46b61fd7))
* Remove `@experimental` decorator from `AgentEngineSandboxCodeExecutor` ([135f763](https://github.com/google/adk-python/commit/135f7633253f6a415302142abc3579b664601d5b))
* Add `--disable_features` CLI option to override default feature enable state ([53b67ce](https://github.com/google/adk-python/commit/53b67ce6340f3f3f8c3d732f9f7811e445c76359))
* Add `otel_to_cloud` flag to `adk deploy agent_engine` command ([21f63f6](https://github.com/google/adk-python/commit/21f63f66ee424501d9a70806277463ef718ae843))
* Add `is_computer_use` field to agent information in `adk-web` server ([5923da7](https://github.com/google/adk-python/commit/5923da786eb1aaef6f0bcbc6adc906cbc8bf9b36))
* Allow `thinking_config` in `generate_content_config` ([e162bb8](https://github.com/google/adk-python/commit/e162bb8832a806e2380048e39165bf837455f88c))
* Convert A2UI messages between A2A `DataPart` metadata and ADK events ([1133ce2](https://github.com/google/adk-python/commit/1133ce219c5a7a9a85222b03e348ba6b13830c8f))
* Add `--enable_features` CLI option to override default feature enable state ([79fcddb](https://github.com/google/adk-python/commit/79fcddb39f71a4c1342e63b4d67832b3eccb2652))
* **[Tools]**
* Add flush mechanism to `BigQueryAgentAnalyticsPlugin` to ensure pending log events are written to BigQuery ([9579bea](https://github.com/google/adk-python/commit/9579bea05d946b3d8b4bfec35e510725dd371224))
* Allow Google Search tool to set a different model ([b57a3d4](https://github.com/google/adk-python/commit/b57a3d43e4656f5a3c5db53addff02b67d1fde26))
* Support authentication for MCP tool listing ([e3d542a](https://github.com/google/adk-python/commit/e3d542a5ba3d357407f8cd29cfdd722f583c8564) [19315fe](https://github.com/google/adk-python/commit/19315fe557039fa8bf446525a4830b1c9f40cba9))
* Use JSON schema for `base_retrieval_tool`, `load_artifacts_tool`, and `load_memory_tool` declarations when the feature is enabled ([69ad605](https://github.com/google/adk-python/commit/69ad605bc4bbe9a4f018127fd3625169ee70488e))
* Use JSON schema for `IntegrationConnectorTool` declaration when the feature is enabled ([2ed6865](https://github.com/google/adk-python/commit/2ed686527ac75ff64128ce7d9b1a3befc2b37c64))
* Start and close `ClientSession` in a single task in `McpSessionManager` ([cce430d](https://github.com/google/adk-python/commit/cce430da799766686e65f6cae02ba64e916d5c8a))
* Use JSON schema for `RestApiTool` declaration when the feature is enabled ([a5f0d33](https://github.com/google/adk-python/commit/a5f0d333d7f26f2966ed511d5d9def7a1933f0c2))
* **[Evals]**
* Update `adk eval` CLI to consume custom metrics by adding `CustomMetricEvaluator` ([ea0934b](https://github.com/google/adk-python/commit/ea0934b9934c1fefd129a1026d6af369f126870e))
* Update `EvalConfig` and `EvalMetric` data models to support custom metrics ([6d2f33a](https://github.com/google/adk-python/commit/6d2f33a59cfba358dd758378290125fc2701c411))
* **[Observability]**
* Add minimal `generate_content {model.name}` spans and logs for non-Gemini inference and when `opentelemetry-inference-google-genai` dependency is missing ([935c279](https://github.com/google/adk-python/commit/935c279f8281bde99224f03d936b8abe51cbabfc))
* **[Integrations]**
* Enhance `TraceManager` asynchronous safety, enrich BigQuery plugin logging, and fix serialization ([a4116a6](https://github.com/google/adk-python/commit/a4116a6cbfadc161982af5dabd55a711d79348b7))
* **[Live]**
* Persist user input content to session in live mode ([a04828d](https://github.com/google/adk-python/commit/a04828dd8a848482acbd48acc7da432d0d2cb0aa))
### Bug Fixes
* Recursively extract input/output schema for AgentTool ([bf2b56d](https://github.com/google/adk-python/commit/bf2b56de6d0052e40b6d871b2d22c56e9225e145))
* Yield buffered `function_call` and `function_response` events during live streaming ([7b25b8f](https://github.com/google/adk-python/commit/7b25b8fb1daf54d7694bf405d545d46d2c012d2b))
* Update `authlib` and `mcp` dependency versions ([7955177](https://github.com/google/adk-python/commit/7955177fb28b8e5dc19aae8be94015a7b5d9882a))
* Set `LITELLM_MODE` to `PRODUCTION` before importing LiteLLM to prevent implicit `.env` file loading ([215c2f5](https://github.com/google/adk-python/commit/215c2f506e21a3d8c39551b80f6356943ecae320))
* Redact sensitive information from URIs in logs ([5257869](https://github.com/google/adk-python/commit/5257869d91a77ebd1381538a85e7fdc3a600da90))
* Handle asynchronous driver URLs in the migration tool ([4b29d15](https://github.com/google/adk-python/commit/4b29d15b3e5df65f3503daffa6bc7af85159507b))
* Remove custom metadata from A2A response events ([81eaeb5](https://github.com/google/adk-python/commit/81eaeb5eba6d40cde0cf6147d96921ed1bf7bb31))
* Handle `None` inferences in eval results ([7d4326c](https://github.com/google/adk-python/commit/7d4326c3606a7ff2ba3c0fdef08d4f6af52ee71e))
* Mark all parts of a thought event as thought ([f92d4e3](https://github.com/google/adk-python/commit/f92d4e397f37445fe9032a95ce26646a3a69300b))
* Use `json.dumps` for error messages in SSE events ([6ad18cc](https://github.com/google/adk-python/commit/6ad18cc2fc3a3315a0fc240cb51b3283b53116b4))
* Use the correct path for config-based agents when deploying to AgentEngine ([83d7bb6](https://github.com/google/adk-python/commit/83d7bb6ef0d952ad04c5d9a61aaf202672c7e17d))
* Support Generator and Async Generator tool declarations in JSON schema ([19555e7](https://github.com/google/adk-python/commit/19555e7dce6d60c3b960ca0bc2f928c138ac3cc0) [7c28297](https://github.com/google/adk-python/commit/7c282973ea193841fee79f90b8a91c5e02627ccc))
* Prevent stopping event processing on events with `None` content ([ed2c3eb](https://github.com/google/adk-python/commit/ed2c3ebde9cafbb5e2bf375f44db1e77cee9fb24))
* Fix `'NoneType'` object is not iterable error ([7db3ce9](https://github.com/google/adk-python/commit/7db3ce9613b1c2c97e6ca3cd8115736516dc1556))
* Use canonical tools to find streaming tools and register them by `tool.name` ([ec6abf4](https://github.com/google/adk-python/commit/ec6abf401019c39e8e1a8d1b2c7d5cf5e8c7ac56))
* Initialize `self._auth_config` inside `BaseAuthenticatedTool` to access authentication headers in `McpTool` ([d4da1bb](https://github.com/google/adk-python/commit/d4da1bb7330cdb87c1dcbe0b9023148357a6bd07))
* Only filter out audio content when sending history ([712b5a3](https://github.com/google/adk-python/commit/712b5a393d44e7b5ce35fc459da98361bae4bb16))
* Add finish reason mapping and remove custom file URI handling in LiteLLM ([89bed43](https://github.com/google/adk-python/commit/89bed43f5e0c5ad12dd31c716d372145b7e33e78))
* Convert unsupported inline artifact MIME types to text in `LoadArtifactsTool` ([fdc98d5](https://github.com/google/adk-python/commit/fdc98d5c927bfef021e87cf72103892e4c2ac12a))
* Pass `log_level` to `uvicorn` in `web` and `api_server` commands ([38d52b2](https://github.com/google/adk-python/commit/38d52b247600fb45a2beeb041c4698e90c00d705))
* Use the agent name as the author of the audio event ([ab62b1b](https://github.com/google/adk-python/commit/ab62b1bffd7ad2df5809d430ad1823872b8bd67a))
* Handle `NOT_FOUND` error when fetching Vertex AI sessions ([75231a3](https://github.com/google/adk-python/commit/75231a30f1857d930804769caf88bcc20839dd08))
* Fix `httpx` client closure during event pagination ([b725045](https://github.com/google/adk-python/commit/b725045e5a1192bc9fd5190cbd2758ab6ff02590))
### Improvements
* Add new conversational analytics API toolset ([82fa10b](https://github.com/google/adk-python/commit/82fa10b71e037b565cb407c82e9e908432dab0ff))
* Filter out `adk_request_input` event from content list ([295b345](https://github.com/google/adk-python/commit/295b34558774d1f64022009980e3edd8eb79527b))
* Always skip executing partial function calls ([d62f9c8](https://github.com/google/adk-python/commit/d62f9c896c301aba3a781e868735e16f946a8862))
* Update comments of request confirmation preprocessor ([1699b09](https://github.com/google/adk-python/commit/1699b090edc9e5b13c34f461c8e664187157c5c0))
* Fix various typos ([a8f2ddd](https://github.com/google/adk-python/commit/a8f2ddd943301bbf53f49b3a23300ece45803cc0))
* Update sample live streaming tools agent to use latest live models ([3dd7e3f](https://github.com/google/adk-python/commit/3dd7e3f1b9be05c28adb061864d84c4202a2d922))
* Make the regex to catch CLI reference strict by adding word boundary anchor ([c222a45](https://github.com/google/adk-python/commit/c222a45ef74f7b55c48dc151ba98cd8c30a15c57))
* Migrate `ToolboxToolset` to use `toolbox-adk` and align validation ([7dc6adf](https://github.com/google/adk-python/commit/7dc6adf4e563330a09e4cf28d2b1994c24b007d1) [277084e](https://github.com/google/adk-python/commit/277084e31368302e6338b69d456affd35d5fedfe))
* Always log API backend when connecting to live model ([7b035aa](https://github.com/google/adk-python/commit/7b035aa9fc43a43489aeffea8f877cd7eaa09f35))
* Add a sample BigQuery agent using BigQuery MCP tools ([672b57f](https://github.com/google/adk-python/commit/672b57f1b76580023d1f348de76227291a9c1012))
* Add a `DebugLoggingPlugin` to record human-readable debugging logs ([8973618](https://github.com/google/adk-python/commit/8973618b0b0e90c513873e22af272c147efb4904))
* Upgrade the sample BigQuery agent model version to `gemini-2.5-flash` ([fd2c0f5](https://github.com/google/adk-python/commit/fd2c0f556b786417a9f6add744827b07e7a06b7d))
* Import `migration_runner` lazily within the migrate command ([905604f](https://github.com/google/adk-python/commit/905604faac82aca8ae0935eebea288f82985e9c5))
## [1.22.1](https://github.com/google/adk-python/compare/v1.22.0...v1.22.1) (2026-01-09)
+12
View File
@@ -188,6 +188,18 @@ part before or alongside your code PR.
pytest ./tests/unittests
```
**Alternatively**, use the included `unittests.sh` script which handles
environment setup and restoration automatically:
```shell
./scripts/unittests.sh
```
This script will:
- Set up the test environment with minimal dependencies (`test`, `eval`, `a2a`)
- Run the unit tests
- Restore the full development environment (`--all-extras`)
6. **Auto-format the code:**
**NOTE**: We use `isort` and `pyink` for styles. Use the included
+1 -1
View File
@@ -115,7 +115,7 @@ root_agent = Agent(
### Define a multi-agent system:
Define a multi-agent system with coordinator agent, greeter agent, and task execution agent. Then ADK engine and the model will guide the agents works together to accomplish the task.
Define a multi-agent system with coordinator agent, greeter agent, and task execution agent. Then ADK engine and the model will guide the agents to work together to accomplish the task.
```python
from google.adk.agents import LlmAgent, BaseAgent
@@ -57,8 +57,25 @@ from google.adk import Agent
from google.adk.agents.loop_agent import LoopAgent
from google.adk.agents.readonly_context import ReadonlyContext
from google.adk.agents.sequential_agent import SequentialAgent
from google.adk.models import Gemini
from google.adk.tools.exit_loop_tool import exit_loop
from google.adk.tools.tool_context import ToolContext
from google.genai import types
# Retry configuration for handling API rate limits and overload
_RETRY_OPTIONS = types.HttpRetryOptions(
initial_delay=10,
attempts=8,
exp_base=2,
max_delay=300,
http_status_codes=[429, 503],
)
# Use gemini-3-pro-preview for planning and summary (better quality)
GEMINI_PRO_WITH_RETRY = Gemini(
model="gemini-3-pro-preview",
retry_options=_RETRY_OPTIONS,
)
# Maximum number of files per analysis group to avoid context overflow
MAX_FILES_PER_GROUP = 5
@@ -249,7 +266,7 @@ def get_release_context(tool_context: ToolContext) -> dict[str, Any]:
# =============================================================================
planner_agent = Agent(
model="gemini-2.5-pro",
model=GEMINI_PRO_WITH_RETRY,
name="release_planner",
description=(
"Plans the analysis by fetching release info and organizing files into"
@@ -272,10 +289,12 @@ efficient processing.
3. Call `get_changed_files_summary` to get the list of changed files WITHOUT
the full patches (to save context space).
- **IMPORTANT**: Pass these parameters:
- `local_repo_path="{LOCAL_REPOS_DIR_PATH}/{CODE_REPO}"` to avoid 300-file limit
- `path_filter="src/google/adk/"` to only get ADK source files (reduces token usage)
4. Filter and organize the files:
- **INCLUDE** only files in `src/google/adk/` directory
- **EXCLUDE** test files, `__init__.py`, and files outside src/
4. Further filter the returned files:
- **EXCLUDE** test files and `__init__.py` files
- **IMPORTANT**: Do NOT exclude any file just because it has few changes.
Even single-line changes to public APIs need documentation updates.
- **PRIORITIZE** by importance:
@@ -421,7 +440,7 @@ files and finding related documentation that needs updating.
file_group_analyzer = Agent(
model="gemini-2.5-pro",
model=GEMINI_PRO_WITH_RETRY,
name="file_group_analyzer",
description=(
"Analyzes a group of changed files and generates recommendations."
@@ -505,7 +524,7 @@ Present a summary of:
summary_agent = Agent(
model="gemini-2.5-pro",
model=GEMINI_PRO_WITH_RETRY,
name="summary_agent",
description="Compiles recommendations and creates the GitHub issue.",
instruction=summary_instruction,
@@ -540,7 +559,7 @@ analysis_pipeline = SequentialAgent(
# =============================================================================
root_agent = Agent(
model="gemini-2.5-pro",
model=GEMINI_PRO_WITH_RETRY,
name="adk_release_analyzer",
description=(
"Analyzes ADK Python releases and generates documentation update"
+160 -4
View File
@@ -600,23 +600,41 @@ def get_file_diff_for_release(
def get_changed_files_summary(
repo_owner: str, repo_name: str, start_tag: str, end_tag: str
repo_owner: str,
repo_name: str,
start_tag: str,
end_tag: str,
local_repo_path: Optional[str] = None,
path_filter: Optional[str] = None,
) -> Dict[str, Any]:
"""Gets a summary of changed files between two releases without patches.
This is a lighter-weight version of get_changed_files_between_releases
that only returns file paths and metadata, without the actual diff content.
Use this for planning which files to analyze.
This function uses local git commands when local_repo_path is provided,
which avoids the GitHub API's 300-file limit for large comparisons.
Falls back to GitHub API if local_repo_path is not provided or invalid.
Args:
repo_owner: The name of the repository owner.
repo_name: The name of the repository.
start_tag: The older tag (base) for the comparison.
end_tag: The newer tag (head) for the comparison.
local_repo_path: Optional absolute path to local git repo. If provided
and valid, uses git diff instead of GitHub API to get complete
file list (avoids 300-file limit).
path_filter: Optional path prefix to filter files. Only files whose
path starts with this prefix will be included. Example:
"src/google/adk/" to only include ADK source files.
Returns:
A dictionary containing the status and a summary of changed files.
"""
# Use local git if valid path is provided (avoids GitHub API 300-file limit)
if local_repo_path and os.path.isdir(os.path.join(local_repo_path, ".git")):
return _get_changed_files_from_local_git(
local_repo_path, start_tag, end_tag, repo_owner, repo_name, path_filter
)
# Fall back to GitHub API (limited to 300 files)
url = f"{GITHUB_BASE_URL}/repos/{repo_owner}/{repo_name}/compare/{start_tag}...{end_tag}"
try:
@@ -654,8 +672,146 @@ def get_changed_files_summary(
f"https://github.com/{repo_owner}/{repo_name}"
f"/compare/{start_tag}...{end_tag}"
),
"note": (
(
"Using GitHub API which is limited to 300 files. "
"Provide local_repo_path to get complete file list."
)
if len(formatted_files) >= 300
else None
),
}
except requests.exceptions.HTTPError as e:
return error_response(f"HTTP Error: {e}")
except requests.exceptions.RequestException as e:
return error_response(f"Request Error: {e}")
def _get_changed_files_from_local_git(
local_repo_path: str,
start_tag: str,
end_tag: str,
repo_owner: str,
repo_name: str,
path_filter: Optional[str] = None,
) -> Dict[str, Any]:
"""Gets changed files using local git commands (no file limit).
Args:
local_repo_path: Path to local git repository.
start_tag: The older tag (base) for the comparison.
end_tag: The newer tag (head) for the comparison.
repo_owner: Repository owner for compare URL.
repo_name: Repository name for compare URL.
path_filter: Optional path prefix to filter files.
Returns:
A dictionary containing the status and a summary of changed files.
"""
try:
# Fetch tags to ensure we have them
subprocess.run(
["git", "fetch", "--tags"],
cwd=local_repo_path,
capture_output=True,
text=True,
check=False,
)
# Get list of changed files with their status
diff_result = subprocess.run(
["git", "diff", "--name-status", f"{start_tag}...{end_tag}"],
cwd=local_repo_path,
capture_output=True,
text=True,
check=True,
)
# Get numstat for additions/deletions
numstat_result = subprocess.run(
["git", "diff", "--numstat", f"{start_tag}...{end_tag}"],
cwd=local_repo_path,
capture_output=True,
text=True,
check=True,
)
# Parse numstat output (additions, deletions, filename)
file_stats: Dict[str, Dict[str, int]] = {}
for line in numstat_result.stdout.strip().split("\n"):
if not line:
continue
parts = line.split("\t")
if len(parts) >= 3:
additions = int(parts[0]) if parts[0] != "-" else 0
deletions = int(parts[1]) if parts[1] != "-" else 0
filename = parts[2]
file_stats[filename] = {
"additions": additions,
"deletions": deletions,
"changes": additions + deletions,
}
# Parse name-status output and combine with numstat
status_map = {
"A": "added",
"D": "removed",
"M": "modified",
"R": "renamed",
"C": "copied",
}
files_by_dir: Dict[str, List[Dict[str, Any]]] = {}
formatted_files = []
for line in diff_result.stdout.strip().split("\n"):
if not line:
continue
parts = line.split("\t")
if len(parts) >= 2:
status_code = parts[0][0] # First char is the status
filename = parts[-1] # Last part is filename (handles renames)
# Apply path filter if specified
if path_filter and not filename.startswith(path_filter):
continue
stats = file_stats.get(
filename,
{
"additions": 0,
"deletions": 0,
"changes": 0,
},
)
file_info = {
"relative_path": filename,
"status": status_map.get(status_code, "modified"),
"additions": stats["additions"],
"deletions": stats["deletions"],
"changes": stats["changes"],
}
formatted_files.append(file_info)
# Group by top-level directory
dir_parts = filename.split("/")
top_dir = dir_parts[0] if dir_parts else "root"
if top_dir not in files_by_dir:
files_by_dir[top_dir] = []
files_by_dir[top_dir].append(file_info)
return {
"status": "success",
"total_files": len(formatted_files),
"files": formatted_files,
"files_by_directory": files_by_dir,
"compare_url": (
f"https://github.com/{repo_owner}/{repo_name}"
f"/compare/{start_tag}...{end_tag}"
),
}
except subprocess.CalledProcessError as e:
return error_response(f"Git command failed: {e.stderr}")
except (OSError, ValueError) as e:
return error_response(f"Error getting changed files: {e}")
@@ -96,7 +96,7 @@ def add_comment_to_issue(issue_number: int, comment: str) -> dict[str, any]:
comment: comment to add
Returns:
The the status of this request, with the applied comment when successful.
The status of this request, with the applied comment when successful.
"""
print(f"Attempting to add comment '{comment}' to issue #{issue_number}")
url = f"{GITHUB_BASE_URL}/repos/{OWNER}/{REPO}/issues/{issue_number}/comments"
@@ -119,7 +119,7 @@ def list_comments_on_issue(issue_number: int) -> dict[str, any]:
issue_number: issue number of the GitHub issue
Returns:
The the status of this request, with the list of comments when successful.
The status of this request, with the list of comments when successful.
"""
print(f"Attempting to list comments on issue #{issue_number}")
url = f"{GITHUB_BASE_URL}/repos/{OWNER}/{REPO}/issues/{issue_number}/comments"
@@ -166,7 +166,7 @@ def add_label_to_pr(pr_number: int, label: str) -> dict[str, Any]:
label: the label to add
Returns:
The the status of this request, with the applied label and response when
The status of this request, with the applied label and response when
successful.
"""
print(f"Attempting to add label '{label}' to PR #{pr_number}")
@@ -201,7 +201,7 @@ def add_comment_to_pr(pr_number: int, comment: str) -> dict[str, Any]:
comment: the comment to add
Returns:
The the status of this request, with the applied comment when successful.
The status of this request, with the applied comment when successful.
"""
print(f"Attempting to add comment '{comment}' to issue #{pr_number}")
@@ -230,7 +230,7 @@ def change_issue_type(issue_number: int, issue_type: str) -> dict[str, Any]:
issue_type: issue type to assign
Returns:
The the status of this request, with the applied issue type when successful.
The status of this request, with the applied issue type when successful.
"""
print(
f"Attempting to change issue type '{issue_type}' to issue #{issue_number}"
+57
View File
@@ -0,0 +1,57 @@
# Data Agent Sample
This sample agent demonstrates ADK's first-party tools for interacting with
Data Agents powered by [Conversational Analytics API](https://docs.cloud.google.com/gemini/docs/conversational-analytics-api/overview).
These tools are distributed via
the `google.adk.tools.data_agent` module and allow you to list,
inspect, and
chat with Data Agents using natural language.
These tools leverage stateful conversations, meaning you can ask follow-up
questions in the same session, and the agent will maintain context.
## Prerequisites
1. An active Google Cloud project with BigQuery and Gemini APIs enabled.
2. Google Cloud authentication configured for Application Default Credentials:
```bash
gcloud auth application-default login
```
3. At least one Data Agent created. You could create data agents via
[Conversational API](https://docs.cloud.google.com/gemini/docs/conversational-analytics-api/overview),
its
[Python SDK](https://docs.cloud.google.com/gemini/docs/conversational-analytics-api/build-agent-sdk),
or for BigQuery data
[BigQuery Studio](https://docs.cloud.google.com/bigquery/docs/create-data-agents#create_a_data_agent).
These agents are created and configured in the Google Cloud console and
point to your BigQuery tables or other data sources.
4. Follow the official
[Setup and prerequisites](https://docs.cloud.google.com/gemini/docs/conversational-analytics-api/overview#setup)
guide to enable the API and configure IAM permissions and authentication for
your data sources.
## Tools Used
* `list_accessible_data_agents`: Lists Data Agents you have permission to
access in the configured GCP project.
* `get_data_agent_info`: Retrieves details about a specific Data Agent given
its full resource name.
* `ask_data_agent`: Chats with a specific Data Agent using natural language.
## How to Run
1. Navigate to the root of the ADK repository.
2. Run the agent using the ADK CLI:
```bash
adk run --agent-path contributing/samples/data_agent
```
3. The CLI will prompt you for input. You can ask questions like the examples
below.
## Sample prompts
* "List accessible data agents."
* "Using agent
`projects/my-project/locations/global/dataAgents/sales-agent-123`, who were
my top 3 customers last quarter?"
* "How does that compare to the quarter before?"
@@ -0,0 +1,15 @@
# Copyright 2026 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
+84
View File
@@ -0,0 +1,84 @@
# Copyright 2026 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 google.adk.agents import Agent
from google.adk.auth.auth_credential import AuthCredentialTypes
from google.adk.tools.data_agent.config import DataAgentToolConfig
from google.adk.tools.data_agent.credentials import DataAgentCredentialsConfig
from google.adk.tools.data_agent.data_agent_toolset import DataAgentToolset
import google.auth
import google.auth.transport.requests
# Define the desired credential type.
# By default use Application Default Credentials (ADC) from the local
# environment, which can be set up by following
# https://cloud.google.com/docs/authentication/provide-credentials-adc.
CREDENTIALS_TYPE = None
if CREDENTIALS_TYPE == AuthCredentialTypes.OAUTH2:
# Initiaze the tools to do interactive OAuth
# The environment variables OAUTH_CLIENT_ID and OAUTH_CLIENT_SECRET
# must be set
credentials_config = DataAgentCredentialsConfig(
client_id=os.getenv("OAUTH_CLIENT_ID"),
client_secret=os.getenv("OAUTH_CLIENT_SECRET"),
)
elif CREDENTIALS_TYPE == AuthCredentialTypes.SERVICE_ACCOUNT:
# Initialize the tools to use the credentials in the service account key.
# If this flow is enabled, make sure to replace the file path with your own
# service account key file
# https://cloud.google.com/iam/docs/service-account-creds#user-managed-keys
creds, _ = google.auth.load_credentials_from_file(
"service_account_key.json",
scopes=["https://www.googleapis.com/auth/cloud-platform"],
)
creds.refresh(google.auth.transport.requests.Request())
credentials_config = DataAgentCredentialsConfig(credentials=creds)
else:
# Initialize the tools to use the application default credentials.
# https://cloud.google.com/docs/authentication/provide-credentials-adc
application_default_credentials, _ = google.auth.default()
credentials_config = DataAgentCredentialsConfig(
credentials=application_default_credentials
)
tool_config = DataAgentToolConfig(
max_query_result_rows=100,
)
da_toolset = DataAgentToolset(
credentials_config=credentials_config,
data_agent_tool_config=tool_config,
tool_filter=[
"list_accessible_data_agents",
"get_data_agent_info",
"ask_data_agent",
],
)
root_agent = Agent(
name="data_agent",
model="gemini-2.0-flash",
description="Agent to answer user questions using Data Agents.",
instruction=(
"## Persona\nYou are a helpful assistant that uses Data Agents"
" to answer user questions about their data.\n\n## Tools\n- You can"
" list available data agents using `list_accessible_data_agents`.\n-"
" You can get information about a specific data agent using"
" `get_data_agent_info`.\n- You can chat with a specific data"
" agent using `ask_data_agent`.\n"
),
tools=[da_toolset],
)
@@ -29,6 +29,11 @@ def concat_number_and_string(num: int, s: str) -> str:
return str(num) + ': ' + s
def write_document(document: str) -> dict[str, str]:
"""Write a document."""
return {'status': 'ok'}
root_agent = Agent(
model='gemini-3-pro-preview',
name='hello_world_stream_fc_args',
@@ -38,9 +43,14 @@ root_agent = Agent(
You can use the `concat_number_and_string` tool to concatenate a number and a string.
You should always call the concat_number_and_string tool to concatenate a number and a string.
You should never concatenate on your own.
You can use the `write_document` tool to write a document.
You should always call the write_document tool to write a document.
You should never write a document on your own.
""",
tools=[
concat_number_and_string,
write_document,
],
generate_content_config=types.GenerateContentConfig(
automatic_function_calling=types.AutomaticFunctionCallingConfig(
@@ -0,0 +1,15 @@
# Copyright 2026 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
@@ -0,0 +1,166 @@
# Copyright 2026 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.
"""Sample agent demonstrating MCP progress callback feature.
This sample shows how to use the progress_callback parameter in McpToolset
to receive progress notifications from MCP servers during long-running tool
executions.
There are two ways to use progress callbacks:
1. Simple callback (shared by all tools):
Pass a ProgressFnT callback that receives (progress, total, message).
2. Factory function (per-tool callbacks with runtime context):
Pass a ProgressCallbackFactory that takes (tool_name, callback_context, **kwargs)
and returns a ProgressFnT or None. This allows different tools to have different
progress handling logic, and the factory can access and modify session state
via the CallbackContext. The **kwargs ensures forward compatibility for future
parameters.
IMPORTANT: Progress callbacks only work when the MCP server actually sends
progress notifications. Most simple MCP servers (like the filesystem server)
do not send progress updates. This sample uses a mock server that demonstrates
progress reporting.
Usage:
adk run contributing/samples/mcp_progress_callback_agent
Then try:
"Run the long running task with 5 steps"
"Process these items: apple, banana, cherry"
"""
import os
import sys
from typing import Any
from google.adk.agents.callback_context import CallbackContext
from google.adk.agents.llm_agent import LlmAgent
from google.adk.tools.mcp_tool import McpToolset
from google.adk.tools.mcp_tool import StdioConnectionParams
from mcp import StdioServerParameters
from mcp.shared.session import ProgressFnT
_current_dir = os.path.dirname(os.path.abspath(__file__))
_mock_server_path = os.path.join(_current_dir, "mock_progress_server.py")
# Option 1: Simple shared callback
async def simple_progress_callback(
progress: float,
total: float | None,
message: str | None,
) -> None:
"""Handle progress notifications from MCP server.
This callback is shared by all tools in the toolset.
"""
if total is not None:
percentage = (progress / total) * 100
bar_length = 20
filled = int(bar_length * progress / total)
bar = "=" * filled + "-" * (bar_length - filled)
print(f"[{bar}] {percentage:.0f}% ({progress}/{total}) {message or ''}")
else:
print(f"Progress: {progress} {f'- {message}' if message else ''}")
# Option 2: Factory function for per-tool callbacks with runtime context
def progress_callback_factory(
tool_name: str,
*,
callback_context: CallbackContext | None = None,
**kwargs: Any,
) -> ProgressFnT | None:
"""Create a progress callback for a specific tool.
This factory allows different tools to have different progress handling.
It receives a CallbackContext for accessing and modifying runtime information
like session state. The **kwargs parameter ensures forward compatibility.
Args:
tool_name: The name of the MCP tool.
callback_context: The callback context providing access to session,
state, artifacts, and other runtime information. Allows modifying
state via ctx.state['key'] = value. May be None if not available.
**kwargs: Additional keyword arguments for future extensibility.
Returns:
A progress callback function, or None if no callback is needed.
"""
# Example: Access session info from context (if available)
session_id = "unknown"
if callback_context and callback_context.session:
session_id = callback_context.session.id
async def callback(
progress: float,
total: float | None,
message: str | None,
) -> None:
# Include tool name and session info in the progress output
prefix = f"[{tool_name}][session:{session_id}]"
if total is not None:
percentage = (progress / total) * 100
bar_length = 20
filled = int(bar_length * progress / total)
bar = "=" * filled + "-" * (bar_length - filled)
print(f"{prefix} [{bar}] {percentage:.0f}% {message or ''}")
# Example: Store progress in state (callback_context allows modification)
if callback_context:
callback_context.state["last_progress"] = progress
callback_context.state["last_total"] = total
else:
print(
f"{prefix} Progress: {progress} {f'- {message}' if message else ''}"
)
return callback
root_agent = LlmAgent(
model="gemini-2.5-flash",
name="progress_demo_agent",
instruction="""\
You are a helpful assistant that can run long-running tasks.
Available tools:
- long_running_task: Simulates a task with multiple steps. You can specify
the number of steps and delay between them.
- process_items: Processes a list of items one by one with progress updates.
When the user asks you to run a task, use these tools and the progress
will be logged automatically.
Example requests:
- "Run a long task with 5 steps"
- "Process these items: apple, banana, cherry, date"
""",
tools=[
McpToolset(
connection_params=StdioConnectionParams(
server_params=StdioServerParameters(
command=sys.executable, # Use current Python interpreter
args=[_mock_server_path],
),
timeout=60,
),
# Use factory function for per-tool callbacks (Option 2)
# Or use simple_progress_callback for shared callback (Option 1)
progress_callback=progress_callback_factory,
)
],
)
@@ -0,0 +1,161 @@
# Copyright 2026 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.
"""Mock MCP server that sends progress notifications.
This server demonstrates how MCP servers can send progress updates
during long-running tool execution.
Run this server directly:
python mock_progress_server.py
Or use it with the sample agent:
See agent_with_mock_server.py
"""
import asyncio
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import TextContent
from mcp.types import Tool
server = Server("mock-progress-server")
@server.list_tools()
async def list_tools() -> list[Tool]:
"""List available tools."""
return [
Tool(
name="long_running_task",
description=(
"A simulated long-running task that reports progress. "
"Use this to test progress callback functionality."
),
inputSchema={
"type": "object",
"properties": {
"steps": {
"type": "integer",
"description": "Number of steps to simulate (default: 5)",
"default": 5,
},
"delay": {
"type": "number",
"description": (
"Delay in seconds between steps (default: 0.5)"
),
"default": 0.5,
},
},
},
),
Tool(
name="process_items",
description="Process a list of items with progress reporting.",
inputSchema={
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {"type": "string"},
"description": "List of items to process",
},
},
"required": ["items"],
},
),
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
"""Handle tool calls with progress reporting."""
ctx = server.request_context
if name == "long_running_task":
steps = arguments.get("steps", 5)
delay = arguments.get("delay", 0.5)
# Get progress token from request metadata
progress_token = None
if ctx.meta and hasattr(ctx.meta, "progressToken"):
progress_token = ctx.meta.progressToken
for i in range(steps):
# Simulate work
await asyncio.sleep(delay)
# Send progress notification if client supports it
if progress_token is not None:
await ctx.session.send_progress_notification(
progress_token=progress_token,
progress=i + 1,
total=steps,
message=f"Completed step {i + 1} of {steps}",
)
return [
TextContent(
type="text",
text=f"Successfully completed {steps} steps!",
)
]
elif name == "process_items":
items = arguments.get("items", [])
total = len(items)
progress_token = None
if ctx.meta and hasattr(ctx.meta, "progressToken"):
progress_token = ctx.meta.progressToken
results = []
for i, item in enumerate(items):
# Simulate processing
await asyncio.sleep(0.3)
results.append(f"Processed: {item}")
# Send progress
if progress_token is not None:
await ctx.session.send_progress_notification(
progress_token=progress_token,
progress=i + 1,
total=total,
message=f"Processing item: {item}",
)
return [
TextContent(
type="text",
text="\n".join(results),
)
]
return [TextContent(type="text", text=f"Unknown tool: {name}")]
async def main():
"""Run the MCP server."""
async with stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
server.create_initialization_options(),
)
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,47 @@
# MCP Toolset OAuth Authentication Sample
This sample demonstrates the toolset authentication feature where OAuth credentials are required for both tool listing and tool calling.
## Overview
The toolset authentication flow works in two phases:
1. **Phase 1**: When the agent tries to get tools from the MCP server without credentials, the toolset signals "authentication required" and returns an auth request event.
2. **Phase 2**: After the user provides OAuth credentials, the agent can successfully list and call tools.
## Files
- `oauth_mcp_server.py` - MCP server that requires Bearer token authentication
- `agent.py` - Agent configuration with OAuth-protected MCP toolset
- `main.py` - Test script demonstrating the two-phase auth flow
## Running the Sample
1. Start the MCP server in one terminal:
```bash
PYTHONPATH=src python contributing/samples/mcp_toolset_auth/oauth_mcp_server.py
```
2. Run the test script in another terminal:
```bash
PYTHONPATH=src python contributing/samples/mcp_toolset_auth/main.py
```
## Expected Behavior
1. First invocation yields an `adk_request_credential` function call
2. The credential ID is `_adk_toolset_auth_McpToolset` to indicate toolset auth
3. After providing the access token, the agent can list and call tools
## Testing with ADK Web UI
You can also test with the ADK web UI:
```bash
adk web contributing/samples/mcp_toolset_auth
```
Note: The web UI will display the auth request and you'll need to manually provide credentials.

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