Compare commits

...

109 Commits

Author SHA1 Message Date
Yeesian Ng 69eb2b50c5 feat: Add --agent_engine_config_file option to adk deploy agent_engine
PiperOrigin-RevId: 800171071
2025-08-27 14:19:43 -07:00
Wei Sun (Jack) 2dd432cc1f fix: add the missing from_config class method in BaseToolset
PiperOrigin-RevId: 799893557
2025-08-26 23:48:05 -07:00
Hangfei Lin 3b997a0a07 fix: change LlmResponse to use Content for transcriptions
The transcription change breaks the multi-agent transfer during live/bidi.

Updates `GeminiLlmConnection` to populate the `content` field of `LlmResponse` with `types.Content` and `types.Part` objects for both input and output transcriptions, instead of using dedicated transcription fields. Also removes a debug print from `audio_cache_manager.py`.

the transcription is not fully ready to be used yet so roll back the transcription change.

PiperOrigin-RevId: 799851950
2025-08-26 21:12:59 -07:00
Google Team Member bcf0dda8bc fix: AgentTool returns last content, instead of the content in the last event
This fixes the issue when inner agent has after_agent_callback updating state only, AgentTool will always return empty.

PiperOrigin-RevId: 799814933
2025-08-26 19:15:23 -07:00
Wei Sun (Jack) 4c70606129 chore: Renames MCPTool and MCPToolset to McpTool and McpToolset
- Keep original class names for backward-compatibility.
- Log warning if user instantiate the classes with original names.
- New names are more aligned with MCP SDKs convention.

PiperOrigin-RevId: 799777320
2025-08-26 17:02:41 -07:00
Ankur Sharma 216cb7aec0 fix: Update routes for run_eval, get_eval_result, list_eval_results and list_metrics_info
PiperOrigin-RevId: 799718430
2025-08-26 14:27:00 -07:00
Mofi Rahman ad81aa54de fix: Fix adk deploy docker file permission
Merge https://github.com/google/adk-python/pull/2563

Currently in adk deploy cloud_run or gke, the dockerfile copies the agent code after the file permission is set. This can lead to file permission not being set correctly for the container to open and read the file.

This PR will make sure the files are copied with the permission of the user that is set in the container.

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2563 from moficodes:deploy-docker d7f6df4d893af75b360e6d96ffd2640ce6076ca2
PiperOrigin-RevId: 799371070
2025-08-25 20:03:37 -07:00
Google Team Member 47b88d2b06 feat: Add the ask_data_insights tool for natural language queries on BigQuery data
PiperOrigin-RevId: 799267061
2025-08-25 14:23:10 -07:00
Jack Wotherspoon 6806deaf88 feat: passthrough extra args for adk deploy cloud_run as Cloud Run args
Merge https://github.com/google/adk-python/pull/2544

The command `adk deploy cloud_run` supports limited `gcloud run deploy` args 😢.

Which makes the command fine for simple deployments...

It should support all current and future Cloud Run deployment args for the command to be widely adopted.

This can easily be done by passing through all extra args passed to `adk deploy cloud_run` to gcloud...

This PR assumes any extra args/flags passed after `AGENT_PATH` are gcloud flags.

## Example

```sh
# ADK flags
adk deploy cloud_run \
--project=$GOOGLE_CLOUD_PROJECT \
--region=$GOOGLE_CLOUD_LOCATION \
$AGENT_PATH \
# Use the -- separator for gcloud args
-- \
--min-instances=2 \
--no-allow-unauthenticated
```

This gives full Cloud Run feature support to ADK users 🤖 🚀

## Test Plan

To test you can just build locally or pip install feature branch directly:

```
uv venv
uv pip install git+https://github.com/jackwotherspoon/adk-python.git
```

Deploy to Cloud Run using additional arguments following `AGENT_PATH`, such as `--min-instance=2` or `--description="Cloud Run test"`:

```sh
uv run adk deploy cloud_run \
--project=$GOOGLE_CLOUD_PROJECT \
--region=$GOOGLE_CLOUD_LOCATION \
--with_ui \
$AGENT_PATH \
-- \
--labels=test-label=adk \
--min-instances=2
```

You can click on the Cloud Run service after deployment and check the service yaml, you should see the additional label etc.

<img width="1612" height="622" alt="image" src="https://github.com/user-attachments/assets/596a260a-0052-460b-9642-c18900ccf7c9" />

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

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2544 from jackwotherspoon:main 184a4d73f8dbe6f565ff92cf1c1fe69bb163de5e
PiperOrigin-RevId: 799252544
2025-08-25 13:45:21 -07:00
Wei Sun (Jack) 2b2f0b52d8 chore: Fixes a2a tests for python 3.9
A more common approach is to just skip entire module in pytest.

PiperOrigin-RevId: 799164877
2025-08-25 10:03:19 -07:00
Wei Sun (Jack) 0eb65c07d5 chore: Ignore hidden files in autoformat.sh
PiperOrigin-RevId: 798490378
2025-08-23 00:38:42 -07:00
akshaypachpute-1998 0b89f1882d chore: replaced hard coded value for user_id to the value from the tool context from parent agent. Fixes google/adk-python#2407
Merge https://github.com/google/adk-python/pull/2409

Description:

This PR Fixes: #2407

The AgentTool in /google/adk/tools/agent_tool.py uses a hardcoded user_id='tmp_user' when creating a new session for the agent it wraps. This happens within the run_async method.

code snippet
... @override async def run_async( self, *, args: dict[str, Any], tool_context: ToolContext, ) -> Any: ... session = await runner.session_service.create_session( app_name=self.agent.name, user_id='tmp_user',  # <-- This is hardcoded state=tool_context.state.to_dict(), ) ...

Why is this a problem?
This hardcoding breaks the chain of user identity. When a parent agent calls a sub-agent via the AgentTool, the original user_id is lost. Any tool or logic inside the sub-agent that needs to perform user-specific actions (e.g., accessing user data from a database, retrieving user-specific memory, checking permissions) will fail or operate on the wrong context because it receives 'tmp_user' instead of the actual user's ID.

Impact:
This prevents the creation of robust, multi-agent applications where user context must be maintained across different agents and tools. It limits the utility of AgentTool to only stateless sub-agents that do not require user-specific information.

Suggested Fix:
The user_id should be retrieved from the parent context, which is available via the tool_context parameter passed into run_async. The create_session call should be updated to use the dynamic user_id from the parent session.For example, the fix might involve accessing the user ID from the tool_context.

code-snippet
session = await runner.session_service.create_session( app_name=self.agent.name, user_id=tool_context._invocation_context.user_id, state=tool_context.state.to_dict(), )

To Reproduce
Steps to reproduce the behavior:
To reproduce this bug, we need to set up a two-agent system: a ParentAgent that calls a ChildAgent using the AgentTool. The ChildAgent will have a tool designed to simply return the user_id it receives from its context.

Expected behavior
It should return the user_id of the user calling the agent,

but, in current situation we are getting tmp_user

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2409 from akshaypachpute-1998:fix-issue-2407-agent-tool-context-propogation 0c3e8656fdf11386e3ab13a3a1f2df99a396dbd1
PiperOrigin-RevId: 798315832
2025-08-22 13:10:07 -07:00
Xiang (Sean) Zhou 124fee64e5 chore: Remove unnecessary import in runners
PiperOrigin-RevId: 798285019
2025-08-22 11:38:37 -07:00
Wei Sun (Jack) a360bc2542 docs: Clean up docs in sample
PiperOrigin-RevId: 798284758
2025-08-22 11:36:58 -07:00
Ankur Sharma 75ba9689e3 fix: Update routes for list_eval_sets, add_session_to_eval_set, get_eval, update_eval and delete_eval
PiperOrigin-RevId: 798241789
2025-08-22 09:41:59 -07:00
Wei Sun (Jack) 2c088acc9b docs: Fixes root_agent.yaml in tool_mcp_stdio_notion_config for Agent Config sample and add README.md
PiperOrigin-RevId: 798241478
2025-08-22 09:40:31 -07:00
rcsantana777 7fe60641e3 feat: Add custom User-Agent header to RestApiTool requests
Merge https://github.com/google/adk-python/pull/2641

This PR adds a custom `User-Agent` header to all requests made via `RestApiTool`. This allows backend services to identify traffic originating from the ADK. The header format is `google-adk/<version> (tool: <tool_name>)`, where `<version>` is the current version of the `google-adk` package, fetched dynamically from `google.adk.version`, and `<tool_name>` is the name of the specific `RestApiTool` instance.

**Associated Issue**

Fixes #2676

**Testing Plan**

**Unit Tests**

I ran the full suite of unit tests locally to ensure the changes did not introduce any regressions. All tests passed successfully.
```bash
$ pytest ./tests/unittests
================================= 4202 passed, 2459 warnings in 44.68s ====================
```
The warnings are related to existing experimental features and are not affected by this change.

**Manual End-to-End (E2E) Test**

I performed a manual test to ensure the integrated flow works as expected.

*   **Setup:** Created a clean virtual environment (`~/venvs/adk-e2e-test`) and installed the locally built `google-adk` package using `uv venv --seed` and `pip install dist/google_adk-*.whl`. Ran a custom `mock_server.py` script (using Flask) in one terminal to listen for requests and print headers. Ran a custom `run_test_tool.py` script in a second terminal to send a request using the modified `RestApiTool`.

*   **Execution:** The `run_test_tool.py` script successfully sent a POST request to the mock server's `/test` endpoint. The mock server received the request and printed the headers.

    **`run_test_tool.py` Output:**
    ```json
    {
      "message": "Request received successfully!"
    }
    ```

    **`mock_server.py` Output (Headers):**
    ```text
    --- Request Received ---
    Headers:
      Host: 127.0.0.1:8000
      User-Agent: google-adk/1.12.0 (tool: TestTool)
      Accept-Encoding: gzip, deflate
      Accept: */*
      Connection: keep-alive
      Content-Type: application/json
      Content-Length: 2
    ------------------------
    ```
    The `User-Agent: google-adk/1.12.0 (tool: TestTool)` header confirms the change is working as intended, dynamically fetching the package version and including the tool name.

**Checklist**

*   [x] Associated issue linked
*   [x] Unit tests passed
*   [x] End-to-end test performed and results documented
*   [x] Code formatted with `./autoformat.sh`

---

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2641 from rcsantana777:feat/add-user-agent a9a9375306c18bb7ba501276cbf76693e70a87ad
PiperOrigin-RevId: 798232385
2025-08-22 09:11:16 -07:00
Xiang (Sean) Zhou 395a50b5dc chore: add triaging instructions for mcp
PiperOrigin-RevId: 798017380
2025-08-21 20:06:51 -07:00
Hangfei Lin ddb070ff07 feat: Add support for store audio and transcription into events/sessions and artifacts
PiperOrigin-RevId: 798011660
2025-08-21 19:50:26 -07:00
Ankur Sharma f660180854 fix: Update create_eval_set API to return the created EvalSet and it route
PiperOrigin-RevId: 797974571
2025-08-21 17:13:46 -07:00
Xiang (Sean) Zhou 157f73181d feat: Allow user to specify protocol for A2A RPC URL in to_a2a utility
this fixes https://github.com/google/adk-python/issues/2405

PiperOrigin-RevId: 797958771
2025-08-21 16:26:57 -07:00
Joe Fernandez ccab076ace docs: Add What's new section to README.md
Merge https://github.com/google/adk-python/pull/2668

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2668 from joefernandez:patch-1 7bc04963755af76fe289d1855341553057348a55
PiperOrigin-RevId: 797958558
2025-08-21 16:25:30 -07:00
Wei Sun (Jack) 80d188199b chore: Fixes a few lint error for tools
PiperOrigin-RevId: 797945021
2025-08-21 15:49:21 -07:00
Google Team Member 29bb75f975 fix: updating BaseAgent.clone() and LlmAgent.clone() to properly clone fields that are lists
PiperOrigin-RevId: 797855214
2025-08-21 11:52:31 -07:00
Google Team Member 167182be01 fix: make tool description for bigquery execute_sql for various write modes self contained
So far we had a default docstring for the `execute-sql` tool and for non-default write modes we were concatenating more content to it. This was working fine in Python 3.9-3.12 but broke in Python 3.13 because of a nuanced difference in the string concatenation to the `__doc__` property of a function b/433914562#comment4. This change makes the docstring management more robust and readable.

PiperOrigin-RevId: 797843736
2025-08-21 11:26:19 -07:00
Xiang (Sean) Zhou 3f3aa7b32d fix: Set invocation_id and branch for event generated when both output_schema and tools are used
fixes https://github.com/google/adk-python/issues/2631

PiperOrigin-RevId: 797843460
2025-08-21 11:24:43 -07:00
Kacper Jawoszek 826f554789 fix: rework parallel_agent.py to always aclose async generators
See https://github.com/google/adk-python/issues/1670#issuecomment-3115891100

PiperOrigin-RevId: 797838268
2025-08-21 11:12:35 -07:00
Kacper Jawoszek 9645cee3a6 chore: add test for parallel agent to verify correct handling of exceptions
PiperOrigin-RevId: 797825924
2025-08-21 10:43:16 -07:00
Kacper Jawoszek 70f50db653 chore: add test for parallel agent to verify correct ordering of agents
PiperOrigin-RevId: 797817063
2025-08-21 10:21:37 -07:00
Google Team Member 81a53b53d6 fix: add table metadata info into Spanner tool get_table_schema and fix the key usage info
This can help to provide more context and information about the table, like parent-child relationship, and row deletion policy etc.

PiperOrigin-RevId: 797562858
2025-08-20 19:30:52 -07:00
Xiang (Sean) Zhou 52a3d6cb8a chore: Bump version number to 1.12.0
PiperOrigin-RevId: 797542097
2025-08-20 18:07:13 -07:00
Wei Sun (Jack) 331978772a fix: Fixes deps in test extra dep
PiperOrigin-RevId: 797509357
2025-08-20 16:25:08 -07:00
Wei Sun (Jack) e93f861cde chore: Removes unused to_agent_config method in BaseAgentConfig
PiperOrigin-RevId: 797502656
2025-08-20 16:08:36 -07:00
Google Team Member 54ed079100 fix: Fix Spanner DatabaseSessionService support
Introduce `DynamicPickleType` to handle session actions, using sqlalchemy-spanner `SpannerPickleType` when the database dialect is Spanner.

Connects to a Spanner database to store session data persistently in tables.
# Example using Spanner database:
`session_service = DatabaseSessionService(db_url="spanner+spanner:///projects/project-id/instances/instance-id/databases/database-id")`

# Example adk web command:
`adk web --session_service_uri="spanner+spanner:///projects/project-id/instances/instance-id/databases/database-id"`

PiperOrigin-RevId: 797416610
2025-08-20 12:26:53 -07:00
Thiago Salvatore c144b5347c fix: Add support for required params
Merge https://github.com/google/adk-python/pull/2212

This PR closes issue #2202

ADK was not parsing the required attribute when using LiteLLM, letting the LLM decide what is required vs not, not respecting function definitions.

## Test Plan

There's a fork of adk-python that is being running live for over 2 weeks in our production environment with millions of requests per day.

Below you can find a screenshot of the unit tests passing. I've also added one change to the test cases to cover this scenario

<img width="1904" height="483" alt="image" src="https://github.com/user-attachments/assets/5a6eb069-63ae-45a3-baca-6b01543f56fb" />

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2212 from thiagosalvatore:main 7de4037d8016389313f3fb22df40c12bac578523
PiperOrigin-RevId: 797393698
2025-08-20 11:31:53 -07:00
Wei Sun (Jack) 5b999ed6fd chore: Uses pydantic Field for Agent configs, so that the generated AgentConfig.json json schema can carry field description
PiperOrigin-RevId: 797094012
2025-08-19 18:03:47 -07:00
George Weale bb8ebd15f9 chore: Update openai dependency version, based on correct OPENAI release
Bump the minimum required version of the `openai` package to `>=1.100.2` for compatibility with LiteLLM.

PiperOrigin-RevId: 797027214
2025-08-19 14:43:58 -07:00
Google Team Member fef5318a22 docs: add contributing bigtable sample
PiperOrigin-RevId: 797014958
2025-08-19 14:08:55 -07:00
Xiang (Sean) Zhou 018db79d13 fix: Lazy load VertexAiCodeExecutor and ContainerCodeExecutor
VertexAiCodeExecutor and ContainerCodeExecutor request additional dependencies, lazy load them so that when users import other names, they won't get impacted if they don't had those additional dependencies installed.
 Original codes swallow the exception and log a debug log is wrong and awkward, should follow the same style as what we did in https://github.com/google/adk-python/blob/main/src/google/adk/tools/retrieval/__init__.py

PiperOrigin-RevId: 796969332
2025-08-19 12:06:59 -07:00
Google Team Member 77ed1f5f15 feat: Add setdefault method to the ADK State object
This makes the ADK state more pythonic, with the State API being more like a python dict.

PiperOrigin-RevId: 796767559
2025-08-19 01:45:33 -07:00
George Weale 4afc9b2f33 feat: Add env var to suppress experimental warnings
Checked with local wheel and worked as intended. The harness shows suppression works: 0 warnings for all true-like values.

This CL adds ADK_DISABLE_EXPERIMENTAL_WARNING to let the users to suppress warning messages from features decorated with @experimental.

Previously, using experimental features would always trigger a UserWarning. This change creates a way to disable these warnings, which can be good to stop flooding logs.

The warning is suppressed if ADK_DISABLE_EXPERIMENTAL_WARNING is set to a truthy value such as "true", "1", "yes", or "on" (case-insensitive).

Added unit tests to make sure:
Warning suppression for functions and classes when the env var is set.
Case-insensitivity and various truthy values for the env var.
Loading the env var from a .env file.

PiperOrigin-RevId: 796649404
2025-08-18 17:59:13 -07:00
Xuan Yang f8fd6a4f09 chore: add the missing license header for core_callback_config init file
PiperOrigin-RevId: 796641146
2025-08-18 17:29:09 -07:00
Google Team Member a953807cce feat: add Bigtable tools
These tools support basic operations to interact with Bigtable table metadata and query results.

PiperOrigin-RevId: 796571736
2025-08-18 14:17:08 -07:00
Google Team Member fa64545a9d fix: Fix the path for agent card in A2A demo
`AGENT_CARD_WELL_KNOWN_PATH` already contains a leading slash so is not needed in the `agent_card` URL.

PiperOrigin-RevId: 796543208
2025-08-18 13:04:38 -07:00
George Weale 004a0a0f2d fix: litellm-test due to breaking change in dep library of extension extra
PiperOrigin-RevId: 796543071
2025-08-18 13:03:04 -07:00
Google Team Member a117cf0af3 fix: Fix the path for agent card in A2A demo
`AGENT_CARD_WELL_KNOWN_PATH` already contains a leading slash so is not needed in the `agent_card` URL.

PiperOrigin-RevId: 796537920
2025-08-18 12:49:20 -07:00
Wei Sun (Jack) 1fd58cb363 chore(config): Creates yaml_utils.py in utils to allow adk dump yaml in the same style
PiperOrigin-RevId: 796531710
2025-08-18 12:34:09 -07:00
Shangjie Chen f03f167779 chore: Return explict None type for DELETE endpoints
The HTTP response code will become 204 No Content instead of 200.

PiperOrigin-RevId: 796123628
2025-08-17 09:58:40 -07:00
Liang Wu 43f302ce1a chore(config): Add _config suffix to all yaml-based agent examples
Also expand agent folder name to full spelling, e.g. ma --> multi_agent

PiperOrigin-RevId: 795926902
2025-08-16 16:10:43 -07:00
Shangjie Chen ecaa7b4c98 chore: Rename run related method and request to align with the conventions
PiperOrigin-RevId: 795660109
2025-08-15 16:48:01 -07:00
Xiang (Sean) Zhou 279e4fedd0 fix: Using base event's invocation id when merge multiple function response event
fix https://github.com/google/adk-python/issues/1531

PiperOrigin-RevId: 795657473
2025-08-15 16:38:15 -07:00
Liang Wu b3b70035c4 feat: support deploying config agent to Agent Engine in CLI
Both pure config and config with code are successfully deployed.

PiperOrigin-RevId: 795645102
2025-08-15 16:01:50 -07:00
Dan Liebling 22f34e9d2c fix: avoid crash when there is no candidates_token_count, which is Optional
PiperOrigin-RevId: 795643665
2025-08-15 15:56:05 -07:00
Liang Wu ba6e85eb3f docs(config): fix core_callback example
PiperOrigin-RevId: 795619488
2025-08-15 14:44:53 -07:00
Terrence Ryan c84350345a feat: adding build image to deploy cloud_run options (#2502)
* feat: adding build image to deploy cloud_run options

Gives the ability for a user to set the build image for the deployment step to Cloud Run.  Currently it is hard coded to python:3.11-slim, and this is still the default, but this allows that value to be overriden.

* fix: applied formatting scripts

testing:

Added tests to ensure the behavior of the cli remains consistent with when used or omitted.

* chore: next time run the formatter before you commit.

---------

Co-authored-by: Ivan Cheung <ivans.mailbox@gmail.com>
2025-08-15 11:36:44 -07:00
Shangjie Chen a2b7909fc3 fix: Fix the packaging version comparison logic in adk cli
Resolves https://github.com/google/adk-python/issues/2503

PiperOrigin-RevId: 795519711
2025-08-15 10:16:45 -07:00
Google Team Member b66054dd0d fix: Add Spanner admin scope to Spanner tool default Oauth scopes
PiperOrigin-RevId: 795519063
2025-08-15 10:15:15 -07:00
Wei Sun (Jack) 8a9a271141 fix(config): Fixes SequentialAgent.config_type type hint
Without ClassVar, it will cause pydantic to generate warning.

PiperOrigin-RevId: 795518646
2025-08-15 10:13:39 -07:00
Xiang (Sean) Zhou a2832d5ac7 feat: Support custom tool_name_prefix in auto-generated GoogleApiToolset
PiperOrigin-RevId: 795508179
2025-08-15 09:44:16 -07:00
Wei Sun (Jack) 1328e6ef62 docs(config): Adds a minimal sample to demo how to use Agent Config to create a multi-agent setup
PiperOrigin-RevId: 795501478
2025-08-15 09:22:52 -07:00
Wei Sun (Jack) cd357bf5ae fix: Fixes the host in the ansi bracket of adk web
localhost cannot always open Web UI, since we only bind 127.0.0.1.

PiperOrigin-RevId: 795484904
2025-08-15 08:26:01 -07:00
Google Team Member a27927dc81 fix: Add spanner tool name prefix
PiperOrigin-RevId: 795339631
2025-08-14 22:14:18 -07:00
Liang Wu 6c217bad82 chore: update models in samples/ folder to be gemini 2.0+
Gemini 1.5 is obsolete and not accessible to new projects.

PiperOrigin-RevId: 795330977
2025-08-14 21:36:23 -07:00
Xuan Yang c32cb6eef9 chore: remove the "one commit" requirement from the contributing guide
PiperOrigin-RevId: 795217167
2025-08-14 15:13:19 -07:00
Hangfei Lin 80052700f6 chore: Bump version to 1.11.0
release for 1.11.0

PiperOrigin-RevId: 795195869
2025-08-14 14:19:15 -07:00
Google Team Member c8059428a2 chore: add contributing spanner sample
PiperOrigin-RevId: 795154839
2025-08-14 12:41:28 -07:00
Xuan Yang e63e2a7106 fix: add the missing required tool parameters for Anthropic models
Fixes #1692

PiperOrigin-RevId: 795125801
2025-08-14 11:31:00 -07:00
Wei Sun (Jack) 9ba8eec220 feat(tracing): Adds more OpenTelemetry convention attributes: gen_ai.request.max_tokens, gen_ai.request.top_p and gen_ai.response.finish_reasons
Fixes #1234

PiperOrigin-RevId: 795082815
2025-08-14 09:52:50 -07:00
Kacper Jawoszek 9cfe43334a fix: Uncomment OTel tracing in base_llm_flow.py
It was mistakenly commented out in the previous change.

PiperOrigin-RevId: 795048873
2025-08-14 08:20:07 -07:00
Xiang (Sean) Zhou 52284b1bae fix: A2A RPC URL got overriden by host and port param of adk api server
PiperOrigin-RevId: 794708721
2025-08-13 13:20:26 -07:00
George Weale a74d3344bc chore: Added upper version bounds to dependencies in "pyproject.toml"
This change keeps dependencies to their major versions, but for the pre 1.0.0 releases pinned to minor releases as they could have breaking changes.

PyYAML: Version 7.0 is in development and could have breaking changes

absolufy-imports: The package is archived and no longer maintained.

anyio: Follows SemVer, so version 5.0 could have breaking changes.

authlib: Follows a SemVer-like pattern, so version 2.0 could have breaking changes.

click: A mature library, version 9.0 is the expected boundary for breaking changes.

fastapi: As a pre-1.0 library, the next minor release could have breaking changes.

google-api-python-client: Although in maintenance, version 3.0 could still have breaking changes.

google-cloud-aiplatform: Follows SemVer, so breaking changes are expected in version 2.0.

google-cloud-secret-manager: A stable Google library, version 3.0 could lead to breaking changes.

google-cloud-speech: A stable Google library, version 3.0 could lead to breaking changes.

google-cloud-storage: A stable Google library.

google-genai: As an official Google SDK, version 2.0 is the expected boundary for breaking changes.

graphviz: As a pre-1.0 library, the next minor release could have breaking changes.

mcp: As an SDK, version 2.0 is the boundary for potential breaking changes.

opentelemetry-api: Follows SemVer; version 2.0 could have breaking changes.

opentelemetry-exporter-gcp-trace: It's tied to the v1 OpenTelemetry API, so version 2.0 would likely be a breaking change.

opentelemetry-sdk: It's coupled to the v1 API, so version 2.0 would likely be a breaking change.

pydantic: This stops upgrades to the incompatible version 3.0 after its major 2.0 rewrite.

python-dateutil: Follows to SemVer, making version 3.0 the boundary for potential breaking changes.

python-dotenv: Locks to stable version 1.0 major release.

requests: As a more mature library, version 3.0 is boundary for breaking changes.

sqlalchemy: This locks the dependency to the stable version 2.0 API after its major rewrite.

starlette: As a pre-1.0 library, the next minor release could have breaking changes.

tenacity: This is a mature library, version 9.0 is boundary for breaking changes.

typing-extensions: Major versions are tied to large typing changes in Python itself.

tzlocal: The library has a history of introducing breaking API changes between major versions.

uvicorn: As a pre-1.0 library, the next minor release could have breaking changes.

watchdog: As a stable library, version 7.0 could have breaking changes.

websockets: Follows SemVer, so version 16.0 is the boundary for breaking changes.
PiperOrigin-RevId: 794677571
2025-08-13 12:03:47 -07:00
George Weale ddf2e2194b chore: Update python-version in .github/workflows/python-unit-tests.yml to ["3.9", "3.10", "3.11", "3.12", "3.13"]
Verified with uv on Python 3.12 and 3.13 using the same ignores as CI.
3.12:
uv python install 3.12 && uv venv --python 3.12 .venv && source .venv/bin/activate
uv sync --extra test --extra eval --extra a2a
python -m pytest tests/unittests --ignore=tests/unittests/artifacts/test_artifact_service.py --ignore=tests/unittests/tools/google_api_tool/test_googleapi_to_openapi_converter.py
3.13: repeated the above with 3.13 (separate env)
Result: All unit tests passed

PiperOrigin-RevId: 794669437
2025-08-13 11:43:24 -07:00
Kacper Jawoszek a30c63c593 fix: aclose all async generators to fix OTel tracing context
See https://github.com/google/adk-python/issues/1670#issuecomment-3115891100

PiperOrigin-RevId: 794659547
2025-08-13 11:18:26 -07:00
Wei Sun (Jack) c5af44cfc0 docs(config): Fixes tool_functions, which is a config-based sample for using tools
PiperOrigin-RevId: 794652291
2025-08-13 11:01:52 -07:00
Google Team Member c52f956433 chore: Update comment to reference "Streamable HTTP Client"
Corrects a typo in the `StreamableHTTPConnectionParams` docstring, changing "SSE" to "Streamable HTTP" to accurately reflect the referenced client.

PiperOrigin-RevId: 794424727
2025-08-12 23:45:23 -07:00
Xuan Yang 585141e0b7 fix: Use PreciseTimestamp for create and update time in database session service to improve precision
PiperOrigin-RevId: 794422113
2025-08-12 23:36:53 -07:00
Google Team Member 114db93d70 ADK changes
PiperOrigin-RevId: 794403729
2025-08-12 22:29:23 -07:00
Hangfei Lin e2518dc371 fix: Ignore AsyncGenerator return types in function declarations
For Vertex model backend, we send response back. This doesn't work for streaming tools that the return type is AsyncGenerator. So the fix here is to ignore the return type when it's AsyncGenerator.

We can't distinguish streaming vs non-streaming tool with AsyncGenerator though as LiveRequestQueue is optional in streaming tool.

Adds an `ignore_response` option to `build_function_declaration` to skip including the return type in the function declaration. This is enabled for tools that return `AsyncGenerator`, as the model does not yet support understanding these return types, while streaming tools can still handle them. Also, removes redundant return statements in `_get_mandatory_params`.

PiperOrigin-RevId: 794392846
2025-08-12 21:45:52 -07:00
Xiang (Sean) Zhou 8c65967cdc fix: Make all subclass of BaseToolset to call parent constructor
So that all member field are created / initialized in the base toolset.

PiperOrigin-RevId: 794388671
2025-08-12 21:32:24 -07:00
Xiang (Sean) Zhou ebd726f1f5 feat: Support adding prefix to tool names returned by toolset
This is to address the name conflict issue of tools returned by different toolset. Mainly it's to give each toolset a namespace.

We have a flag `add_tool_name_prefix` to decide whether to apply this behavior
We have a `tool_name_prefix` to let client specify a custom prefix, if not set , toolset name will be used as prefix.

PiperOrigin-RevId: 794306796
2025-08-12 16:17:53 -07:00
Goldy 54680edf3c fix: path parameter extraction for complex Google API endpoints
Merge https://github.com/google/adk-python/pull/1815

fix: path parameter extraction for complex Google API endpoints

- Fix GoogleApiToOpenApiConverter to handle path parameters in complex endpoints like /v1/documents/{documentId}:batchUpdate
- Use Google Discovery Document 'location' field
- Add comprehensive test suite for Google Docs batchUpdate functionality
- Verify parameter location handling for complex endpoint patterns
- Test schema validation for BatchUpdateDocumentRequest/Response

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/1815 from goldylocks87:fix-issue-1814-path-parameter-extraction af5508ec6975b1ccbc34931a0041e422ee259c16
PiperOrigin-RevId: 794301898
2025-08-12 16:02:45 -07:00
Shangjie Chen bb3735c9ca chore: Remove logging that contains full event data from DatabaseSessionService
Resolves https://github.com/google/adk-python/issues/2153

PiperOrigin-RevId: 794288546
2025-08-12 15:19:52 -07:00
Xuan Yang a09a5e67aa chore: add the missing env variables in discussion_answering.yml
PiperOrigin-RevId: 794285985
2025-08-12 15:12:03 -07:00
Google Team Member 7e0880869b feat: Expose print_detailed_results param to AgentEvaluator.evaluate
Currently `print_detailed_results` is available only in `AgentEvaluator.evaluate_eval_set` while the adk-samples data science agent uses `AgentEvaluator.evaluate` https://github.com/google/adk-samples/blob/c230c3ddc16a3f9fc7edff409b567bdd65ebd9da/python/agents/data-science/eval/test_eval.py#L32.

PiperOrigin-RevId: 794262781
2025-08-12 14:07:17 -07:00
Google Team Member 1fc8d20ae8 feat: add Spanner first-party toolset (breaking change to BigQueryTool, consolidating into generic GoogleTool)
Spanner toolset support basic operations to interact with Spanner table metadata and query results.

Consolidate BigQueryTool into generic GoogleTool, so that BigQueryToolset and SpannerToolset can share.

PiperOrigin-RevId: 794259782
2025-08-12 13:59:37 -07:00
Google Team Member 10e3dfab1a feat: remove load_dotenv() in feature_decorator.py
PiperOrigin-RevId: 794249376
2025-08-12 13:35:49 -07:00
Xuan Yang 5fba1963c3 chore: add Gemini API docs as a new datastore for the ADK Answering Agent
PiperOrigin-RevId: 794247007
2025-08-12 13:30:00 -07:00
Xuan Yang 7d2cb654f0 chore: add the missing license header for some sample agents' files
PiperOrigin-RevId: 794165986
2025-08-12 10:24:36 -07:00
Xiang (Sean) Zhou 88f759a941 fix: docstring concatenation in 3.13
The issue was caused by a breaking change in Python 3.13's inspect.cleandoc() function that made it more conservative about whitespace handling:

Root Cause:

- Python 3.12-: inspect.cleandoc() used line.lstrip() - strips all whitespace (spaces, tabs, etc.)
- Python 3.13+: inspect.cleandoc() uses line.lstrip(' ') - strips only space characters

PiperOrigin-RevId: 793921360
2025-08-11 21:26:25 -07:00
Wei Sun (Jack) e295feb4c6 docs: Add workflow_triage sample for multi-agent request orchestration
Demonstrates intelligent triage system with root planning agent and parallel
execution agents.

Use session state to store the execution plan and coordinate with other specialized agents.

Check out README.md for more details.

PiperOrigin-RevId: 793884758
2025-08-11 19:05:53 -07:00
Liang Wu d87feb8ddb docs(config): add examples for config agents
PiperOrigin-RevId: 793871778
2025-08-11 18:09:55 -07:00
Shangjie Chen 88114d7c73 chore: Add docstring to clarify the behavior of preload memory tool
PiperOrigin-RevId: 793840979
2025-08-11 16:33:49 -07:00
Xiang (Sean) Zhou d0b3b5d857 chore: add experimental messages for a2a related API
PiperOrigin-RevId: 793812544
2025-08-11 15:09:08 -07:00
Wei Sun (Jack) d674178a05 chore: Fixes generate_image sample
PiperOrigin-RevId: 793725452
2025-08-11 11:15:06 -07:00
Wei Sun (Jack) dc26aad663 docs: Adds pypi badge to README.md
PiperOrigin-RevId: 793713600
2025-08-11 10:47:57 -07:00
Shangjie Chen 7f12387eb1 chore: Make all FastAPI endpoints async
PiperOrigin-RevId: 792886431
2025-08-08 21:58:34 -07:00
Eugen-Bleck 8f937b5175 docs: update StreamableHTTPConnectionParams docstring to remove SSE references (#1456)
Co-authored-by: Terrence Ryan <tpryan@google.com>
Fixes: google/adk-docs#475
2025-08-08 20:42:43 -07:00
Shangjie Chen c323de5c69 chore: Group FastAPI endpoints with tags
PiperOrigin-RevId: 792816574
2025-08-08 17:20:46 -07:00
Liang Wu b4f1ebea31 chore(config): replace @working_in_progress with @experimental for config agent feature
PiperOrigin-RevId: 792803076
2025-08-08 16:32:50 -07:00
Google Team Member 944e39ec2a chore: allow implementations to skip defining a close method on Toolset
PiperOrigin-RevId: 792760747
2025-08-08 14:22:17 -07:00
Xiang (Sean) Zhou 9478a31bf2 fix: Lazy load retrieval tools and prompt users to install extensions if import failed
PiperOrigin-RevId: 792701550
2025-08-08 11:37:47 -07:00
Xiang (Sean) Zhou f2005a2026 chore: Add sample agent to test support of output_schema and tools at the same time for gemini model
PiperOrigin-RevId: 792694074
2025-08-08 11:16:31 -07:00
Xiang (Sean) Zhou af635674b5 feat: Support both output_schema and tools at the same time in LlmAgent
1. Allow developers to specify output schema and tools together.
2. If both are specified, do the following:
  2.1 Do not set output schema on the model config
  2.2 Add a special tool called set_model_response(result)
  2.3 `result` has the same schema as the requested output_schema
  2.4 Instruct the model to use set_model_response() to output its final result, rather than output text directly.
  2.5 When the set_model_response() is called, ADK will extract its content and put it in a text part, so the client would treat it as the model response.

PiperOrigin-RevId: 792686011
2025-08-08 10:55:22 -07:00
Xiang (Sean) Zhou b4ce3b12d1 fix: incorrect logic in LlmRequest.append_tools and make BaseTool to call it
PiperOrigin-RevId: 792642882
2025-08-08 08:53:57 -07:00
Google Team Member 98a5730115 fix: incorrect logic in LlmRequest.append_tools and make BaseTool to call it
PiperOrigin-RevId: 792518526
2025-08-08 01:29:47 -07:00
Xiang (Sean) Zhou 4b2a8e3186 fix: incorrect logic in LlmRequest.append_tools and make BaseTool to call it
PiperOrigin-RevId: 792474565
2025-08-07 22:53:54 -07:00
Sam Hatfield e4d54b66b3 fix: Creates an InMemoryMemoryService within the EvaluationGenerator
Merge https://github.com/google/adk-python/pull/2263

Addresses #2084

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2263 from sjhatfield:main 8d6fca48437f151c34b2766ea4813a777bafcabd
PiperOrigin-RevId: 792336301
2025-08-07 15:40:42 -07:00
Xuan Yang 5900273455 chore: add Github workflow config for uploading ADK docs to knowledge store
PiperOrigin-RevId: 792272706
2025-08-07 12:52:28 -07:00
Xuan Yang b5a8bad170 chore: update ADK Answering agent to reference doc site instead of adk-docs repo
PiperOrigin-RevId: 792259386
2025-08-07 12:17:09 -07:00
243 changed files with 13439 additions and 1956 deletions
+3 -1
View File
@@ -27,14 +27,16 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install google-adk
pip install google-adk google-cloud-discoveryengine
- name: Run Answering Script
env:
GITHUB_TOKEN: ${{ secrets.ADK_TRIAGE_AGENT }}
ADK_GCP_SA_KEY: ${{ secrets.ADK_GCP_SA_KEY }}
GOOGLE_CLOUD_PROJECT: ${{ secrets.GOOGLE_CLOUD_PROJECT }}
GOOGLE_CLOUD_LOCATION: ${{ secrets.GOOGLE_CLOUD_LOCATION }}
VERTEXAI_DATASTORE_ID: ${{ secrets.VERTEXAI_DATASTORE_ID }}
GEMINI_API_DATASTORE_ID: ${{ secrets.GEMINI_API_DATASTORE_ID }}
GOOGLE_GENAI_USE_VERTEXAI: 1
OWNER: 'google'
REPO: 'adk-python'
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- name: Checkout code
@@ -0,0 +1,51 @@
name: Upload ADK Docs to Vertex AI Search
on:
# Runs once per day at 16:00 UTC
schedule:
- cron: '00 16 * * *'
# Manual trigger for testing and fixing
workflow_dispatch:
jobs:
upload-adk-docs-to-vertex-ai-search:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Clone adk-docs repository
run: git clone https://github.com/google/adk-docs.git /tmp/adk-docs
- name: Clone adk-python repository
run: git clone https://github.com/google/adk-python.git /tmp/adk-python
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Authenticate to Google Cloud
id: auth
uses: 'google-github-actions/auth@v2'
with:
credentials_json: '${{ secrets.ADK_GCP_SA_KEY }}'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install google-adk markdown google-cloud-storage google-cloud-discoveryengine
- name: Run Answering Script
env:
GITHUB_TOKEN: ${{ secrets.ADK_TRIAGE_AGENT }}
GOOGLE_CLOUD_PROJECT: ${{ secrets.GOOGLE_CLOUD_PROJECT }}
GOOGLE_CLOUD_LOCATION: ${{ secrets.GOOGLE_CLOUD_LOCATION }}
VERTEXAI_DATASTORE_ID: ${{ secrets.VERTEXAI_DATASTORE_ID }}
GOOGLE_GENAI_USE_VERTEXAI: 1
GCS_BUCKET_NAME: ${{ secrets.GCS_BUCKET_NAME }}
ADK_DOCS_ROOT_PATH: /tmp/adk-docs
ADK_PYTHON_ROOT_PATH: /tmp/adk-python
PYTHONPATH: contributing/samples
run: python -m adk_answering_agent.upload_docs_to_vertex_ai_search
+98
View File
@@ -1,5 +1,103 @@
# Changelog
## 1.12.0 (2025-08-21)
### Features
**[Agent Config]** 🌟 **NEW FEATURE**: Support using config file (YAML) to author agents in addition to python code. See the [documentation](https://google.github.io/adk-docs/agents/config/) for details.
* [Agent Config] Support deploying config agent to Agent Engine in CLI ([b3b7003](https://github.com/google/adk-python/commit/b3b70035c432670a5f0b5cdd1e9467f43b80495c))
* [Tools] Add a dedicated Bigtable toolset to provide an easier, integrated way to interact
with Bigtable for building AI Agent applications(experimental feature) ([a953807](https://github.com/google/adk-python/commit/a953807cce341425ba23e3f0a85eae58d6b0630f))
* [Tools] Support custom tool_name_prefix in auto-generated GoogleApiToolset ([a2832d5](https://github.com/google/adk-python/commit/a2832d5ac7ba5264ee91f6d5a6a0058cfe4c9e8a)) See [oauth_calendar_agent](https://github.com/google/adk-python/tree/main/contributing/samples/oauth_calendar_agent) as an example.
* [CLI] Add `build_image` option for `adk deploy cloud_run` CLI ([c843503](https://github.com/google/adk-python/commit/c84350345af0ea6a232e0818b20c4262b228b103))
* [Services] Add setdefault method to the ADK State object ([77ed1f5](https://github.com/google/adk-python/commit/77ed1f5f15ed3f009547ed0e20f86d949de12ec2))
### Bug Fixes
* Lazy load VertexAiCodeExecutor and ContainerCodeExecutor ([018db79](https://github.com/google/adk-python/commit/018db79d1354f93b8328abb8416f63070b25f9f1))
* Fix the path for agent card in A2A demo ([fa64545](https://github.com/google/adk-python/commit/fa64545a9de216312a69f93126cfd37f1016c14b))
* Fix the path for agent card in A2A demo ([a117cf0](https://github.com/google/adk-python/commit/a117cf0af335c5e316ae9d61336a433052316462))
* litellm-test due to breaking change in dep library of extension extra ([004a0a0](https://github.com/google/adk-python/commit/004a0a0f2d9a4f7ae6bff42a7cad96c11a99acaf))
* Using base event's invocation id when merge multiple function response event ([279e4fe](https://github.com/google/adk-python/commit/279e4fedd0b1c0d1499c0f9a4454357af7da490e))
* Avoid crash when there is no candidates_token_count, which is Optional ([22f34e9](https://github.com/google/adk-python/commit/22f34e9d2c552fbcfa15a672ef6ff0c36fa32619))
* Fix the packaging version comparison logic in adk cli ([a2b7909](https://github.com/google/adk-python/commit/a2b7909fc36e7786a721f28e2bf75a1e86ad230d))
* Add Spanner admin scope to Spanner tool default Oauth scopes ([b66054d](https://github.com/google/adk-python/commit/b66054dd0d8c5b3d6f6ad58ac1fbd8128d1da614))
* Fixes SequentialAgent.config_type type hint ([8a9a271](https://github.com/google/adk-python/commit/8a9a271141678996c9b84b8c55d4b539d011391c))
* Fixes the host in the ansi bracket of adk web ([cd357bf](https://github.com/google/adk-python/commit/cd357bf5aeb01f1a6ae2a72349a73700ca9f1ed2))
* Add spanner tool name prefix ([a27927d](https://github.com/google/adk-python/commit/a27927dc8197c391c80acb8b2c23d610fba2f887))
### Improvements
* Support `ADK_SUPPRESS_EXPERIMENTAL_FEATURE_WARNINGS` as environment variable to suppress experimental warnings ([4afc9b2](https://github.com/google/adk-python/commit/4afc9b2f33d63381583cea328f97c02213611529))
* Uses pydantic `Field` for Agent configs, so that the generated AgentConfig.json json schema can carry field description ([5b999ed](https://github.com/google/adk-python/commit/5b999ed6fd23a0fc1da56ccff4c09621f433846b))
* Update `openai` dependency version, based on correct OPENAI release ([bb8ebd1](https://github.com/google/adk-python/commit/bb8ebd15f90768b518cd0e21a59b269e30d6d944))
* Add the missing license header for core_callback_config init file ([f8fd6a4](https://github.com/google/adk-python/commit/f8fd6a4f09ab520b8ecdbd8f9fe48228dbff7ebe))
* Creates yaml_utils.py in utils to allow adk dump yaml in the same style ([1fd58cb](https://github.com/google/adk-python/commit/1fd58cb3633992cd88fa7e09ca6eda0f9b34236f))
* Return explict None type for DELETE endpoints ([f03f167](https://github.com/google/adk-python/commit/f03f1677790c0a9e59b6ba6f46010d0b7b64be50))
* Add _config suffix to all yaml-based agent examples ([43f302c](https://github.com/google/adk-python/commit/43f302ce1ab53077ee8f1486d5294540678921e6))
* Rename run related method and request to align with the conventions ([ecaa7b4](https://github.com/google/adk-python/commit/ecaa7b4c9847b478c7cdc37185b1525f733bb403))
* Update models in samples/ folder to be gemini 2.0+ ([6c217ba](https://github.com/google/adk-python/commit/6c217bad828edf62b41ec06b168f8a6cb7ece2ed))
* Remove the "one commit" requirement from the contributing guide ([c32cb6e](https://github.com/google/adk-python/commit/c32cb6eef9ce320ea5a1f3845fc57b83762c237e))
* Bump version to 1.11.0 ([8005270](https://github.com/google/adk-python/commit/80052700f6cee947322080ae6c415d3a428b6c91))
### Documentation
* Add contributing bigtable sample ([fef5318](https://github.com/google/adk-python/commit/fef5318a22f3dcaadb7ecb858725eb61a0350140))
* Fix core_callback example ([ba6e85e](https://github.com/google/adk-python/commit/ba6e85eb3fb06f58ce9077574eac193298e18bea))
* Adds a minimal sample to demo how to use Agent Config to create a multi-agent setup ([1328e6e](https://github.com/google/adk-python/commit/1328e6ef62e9e6260048c0078579edb85a0440bc))
## [1.11.0](https://github.com/google/adk-python/compare/v1.10.0...v1.11.0) (2025-08-14)
### Features
* [Tools] Support adding prefix to tool names returned by toolset ([ebd726f](https://github.com/google/adk-python/commit/ebd726f1f5e0a76f383192cace4a80a83204325b))
* [Eval] Expose `print_detailed_results` param to `AgentEvaluator.evaluate` ([7e08808](https://github.com/google/adk-python/commit/7e0880869b340e9a5e0d68d6936219e64ab41212))
* [Tools] Add Spanner toolset (breaking change to BigQueryTool, consolidating into generic GoogleTool) ([1fc8d20](https://github.com/google/adk-python/commit/1fc8d20ae88451b7ed764aa86c17c3cdfaffa1cf))
* [Core] Support both output_schema and tools at the same time in LlmAgent([sample](https://github.com/google/adk-python/tree/main/contributing/samples/output_schema_with_tools)) ([af63567](https://github.com/google/adk-python/commit/af635674b5d3c128cf21737056e091646283aeb7))
### Bug Fixes
* A2A RPC URL got overriden by host and port param of adk api server ([52284b1](https://github.com/google/adk-python/commit/52284b1bae561e0d6c93c9d3240a09f210551b97))
* Aclose all async generators to fix OTel tracing context ([a30c63c](https://github.com/google/adk-python/commit/a30c63c5933a770b960b08a6e2f8bf13eece8a22))
* Use PreciseTimestamp for create and update time in database session service to improve precision ([585141e](https://github.com/google/adk-python/commit/585141e0b7dda20abb024c7164073862c8eea7ae))
* Ignore AsyncGenerator return types in function declarations ([e2518dc](https://github.com/google/adk-python/commit/e2518dc371fe77d7b30328d8d6f5f864176edeac))
* Make all subclass of BaseToolset to call parent constructor ([8c65967](https://github.com/google/adk-python/commit/8c65967cdc2dc79fa925ff49a2a8d67c2a248fa9))
* Path parameter extraction for complex Google API endpoints ([54680ed](https://github.com/google/adk-python/commit/54680edf3cac7477c281680ec988c0a207c0915d))
* Docstring concatenation in 3.13 ([88f759a](https://github.com/google/adk-python/commit/88f759a941c95beef0571f36f8e7a34f27971ba8))
* Lazy load retrieval tools and prompt users to install extensions if import failed ([9478a31](https://github.com/google/adk-python/commit/9478a31bf2257f0b668ae7eb91a10863e87c7bed))
* Incorrect logic in LlmRequest.append_tools and make BaseTool to call it ([b4ce3b1](https://github.com/google/adk-python/commit/b4ce3b12d109dd0386f4985fc4b27d5b93787532))
* Creates an InMemoryMemoryService within the EvaluationGenerator ([e4d54b6](https://github.com/google/adk-python/commit/e4d54b66b38ed334ca92c3bb1a83aca01b19e490))
* Uncomment OTel tracing in base_llm_flow.py ([9cfe433](https://github.com/google/adk-python/commit/9cfe43334ae50f814fed663cca7cbe330e663b8c))
### Improvements
* Added upper version bounds to dependencies in "pyproject.toml" ([a74d334](https://github.com/google/adk-python/commit/a74d3344bc19e587c5e9f55f3c90fa9d22c478d8))
* Update python-version in .github/workflows/python-unit-tests.yml to \["3.9", "3.10", "3.11", "3.12", "3.13"] ([ddf2e21](https://github.com/google/adk-python/commit/ddf2e2194b49667c8e91b4a6afde694474674250))
* Update comment to reference "Streamable HTTP Client" ([c52f956](https://github.com/google/adk-python/commit/c52f9564330f0c00d82338cc58df28cb22400b6f))
* Remove logging that contains full event data from DatabaseSessionService ([bb3735c](https://github.com/google/adk-python/commit/bb3735c9cab1baa1af2cc22981af3b3984ddfe15))
* Add the missing env variables in discussion_answering.yml ([a09a5e6](https://github.com/google/adk-python/commit/a09a5e67aa95cf71b51732ab445232dc4815d83d))
* Add Gemini API docs as a new datastore for the ADK Answering Agent ([5fba196](https://github.com/google/adk-python/commit/5fba1963c31eec512558325c480812ccb919a7bb))
* Add the missing license header for some sample agents' files ([7d2cb65](https://github.com/google/adk-python/commit/7d2cb654f0d64728741b5de733e572c44c8a5b04))
* Add docstring to clarify the behavior of preload memory tool ([88114d7](https://github.com/google/adk-python/commit/88114d7c739ca6a1b9bd19d40ed7160e53054a89))
* Add experimental messages for a2a related API ([d0b3b5d](https://github.com/google/adk-python/commit/d0b3b5d857d8105c689bd64204e367102a67eded))
* Fixes generate_image sample ([d674178](https://github.com/google/adk-python/commit/d674178a0535be3769edbf6af5a3d8cd3d47fcd2))
* Make all FastAPI endpoints async ([7f12387](https://github.com/google/adk-python/commit/7f12387eb19b9335a64b80df00609c3c765480e7))
* Group FastAPI endpoints with tags ([c323de5](https://github.com/google/adk-python/commit/c323de5c692223e55372c3797e62d4752835774d))
* Allow implementations to skip defining a close method on Toolset ([944e39e](https://github.com/google/adk-python/commit/944e39ec2a7c9ad7f20c08fd66bf544de94a23d7))
* Add sample agent to test support of output_schema and tools at the same time for gemini model ([f2005a2](https://github.com/google/adk-python/commit/f2005a20267e1ee8581cb79c37aa55dc8e18c0ea))
* Add Github workflow config for uploading ADK docs to knowledge store ([5900273](https://github.com/google/adk-python/commit/59002734559d49a46940db9822b9c5f490220a8c))
* Update ADK Answering agent to reference doc site instead of adk-docs repo ([b5a8bad](https://github.com/google/adk-python/commit/b5a8bad170e271b475385dac440c7983ed207df8))
### Documentation
* Fixes tool_functions, which is a config-based sample for using tools ([c5af44c](https://github.com/google/adk-python/commit/c5af44cfc0224e2f07ddc7a649a8561e7141fcdc))
* Add workflow_triage sample for multi-agent request orchestration ([e295feb](https://github.com/google/adk-python/commit/e295feb4c67cbe8ac4425d9ae230210840378b2e))
* Add examples for config agents ([d87feb8](https://github.com/google/adk-python/commit/d87feb8ddb6a5e402c63bd3c35625160eb94e132))
* Adds pypi badge to README.md ([dc26aad](https://github.com/google/adk-python/commit/dc26aad663b6ae72223cfec9b91eaf73a636402d))
* Update StreamableHTTPConnectionParams docstring to remove SSE references ([8f937b5](https://github.com/google/adk-python/commit/8f937b517548a1ce0569f9698ea55c0a130ef221))
## [1.10.0](https://github.com/google/adk-python/compare/v1.9.0...v1.10.0) (2025-08-07)
### Features
+1 -2
View File
@@ -49,8 +49,7 @@ This project follows
## Requirement for PRs
- Each PR should only have one commit. Please squash it if there are multiple PRs.
- All PRs, other than small documentation or typo fixes, should have a Issue assoicated. If not, please create one.
- All PRs, other than small documentation or typo fixes, should have a Issue associated. If not, please create one.
- Small, focused PRs. Keep changes minimal—one concern per PR.
- For bug fixes or features, please provide logs or screenshot after the fix is applied to help reviewers better understand the fix.
- Please include a `testing plan` section in your PR to talk about how you will test. This will save time for PR review. See `Testing Requirements` section for more details.
+7 -1
View File
@@ -1,6 +1,7 @@
# Agent Development Kit (ADK)
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
[![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)
@@ -14,7 +15,7 @@
</h3>
<h3 align="center">
Important Links:
<a href="https://google.github.io/adk-docs/">Docs</a>,
<a href="https://google.github.io/adk-docs/">Docs</a>,
<a href="https://github.com/google/adk-samples">Samples</a>,
<a href="https://github.com/google/adk-java">Java ADK</a> &
<a href="https://github.com/google/adk-web">ADK Web</a>.
@@ -26,6 +27,11 @@ Agent Development Kit (ADK) is a flexible and modular framework for developing a
---
## ✨ What's new
- **Agent Config**: Build agents without code. Check out the
[Agent Config](https://google.github.io/adk-docs/agents/config/) feature.
## ✨ Key Features
- **Rich Tool Ecosystem**: Utilize pre-built tools, custom functions,
+3 -3
View File
@@ -52,16 +52,16 @@ echo '---------------------------------------'
echo '| Auto-formatting src/...'
echo '---------------------------------------'
find -L src/ -type f -name "*.py" -exec pyink --config pyproject.toml {} +
find -L src/ -not -path "*/.*" -type f -name "*.py" -exec pyink --config pyproject.toml {} +
echo '---------------------------------------'
echo '| Auto-formatting tests/...'
echo '---------------------------------------'
find -L tests/ -type f -name "*.py" -exec pyink --config pyproject.toml {} +
find -L tests/ -not -path "*/.*" -type f -name "*.py" -exec pyink --config pyproject.toml {} +
echo '---------------------------------------'
echo '| Auto-formatting contributing/...'
echo '---------------------------------------'
find -L contributing/ -type f -name "*.py" -exec pyink --config pyproject.toml {} +
find -L contributing/ -not -path "*/.*" -type f -name "*.py" -exec pyink --config pyproject.toml {} +
+32
View File
@@ -157,12 +157,43 @@ You can extend this sample by:
- Adding audit logging for authentication events
- Implementing multi-tenant OAuth token management
## Deployment to Other Environments
When deploying the remote BigQuery A2A agent to different environments (e.g., Cloud Run, different hosts/ports), you **must** update the `url` field in the agent card JSON file:
### Local Development
```json
{
"url": "http://localhost:8001/a2a/bigquery_agent",
...
}
```
### Cloud Run Example
```json
{
"url": "https://your-bigquery-service-abc123-uc.a.run.app/a2a/bigquery_agent",
...
}
```
### Custom Host/Port Example
```json
{
"url": "https://your-domain.com:9000/a2a/bigquery_agent",
...
}
```
**Important:** The `url` field in `remote_a2a/bigquery_agent/agent.json` must point to the actual RPC endpoint where your remote BigQuery A2A agent is deployed and accessible.
## Troubleshooting
**Connection Issues:**
- Ensure the local ADK web server is running on port 8000
- Ensure the remote A2A server is running on port 8001
- Check that no firewall is blocking localhost connections
- **Verify the `url` field in `remote_a2a/bigquery_agent/agent.json` matches the actual deployed location of your remote A2A server**
- Verify the agent card URL passed to RemoteA2AAgent constructor matches the running A2A server
@@ -182,3 +213,4 @@ You can extend this sample by:
- Check the logs for both the local ADK web server and remote A2A server
- Verify OAuth tokens are properly passed between agents
- Ensure agent instructions are clear about authentication requirements
- **Double-check that the RPC URL in the agent.json file is correct and accessible**
@@ -24,6 +24,6 @@
"tags": ["authentication", "oauth", "security"]
}
],
"url": "http://localhost:8000/a2a/bigquery_agent",
"url": "http://localhost:8001/a2a/bigquery_agent",
"version": "1.0.0"
}
+32
View File
@@ -107,15 +107,47 @@ You can extend this sample by:
- Adding persistent state management
- Integrating with external APIs or databases
## Deployment to Other Environments
When deploying the remote A2A agent to different environments (e.g., Cloud Run, different hosts/ports), you **must** update the `url` field in the agent card JSON file:
### Local Development
```json
{
"url": "http://localhost:8001/a2a/check_prime_agent",
...
}
```
### Cloud Run Example
```json
{
"url": "https://your-service-abc123-uc.a.run.app/a2a/check_prime_agent",
...
}
```
### Custom Host/Port Example
```json
{
"url": "https://your-domain.com:9000/a2a/check_prime_agent",
...
}
```
**Important:** The `url` field in `remote_a2a/check_prime_agent/agent.json` must point to the actual RPC endpoint where your remote A2A agent is deployed and accessible.
## Troubleshooting
**Connection Issues:**
- Ensure the local ADK web server is running on port 8000
- Ensure the remote A2A server is running on port 8001
- Check that no firewall is blocking localhost connections
- **Verify the `url` field in `remote_a2a/check_prime_agent/agent.json` matches the actual deployed location of your remote A2A server**
- Verify the agent card URL passed to RemoteA2AAgent constructor matches the running A2A server
**Agent Not Responding:**
- Check the logs for both the local ADK web server on port 8000 and remote A2A server on port 8001
- Verify the agent instructions are clear and unambiguous
- **Double-check that the RPC URL in the agent.json file is correct and accessible**
@@ -116,18 +116,50 @@ You can extend this sample by:
- Integrating with external approval systems or databases
- Implementing approval timeouts and escalation procedures
## Deployment to Other Environments
When deploying the remote approval A2A agent to different environments (e.g., Cloud Run, different hosts/ports), you **must** update the `url` field in the agent card JSON file:
### Local Development
```json
{
"url": "http://localhost:8001/a2a/human_in_loop",
...
}
```
### Cloud Run Example
```json
{
"url": "https://your-approval-service-abc123-uc.a.run.app/a2a/human_in_loop",
...
}
```
### Custom Host/Port Example
```json
{
"url": "https://your-domain.com:9000/a2a/human_in_loop",
...
}
```
**Important:** The `url` field in `remote_a2a/human_in_loop/agent.json` must point to the actual RPC endpoint where your remote approval A2A agent is deployed and accessible.
## Troubleshooting
**Connection Issues:**
- Ensure the local ADK web server is running on port 8000
- Ensure the remote A2A server is running on port 8001
- Check that no firewall is blocking localhost connections
- **Verify the `url` field in `remote_a2a/human_in_loop/agent.json` matches the actual deployed location of your remote A2A server**
- Verify the agent card URL passed to RemoteA2AAgent constructor matches the running A2A server
**Agent Not Responding:**
- Check the logs for both the local ADK web server on port 8000 and remote A2A server on port 8001
- Verify the agent instructions are clear and unambiguous
- Ensure long-running tool responses are properly formatted with matching IDs
- **Double-check that the RPC URL in the agent.json file is correct and accessible**
**Approval Workflow Issues:**
- Verify that updated tool responses use the same `id` and `name` as the original function call
@@ -24,6 +24,6 @@
"tags": ["expenses", "processing", "employee-services"]
}
],
"url": "http://localhost:8000/a2a/human_in_loop",
"url": "http://localhost:8001/a2a/human_in_loop",
"version": "1.0.0"
}
+16 -208
View File
@@ -12,18 +12,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Any
from adk_answering_agent.gemini_assistant.agent import root_agent as gemini_assistant_agent
from adk_answering_agent.settings import BOT_RESPONSE_LABEL
from adk_answering_agent.settings import IS_INTERACTIVE
from adk_answering_agent.settings import OWNER
from adk_answering_agent.settings import REPO
from adk_answering_agent.settings import VERTEXAI_DATASTORE_ID
from adk_answering_agent.utils import error_response
from adk_answering_agent.utils import run_graphql_query
from adk_answering_agent.tools import add_comment_to_discussion
from adk_answering_agent.tools import add_label_to_discussion
from adk_answering_agent.tools import convert_gcs_links_to_https
from adk_answering_agent.tools import get_discussion_and_comments
from google.adk.agents.llm_agent import Agent
from google.adk.tools.agent_tool import AgentTool
from google.adk.tools.vertex_ai_search_tool import VertexAiSearchTool
import requests
if IS_INTERACTIVE:
APPROVAL_INSTRUCTION = (
@@ -35,197 +36,6 @@ else:
" comment."
)
def get_discussion_and_comments(discussion_number: int) -> dict[str, Any]:
"""Fetches a discussion and its comments using the GitHub GraphQL API.
Args:
discussion_number: The number of the GitHub discussion.
Returns:
A dictionary with the request status and the discussion details.
"""
print(f"Attempting to get discussion #{discussion_number} and its comments")
query = """
query($owner: String!, $repo: String!, $discussionNumber: Int!) {
repository(owner: $owner, name: $repo) {
discussion(number: $discussionNumber) {
id
title
body
createdAt
closed
author {
login
}
# For each discussion, fetch the latest 20 labels.
labels(last: 20) {
nodes {
id
name
}
}
# For each discussion, fetch the latest 100 comments.
comments(last: 100) {
nodes {
id
body
createdAt
author {
login
}
# For each discussion, fetch the latest 50 replies
replies(last: 50) {
nodes {
id
body
createdAt
author {
login
}
}
}
}
}
}
}
}
"""
variables = {
"owner": OWNER,
"repo": REPO,
"discussionNumber": discussion_number,
}
try:
response = run_graphql_query(query, variables)
if "errors" in response:
return error_response(str(response["errors"]))
discussion_data = (
response.get("data", {}).get("repository", {}).get("discussion")
)
if not discussion_data:
return error_response(f"Discussion #{discussion_number} not found.")
return {"status": "success", "discussion": discussion_data}
except requests.exceptions.RequestException as e:
return error_response(str(e))
def add_comment_to_discussion(
discussion_id: str, comment_body: str
) -> dict[str, Any]:
"""Adds a comment to a specific discussion.
Args:
discussion_id: The GraphQL node ID of the discussion.
comment_body: The content of the comment in Markdown.
Returns:
The status of the request and the new comment's details.
"""
print(f"Adding comment to discussion {discussion_id}")
query = """
mutation($discussionId: ID!, $body: String!) {
addDiscussionComment(input: {discussionId: $discussionId, body: $body}) {
comment {
id
body
createdAt
author {
login
}
}
}
}
"""
comment_body = (
"**Response from ADK Answering Agent (experimental, answer may be"
" inaccurate)**\n\n"
+ comment_body
)
variables = {"discussionId": discussion_id, "body": comment_body}
try:
response = run_graphql_query(query, variables)
if "errors" in response:
return error_response(str(response["errors"]))
new_comment = (
response.get("data", {}).get("addDiscussionComment", {}).get("comment")
)
return {"status": "success", "comment": new_comment}
except requests.exceptions.RequestException as e:
return error_response(str(e))
def get_label_id(label_name: str) -> str | None:
"""Helper function to find the GraphQL node ID for a given label name."""
print(f"Finding ID for label '{label_name}'...")
query = """
query($owner: String!, $repo: String!, $labelName: String!) {
repository(owner: $owner, name: $repo) {
label(name: $labelName) {
id
}
}
}
"""
variables = {"owner": OWNER, "repo": REPO, "labelName": label_name}
try:
response = run_graphql_query(query, variables)
if "errors" in response:
print(
f"[Warning] Error from GitHub API response for label '{label_name}':"
f" {response['errors']}"
)
return None
label_info = response["data"].get("repository", {}).get("label")
if label_info:
return label_info.get("id")
print(f"[Warning] Label information for '{label_name}' not found.")
return None
except requests.exceptions.RequestException as e:
print(f"[Warning] Error from GitHub API: {e}")
return None
def add_label_to_discussion(
discussion_id: str, label_name: str
) -> dict[str, Any]:
"""Adds a label to a specific discussion.
Args:
discussion_id: The GraphQL node ID of the discussion.
label_name: The name of the label to add (e.g., "bug").
Returns:
The status of the request and the label details.
"""
print(
f"Attempting to add label '{label_name}' to discussion {discussion_id}..."
)
# First, get the GraphQL ID of the label by its name
label_id = get_label_id(label_name)
if not label_id:
return error_response(f"Label '{label_name}' not found.")
# Then, perform the mutation to add the label to the discussion
mutation = """
mutation AddLabel($discussionId: ID!, $labelId: ID!) {
addLabelsToLabelable(input: {labelableId: $discussionId, labelIds: [$labelId]}) {
clientMutationId
}
}
"""
variables = {"discussionId": discussion_id, "labelId": label_id}
try:
response = run_graphql_query(mutation, variables)
if "errors" in response:
return error_response(str(response["errors"]))
return {"status": "success", "label_id": label_id, "label_name": label_name}
except requests.exceptions.RequestException as e:
return error_response(str(e))
root_agent = Agent(
model="gemini-2.5-pro",
name="adk_answering_agent",
@@ -244,10 +54,10 @@ root_agent = Agent(
* The latest comment is not from you or other agents (marked as "Response from XXX Agent").
* The latest comment is asking a question or requesting information.
4. Use the `VertexAiSearchTool` to find relevant information before answering.
* If you need infromation about Gemini API, ask the `gemini_assistant` agent to provide the information and references.
* You can call the `gemini_assistant` agent with multiple queries to find all the relevant information.
5. If you can find relevant information, use the `add_comment_to_discussion` tool to add a comment to the discussion.
6. If you post a commment and the discussion does not have a label named {BOT_RESPONSE_LABEL},
add the label {BOT_RESPONSE_LABEL} to the discussion using the `add_label_to_discussion` tool.
6. If you post a comment, add the label {BOT_RESPONSE_LABEL} to the discussion using the `add_label_to_discussion` tool.
IMPORTANT:
* {APPROVAL_INSTRUCTION}
@@ -255,25 +65,23 @@ root_agent = Agent(
information that is not in the document store. Do not invent citations which are not in the document store.
* **Be Objective**: your answer should be based on the facts you found in the document store, do not be misled by user's assumptions or user's understanding of ADK.
* If you can't find the answer or information in the document store, **do not** respond.
* Inlclude a short summary of your response in the comment as a TLDR, e.g. "**TLDR**: <your summary>".
* Start with a short summary of your response in the comment as a TLDR, e.g. "**TLDR**: <your summary>".
* Have a divider line between the TLDR and your detail response.
* Do not respond to any other discussion except the one specified by the user.
* Please include your justification for your decision in your output
to the user who is telling with you.
* If you uses citation from the document store, please provide a footnote
referencing the source document format it as: "[1] URL of the document".
* Replace the "gs://prefix/" part, e.g. "gs://adk-qa-bucket/", to be "https://github.com/google/"
* Add "blob/main/" after the repo name, e.g. "adk-python", "adk-docs", for example:
* If the original URL is "gs://adk-qa-bucket/adk-python/src/google/adk/version.py",
then the citation URL is "https://github.com/google/adk-python/blob/main/src/google/adk/version.py",
* If the original URL is "gs://adk-qa-bucket/adk-docs/docs/index.md",
then the citation URL is "https://github.com/google/adk-docs/blob/main/docs/index.md"
* If the file is a html file, replace the ".html" to be ".md"
referencing the source document format it as: "[1] publicly accessible HTTPS URL of the document".
* You **should always** use the `convert_gcs_links_to_https` tool to convert GCS links (e.g. "gs://...") to HTTPS links.
* **Do not** use the `convert_gcs_links_to_https` tool for non-GCS links.
* Make sure the citation URL is valid. Otherwise do not list this specific citation.
""",
tools=[
VertexAiSearchTool(data_store_id=VERTEXAI_DATASTORE_ID),
AgentTool(gemini_assistant_agent),
get_discussion_and_comments,
add_comment_to_discussion,
add_label_to_discussion,
convert_gcs_links_to_https,
],
)
@@ -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
@@ -0,0 +1,94 @@
# 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 json
from typing import Any
from typing import Dict
from typing import List
from adk_answering_agent.settings import ADK_GCP_SA_KEY
from adk_answering_agent.settings import GEMINI_API_DATASTORE_ID
from adk_answering_agent.utils import error_response
from google.adk.agents.llm_agent import Agent
from google.api_core.exceptions import GoogleAPICallError
from google.cloud import discoveryengine_v1beta as discoveryengine
from google.oauth2 import service_account
def search_gemini_api_docs(queries: List[str]) -> Dict[str, Any]:
"""Searches Gemini API docs using Vertex AI Search.
Args:
queries: The list of queries to search.
Returns:
A dictionary containing the status of the request and the list of search
results, which contains the title, url and snippets.
"""
try:
adk_gcp_sa_key_info = json.loads(ADK_GCP_SA_KEY)
client = discoveryengine.SearchServiceClient(
credentials=service_account.Credentials.from_service_account_info(
adk_gcp_sa_key_info
)
)
except (TypeError, ValueError) as e:
return error_response(f"Error creating Vertex AI Search client: {e}")
serving_config = f"{GEMINI_API_DATASTORE_ID}/servingConfigs/default_config"
results = []
try:
for query in queries:
request = discoveryengine.SearchRequest(
serving_config=serving_config,
query=query,
page_size=20,
)
response = client.search(request=request)
for item in response.results:
snippets = []
for snippet in item.document.derived_struct_data.get("snippets", []):
snippets.append(snippet.get("snippet"))
results.append({
"title": item.document.derived_struct_data.get("title"),
"url": item.document.derived_struct_data.get("link"),
"snippets": snippets,
})
except GoogleAPICallError as e:
return error_response(f"Error from Vertex AI Search: {e}")
return {"status": "success", "results": results}
root_agent = Agent(
model="gemini-2.5-pro",
name="gemini_assistant",
description="Answer questions about Gemini API.",
instruction="""
You are a helpful assistant that responds to questions about Gemini API based on information
found in the document store. You can access the document store using the `search_gemini_api_docs` tool.
When user asks a question, here are the steps:
1. Use the `search_gemini_api_docs` tool to find relevant information before answering.
* You can call the tool with multiple queries to find all the relevant information.
2. Provide a response based on the information you found in the document store. Reference the source document in the response.
IMPORTANT:
* Your response should be based on the information you found in the document store. Do not invent
information that is not in the document store. Do not invent citations which are not in the document store.
* If you can't find the answer or information in the document store, just respond with "I can't find the answer or information in the document store".
* If you uses citation from the document store, please always provide a footnote referencing the source document format it as: "[1] URL of the document".
""",
tools=[search_gemini_api_docs],
)
@@ -13,6 +13,7 @@
# limitations under the License.
import asyncio
import logging
import time
from adk_answering_agent import agent
@@ -21,11 +22,14 @@ from adk_answering_agent.settings import OWNER
from adk_answering_agent.settings import REPO
from adk_answering_agent.utils import call_agent_async
from adk_answering_agent.utils import parse_number_string
from google.adk.cli.utils import logs
from google.adk.runners import InMemoryRunner
APP_NAME = "adk_answering_app"
USER_ID = "adk_answering_user"
logs.setup_adk_logger(level=logging.DEBUG)
async def main():
runner = InMemoryRunner(
@@ -31,6 +31,9 @@ if not VERTEXAI_DATASTORE_ID:
GOOGLE_CLOUD_PROJECT = os.getenv("GOOGLE_CLOUD_PROJECT")
GCS_BUCKET_NAME = os.getenv("GCS_BUCKET_NAME")
GEMINI_API_DATASTORE_ID = os.getenv("GEMINI_API_DATASTORE_ID")
ADK_GCP_SA_KEY = os.getenv("ADK_GCP_SA_KEY")
ADK_DOCS_ROOT_PATH = os.getenv("ADK_DOCS_ROOT_PATH")
ADK_PYTHON_ROOT_PATH = os.getenv("ADK_PYTHON_ROOT_PATH")
@@ -0,0 +1,230 @@
# 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 typing import Any
from typing import Dict
from typing import Optional
from adk_answering_agent.settings import OWNER
from adk_answering_agent.settings import REPO
from adk_answering_agent.utils import convert_gcs_to_https
from adk_answering_agent.utils import error_response
from adk_answering_agent.utils import run_graphql_query
import requests
def get_discussion_and_comments(discussion_number: int) -> dict[str, Any]:
"""Fetches a discussion and its comments using the GitHub GraphQL API.
Args:
discussion_number: The number of the GitHub discussion.
Returns:
A dictionary with the request status and the discussion details.
"""
print(f"Attempting to get discussion #{discussion_number} and its comments")
query = """
query($owner: String!, $repo: String!, $discussionNumber: Int!) {
repository(owner: $owner, name: $repo) {
discussion(number: $discussionNumber) {
id
title
body
createdAt
closed
author {
login
}
# For each discussion, fetch the latest 20 labels.
labels(last: 20) {
nodes {
id
name
}
}
# For each discussion, fetch the latest 100 comments.
comments(last: 100) {
nodes {
id
body
createdAt
author {
login
}
# For each discussion, fetch the latest 50 replies
replies(last: 50) {
nodes {
id
body
createdAt
author {
login
}
}
}
}
}
}
}
}
"""
variables = {
"owner": OWNER,
"repo": REPO,
"discussionNumber": discussion_number,
}
try:
response = run_graphql_query(query, variables)
if "errors" in response:
return error_response(str(response["errors"]))
discussion_data = (
response.get("data", {}).get("repository", {}).get("discussion")
)
if not discussion_data:
return error_response(f"Discussion #{discussion_number} not found.")
return {"status": "success", "discussion": discussion_data}
except requests.exceptions.RequestException as e:
return error_response(str(e))
def add_comment_to_discussion(
discussion_id: str, comment_body: str
) -> dict[str, Any]:
"""Adds a comment to a specific discussion.
Args:
discussion_id: The GraphQL node ID of the discussion.
comment_body: The content of the comment in Markdown.
Returns:
The status of the request and the new comment's details.
"""
print(f"Adding comment to discussion {discussion_id}")
query = """
mutation($discussionId: ID!, $body: String!) {
addDiscussionComment(input: {discussionId: $discussionId, body: $body}) {
comment {
id
body
createdAt
author {
login
}
}
}
}
"""
if not comment_body.startswith("**Response from ADK Answering Agent"):
comment_body = (
"**Response from ADK Answering Agent (experimental, answer may be"
" inaccurate)**\n\n"
+ comment_body
)
variables = {"discussionId": discussion_id, "body": comment_body}
try:
response = run_graphql_query(query, variables)
if "errors" in response:
return error_response(str(response["errors"]))
new_comment = (
response.get("data", {}).get("addDiscussionComment", {}).get("comment")
)
return {"status": "success", "comment": new_comment}
except requests.exceptions.RequestException as e:
return error_response(str(e))
def get_label_id(label_name: str) -> str | None:
"""Helper function to find the GraphQL node ID for a given label name."""
print(f"Finding ID for label '{label_name}'...")
query = """
query($owner: String!, $repo: String!, $labelName: String!) {
repository(owner: $owner, name: $repo) {
label(name: $labelName) {
id
}
}
}
"""
variables = {"owner": OWNER, "repo": REPO, "labelName": label_name}
try:
response = run_graphql_query(query, variables)
if "errors" in response:
print(
f"[Warning] Error from GitHub API response for label '{label_name}':"
f" {response['errors']}"
)
return None
label_info = response["data"].get("repository", {}).get("label")
if label_info:
return label_info.get("id")
print(f"[Warning] Label information for '{label_name}' not found.")
return None
except requests.exceptions.RequestException as e:
print(f"[Warning] Error from GitHub API: {e}")
return None
def add_label_to_discussion(
discussion_id: str, label_name: str
) -> dict[str, Any]:
"""Adds a label to a specific discussion.
Args:
discussion_id: The GraphQL node ID of the discussion.
label_name: The name of the label to add (e.g., "bug").
Returns:
The status of the request and the label details.
"""
print(
f"Attempting to add label '{label_name}' to discussion {discussion_id}..."
)
# First, get the GraphQL ID of the label by its name
label_id = get_label_id(label_name)
if not label_id:
return error_response(f"Label '{label_name}' not found.")
# Then, perform the mutation to add the label to the discussion
mutation = """
mutation AddLabel($discussionId: ID!, $labelId: ID!) {
addLabelsToLabelable(input: {labelableId: $discussionId, labelIds: [$labelId]}) {
clientMutationId
}
}
"""
variables = {"discussionId": discussion_id, "labelId": label_id}
try:
response = run_graphql_query(mutation, variables)
if "errors" in response:
return error_response(str(response["errors"]))
return {"status": "success", "label_id": label_id, "label_name": label_name}
except requests.exceptions.RequestException as e:
return error_response(str(e))
def convert_gcs_links_to_https(gcs_uris: list[str]) -> Dict[str, Optional[str]]:
"""Converts GCS files link into publicly accessible HTTPS links.
Args:
gcs_uris: A list of GCS files links, in the format
'gs://bucket_name/prefix/relative_path'.
Returns:
A dictionary mapping the original GCS files links to the converted HTTPS
links. If a GCS link is invalid, the corresponding value in the dictionary
will be None.
"""
return {gcs_uri: convert_gcs_to_https(gcs_uri) for gcs_uri in gcs_uris}
@@ -12,8 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import sys
from typing import Any
from typing import Optional
from urllib.parse import urljoin
from adk_answering_agent.settings import GITHUB_GRAPHQL_URL
from adk_answering_agent.settings import GITHUB_TOKEN
@@ -58,6 +61,92 @@ def parse_number_string(number_str: str | None, default_value: int = 0) -> int:
return default_value
def _check_url_exists(url: str) -> bool:
"""Checks if a URL exists and is accessible."""
try:
# Set a timeout to prevent the program from waiting indefinitely.
# allow_redirects=True ensures we correctly handle valid links
# after redirection.
response = requests.head(url, timeout=5, allow_redirects=True)
# Status codes 2xx (Success) or 3xx (Redirection) are considered valid.
return response.ok
except requests.RequestException:
# Catch all possible exceptions from the requests library
# (e.g., connection errors, timeouts).
return False
def _generate_github_url(repo_name: str, relative_path: str) -> str:
"""Generates a standard GitHub URL for a repo file."""
return f"https://github.com/google/{repo_name}/blob/main/{relative_path}"
def convert_gcs_to_https(gcs_uri: str) -> Optional[str]:
"""Converts a GCS file link into a publicly accessible HTTPS link.
Args:
gcs_uri: The Google Cloud Storage link, in the format
'gs://bucket_name/prefix/relative_path'.
Returns:
The converted HTTPS link as a string, or None if the input format is
incorrect.
"""
# Parse the GCS link
if not gcs_uri or not gcs_uri.startswith("gs://"):
print(f"Error: Invalid GCS link format: {gcs_uri}")
return None
try:
# Strip 'gs://' and split by '/', requiring at least 3 parts
# (bucket, prefix, path)
parts = gcs_uri[5:].split("/", 2)
if len(parts) < 3:
raise ValueError(
"GCS link must contain a bucket, prefix, and relative_path."
)
_, prefix, relative_path = parts
except (ValueError, IndexError) as e:
print(f"Error: Failed to parse GCS link '{gcs_uri}': {e}")
return None
# Replace .html with .md
if relative_path.endswith(".html"):
relative_path = relative_path.removesuffix(".html") + ".md"
# Convert the links for adk-docs
if prefix == "adk-docs" and relative_path.startswith("docs/"):
path_after_docs = relative_path[len("docs/") :]
if not path_after_docs.endswith(".md"):
# Use the regular github url
return _generate_github_url(prefix, relative_path)
base_url = "https://google.github.io/adk-docs/"
if os.path.basename(path_after_docs) == "index.md":
# Use the directory path if it is a index file
final_path_segment = os.path.dirname(path_after_docs)
else:
# Otherwise, use the file name without extention
final_path_segment = path_after_docs.removesuffix(".md")
if final_path_segment and not final_path_segment.endswith("/"):
final_path_segment += "/"
potential_url = urljoin(base_url, final_path_segment)
# Check if the generated link exists
if _check_url_exists(potential_url):
return potential_url
else:
# If it doesn't exist, fallback to the regular github url
return _generate_github_url(prefix, relative_path)
# Convert the links for other cases, e.g. adk-python
else:
return _generate_github_url(prefix, relative_path)
async def call_agent_async(
runner: Runner, user_id: str, session_id: str, prompt: str
) -> str:
@@ -32,6 +32,7 @@ LABEL_TO_OWNER = {
"documentation": "polong-lin",
"services": "DeanChensj",
"tools": "seanzhou1023",
"mcp": "seanzhou1023",
"eval": "ankursharmas",
"live": "hangfei",
"models": "genquan9",
@@ -267,6 +268,7 @@ root_agent = Agent(
- If it's about model support(non-Gemini, like Litellm, Ollama, OpenAI models), label it with "models".
- If it's about tracing, label it with "tracing".
- If it's agent orchestration, agent definition, label it with "core".
- If it's about Model Context Protocol (e.g. MCP tool, MCP toolset, MCP session management etc.), label it with "mcp".
- If you can't find a appropriate labels for the PR, follow the previous instruction that starts with "IMPORTANT:".
Here is the contribution guidelines:

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