Compare commits

..

89 Commits

Author SHA1 Message Date
Yifan Wang fda34ea9ff chore: bump version number to 1.1.1
Include https://github.com/google/adk-python/commit/d6c6bb4b2489a8b7a4713e4747c30d6df0c07961 in addition to 1.1.0

PiperOrigin-RevId: 764478592
2025-05-28 17:09:01 -07:00
Selcuk Gun 609b3a572c Provide sample LongRunningFunctionTool runner script and documentation
PiperOrigin-RevId: 764475345
2025-05-28 16:59:52 -07:00
Liang Wu 2a8ca06c3e chore: remove reference to genai SDK folder.
Added `from __future__ import annotations` to follow the best practice.

PiperOrigin-RevId: 764473253
2025-05-28 16:54:19 -07:00
Google Team Member 3489bb598d Minor typo fix.
PiperOrigin-RevId: 764468987
2025-05-28 16:41:40 -07:00
Liang Wu ed63cd86eb chore: re-format import statements in mcp_session_manager.py.
They are causing Kokoro failures.

PiperOrigin-RevId: 764467630
2025-05-28 16:37:56 -07:00
Ankur Sharma 447798d573 Accounting for "references" to be absent in eval files in older format.
PiperOrigin-RevId: 764454937
2025-05-28 16:04:18 -07:00
Google Team Member 958c18db2b Add download object as json function in the download service.
PiperOrigin-RevId: 764421398
2025-05-28 14:37:47 -07:00
Google Team Member 83ba6419cd Allow GOOGLE_AI to support optional args.
PiperOrigin-RevId: 764419789
2025-05-28 14:34:11 -07:00
Omar BENHAMID d232e6216d feat: google/adk-python#479 support for streamable http MCP servers for MCPToolset
Copybara import of the project:

--
c5b9d49d7b6d858ff0a93bd690e6d653b7c32221 by Omar BENHAMID <omar.benhamid@smart-gts.com>:

feat: google/adk-python#479 support for streamable http MCP servers for MCPToolset

--
9431bc19e6538c1b814aba0b24ff564acf046075 by Omar BENHAMID <omar.benhamid@smart-gts.com>:

feat: google/adk-python#479 streamable http added to right package

--
8b4aabed45a6f0dc828beb61f12985dc7b14f3d0 by Omar BENHAMID <omar.benhamid@smart-gts.com>:

feat: google/adk-python#479 streamable http : review feedbacks + sample agent
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/650 from omarbenhamid:feature/mcp-streamable-http 625f028784c216401d45cb1b5d4d998535ebcb00
PiperOrigin-RevId: 764419586
2025-05-28 14:33:19 -07:00
Xiang (Sean) Zhou c7ce987676 chore: reformat load_memory_tool
PiperOrigin-RevId: 764402270
2025-05-28 13:52:40 -07:00
Shangjie Chen bf27f22a95 fix: Make GroundingMetadata JSON serializable. Also use the same logic to simplify content serialization.
PiperOrigin-RevId: 764401248
2025-05-28 13:50:25 -07:00
avidelatm 7fc09b2c64 chore: Adding regex for claude-4 models to anthropic_llm and updating tests
Copybara import of the project:

--
824b4379d59a375191f8ce10997efd4021d5d0b3 by Andres Videla <andres.videla@trademe.co.nz>:

Adding regex for claude-4 models to anthropic_llm and updating tests

--
8fa2a2df1931026dc803eee0e9b60e82e90c9efa by Wei Sun (Jack) <Jacksunwei@gmail.com>:

Adds trailing comma.

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/934 from avidelatm:feature/add-support-for-claude-4-models-to-anthropic_llm 8d10bacdbec952ec9832180ac6c1d220916641da
PiperOrigin-RevId: 764396694
2025-05-28 13:39:04 -07:00
Yuan Chai c4cd1802bf Copybara import of the project:
--
50b09bbe9735c889a9c815eddcee6715ebe848da by Yuan Chai <350365422@qq.com>:

fix: improve json serialization by allowing non-ascii characters
--
c66977ae6ec4edc71a2d633eb09918eb2a226461 by Yuan Chai <350365422@qq.com>:

fix: serialize function call arguments to JSON string

Previously accepted JSON objects directly, which was less robust. Now serialize arguments to JSON strings using `_safe_json_serialize`, ensuring stability and handling non-ASCII characters correctly.

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/605 from nauyiahc:fix_non_ascii_char a52513c5747296b717acee989684324e1b072d34
PiperOrigin-RevId: 764396496
2025-05-28 13:38:11 -07:00
Sanchit Rk 3cd4cd3ecf Fix: add missing kwargs to db session service
Copybara import of the project:

--
bbd21e72e46227d5bbcaef6601f4a81724e7829f by Sanchit Rk <sanchitrrk@gmail.com>:

Fix: add missing kwargs to db session service
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/946 from sanchitrk:fix/missing-kwargs-db-session-service ebb699b04d8420ad14244cf3d43a2982b01d6b7f
PiperOrigin-RevId: 764392251
2025-05-28 13:27:58 -07:00
Alankrit Verma 2a07eefc27 chore: Raises error if the last event is partial, which is an unexpected state and can happen when the model output limit is maxed out.
Copybara import of the project:

--
a6e3a220a507a27523e427e88a803f4fec40db9c by Alankrit Verma <alankrit386@gmail.com>:

test(base_llm_flow): add test for infinite loop on truncated responses

--
b5f2245788b8ed51189d1ad057372989452f070d by Alankrit Verma <alankrit386@gmail.com>:

feat(base_llm_flow): break run_async loop on partial/truncated events

--
cbbae4c468a4de3b5a737aef07cb4615f8418c38 by Wei Sun (Jack) <Jacksunwei@gmail.com>:

Raise ValueError if the last event is partial.

This is invalid state for llm flow.
--
6eebae0bc27c664eee4743ff7278ae5803415c9f by Wei Sun (Jack) <Jacksunwei@gmail.com>:

Delete tests/unittests/flows/llm_flows/test_base_llm_flow_truncation.py
--
e08b0ab19ca6eb88eb84f044bf72e815b2cf317c by Wei Sun (Jack) <Jacksunwei@gmail.com>:

format base_llm_flow.py

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/659 from AlankritVerma01:fix/522-base-llm-flow-truncation e08b0ab19ca6eb88eb84f044bf72e815b2cf317c
PiperOrigin-RevId: 764380150
2025-05-28 12:58:04 -07:00
Shangjie Chen d83ee1a6f2 fix: Remove excessive db call to update state table.
PiperOrigin-RevId: 764375522
2025-05-28 12:46:13 -07:00
shubham rajput 3611e9fe09 Copybara import of the project:
--
19b6701800bc3a26321ff3633b356106aba5a800 by shubham rajput <42935066+shu8hamrajput@users.noreply.github.com>:

fix: Update load_memory_tool.py

make `query` args required.

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/389 from shu8hamrajput:fix/make-query-required-arg 92eb6472a380aa02b287a6ca94d2bfabdaa1c38b
PiperOrigin-RevId: 764303198
2025-05-28 09:49:36 -07:00
Wei Sun (Jack) 97bf28bad6 chore: Adds --version to adk cli.
Sample:

```
-> % adk --version
adk, version 1.0.0
```

Help with https://github.com/google/adk-python/issues/832

PiperOrigin-RevId: 764297949
2025-05-28 09:37:23 -07:00
Google Team Member d6c6bb4b24 feat: add BigQuery first-party tools.
These tools support getting BigQuery dataset/table metadata and query results.

PiperOrigin-RevId: 764139132
2025-05-28 00:59:19 -07:00
Wei Sun (Jack) 46282eeb0d chore: Don't add prompt about parent agent when disallow_transfer_to_parent=True
Fixes https://github.com/google/adk-python/issues/844

PiperOrigin-RevId: 764116180
2025-05-27 23:41:30 -07:00
Wei Sun (Jack) f4c7d788b0 fix: build_graph is not awaited.
Fixes https://github.com/google/adk-python/issues/927

PiperOrigin-RevId: 764113488
2025-05-27 23:32:21 -07:00
Yifan Wang 15109c6879 fix: oauth fix for adk web
PiperOrigin-RevId: 764111115
2025-05-27 23:22:45 -07:00
Xiang (Sean) Zhou d40df2edf2 chore: fix unit tests
PiperOrigin-RevId: 764107186
2025-05-27 23:07:29 -07:00
Yifan Wang a66f12273c bump version number to 1.1.0.
PiperOrigin-RevId: 764026739
2025-05-27 18:17:04 -07:00
Yifan Wang 5d2a876ca8 feat:update minified adk web files
PiperOrigin-RevId: 763970627
2025-05-27 15:26:19 -07:00
Yifan Wang 0c4f52699b fix: serve web app at /dev-ui/
PiperOrigin-RevId: 763965859
2025-05-27 15:14:23 -07:00
Xiang (Sean) Zhou 618c824994 feat: extract agent loading logic from fast_api.py to a separate AgentLoader class and support more agent definition folder/file structure.
Structures supported:
a) agents_dir/agent_name.py (with root_agent or agent.root_agent in it)
b) agents_dir/agent_name_folder/__init__.py (with root_agent or agent.root_agent in the package)
c) agents_dir/agent_name_folder/agent.py (where agent.py has root_agent)

PiperOrigin-RevId: 763943716
2025-05-27 14:14:51 -07:00
Wei Sun (Jack) 16d9696012 fix: avoid duplicating log in stderr.
This was introduced to work around google-auth kills all logs via `google` root logging namespace. Given we're now using `google_adk` as root logging namesapce, we don't need additional Stream log handler now.

PiperOrigin-RevId: 763924531
2025-05-27 13:28:00 -07:00
Stephen Smith 03fe909ada Copybara import of the project:
--
b781880d9bfb9786bd5e50314eaedc441fc2a93e by Stephen Smith <stephen.smith@newfront.com>:

Telemetry unit test for non-serializable data.

--
179da9db997bb3f992e126c9c64193ff7df67b3d by Stephen Smith <stephen.smith@newfront.com>:

When converting the llm_request to JSON, skip non-serializable data.

--
5dc68f4f5a6d12b753fdb81d1449716d13490afb by Stephen Smith <stephen.smith@newfront.com>:

Update _create_invocation_context() return type to InvocationContext.

--
23a33f754409fcd2a7641098d68cef7e4f1c72c6 by Stephen Smith <stephen.smith@newfront.com>:

Oops, remove unnecessary import.

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/879 from stephensmithnewfront:main f71e195a9ed157e4c0b3abfa74ae078b0c1a920a
PiperOrigin-RevId: 763922003
2025-05-27 13:21:44 -07:00
Google Team Member b79400162d Use audio player component in the artifact tab
PiperOrigin-RevId: 763875097
2025-05-27 11:23:04 -07:00
Yongsul Kim c024ac5762 Align event filtering and ordering logic
Copybara import of the project:

--
d01a8fd5f079bc4fca9e4b71796dbe65312ce9ff by Leo Yongsul Kim <ystory84@gmail.com>:

fix(DatabaseSessionService): Align event filtering and ordering logic

This commit addresses inconsistencies in how DatabaseSessionService
handles config.after_timestamp and config.num_recent_events
parameters, aligning its behavior with InMemorySessionService and
VertexAiSessionService.

Key changes:
- Made after_timestamp filtering inclusive
- Corrected num_recent_events behavior to fetch the N most recent events
- Refined timezone handling for after_timestamp
- Updated the unit test test_get_session_with_config to includeSessionServiceType.DATABASE, allowing verification of these fixes.

Fixes #911

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/915 from ystory:fix/database-session-timestamp-recency 5cc8cf5f5a5c0cb3e87f6ab178a5725d3f696c88
PiperOrigin-RevId: 763874840
2025-05-27 11:22:04 -07:00
Wei Sun (Jack) 19ca61d241 fix: model_config is misplaced for EvalMetricResultPerInvocation.
PiperOrigin-RevId: 763648920
2025-05-26 23:50:05 -07:00
Xiang (Sean) Zhou 7c2df7e4f6 refactor: refactor evaluation to make cli module depends on evaluation module. Modules outside of cli module should not reference cli module
PiperOrigin-RevId: 763577749
2025-05-26 19:18:55 -07:00
Wei Sun (Jack) d43c80b718 chore: updates mypy config in pyproject.toml.
1. Use non-strict as starting point;
2. Apply python_version to 3.9.
3. Sets `follow_imports` to skip.

PiperOrigin-RevId: 763553672
2025-05-26 17:25:48 -07:00
Xiang (Sean) Zhou be0786ea88 refactor: rename agent_dir to agents_dir and rename app_id to app_name in fast_api.py to make it consistent among every endpoints
PiperOrigin-RevId: 763483339
2025-05-26 11:51:19 -07:00
Wei Sun (Jack) 6b89ceb49a fix: mutable object cannot be default value in parameter.
PiperOrigin-RevId: 763479612
2025-05-26 11:32:07 -07:00
Xiang (Sean) Zhou 694eca08e5 fix: fix bigquery credentials and bigquery tool to make it compatible with python 3.9 and make the credential serializable in session
PiperOrigin-RevId: 763332829
2025-05-26 01:58:52 -07:00
Google Team Member 55cb36edfe Add handling for None param.annotation.
This is the case for function tools that have no return value.

PiperOrigin-RevId: 763306054
2025-05-26 00:21:15 -07:00
Xiang (Sean) Zhou 74a331f1fb chore: reformat the code with autoformat.sh
PiperOrigin-RevId: 763256626
2025-05-25 20:33:45 -07:00
Xiang (Sean) Zhou 98c8a71d67 refactor: rename parameter name and add type annotation of _get_agent_to_run method
PiperOrigin-RevId: 763234780
2025-05-25 18:56:16 -07:00
Xiang (Sean) Zhou b0fb3536e5 chore: cleanup unused import in cli module
PiperOrigin-RevId: 763233551
2025-05-25 18:50:31 -07:00
Calvin Giles 1a43cf0f60 Copybara import of the project:
--
73826d258b136f92a8da8171f7dc14d5f07de8dd by Calvin Giles <calvin.giles@trademe.co.nz>:

fix: Enable InMemoryRunner to be used in async tests
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/868 from calvingiles:enable-test-runner-in-async fb9033ed6f350a3114859715cae51798f864ecf6
PiperOrigin-RevId: 763233472
2025-05-25 18:49:35 -07:00
Xiang (Sean) Zhou a380d70ac7 chore: fix cli UT
PiperOrigin-RevId: 762734348
2025-05-24 00:11:32 -07:00
Xiang (Sean) Zhou a134ba6f7a feat: add toolset cleanup logic to run_cli
PiperOrigin-RevId: 762638389
2025-05-23 18:06:30 -07:00
Yifan Wang 7067c0319d fix: adk web now can be served under custom route
PiperOrigin-RevId: 762629432
2025-05-23 17:35:44 -07:00
Xiang (Sean) Zhou 756a326033 feat: add customized bigquer tool wrapper class to facilitate developer to handcraft bigquery api tool
PiperOrigin-RevId: 762626700
2025-05-23 17:23:41 -07:00
Hangfei Lin 0e284f45ff Copybara import of the project:
--
76f579c8f75e28c79e322dc60c9dca56ac96d0fa by Hangfei Lin <hangfei@google.com>:

doc: Update README.md
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/901 from google:hangfei-patch-3 6c5e264c8134e8eb164114992f3d8f2f2efe83fc
PiperOrigin-RevId: 762603928
2025-05-23 16:10:22 -07:00
Google Team Member 8076b2294f ADK changes
PiperOrigin-RevId: 762573275
2025-05-23 14:41:27 -07:00
Ariz Chang f1a92e9674 chore: add google search agent for testing
PiperOrigin-RevId: 762571269
2025-05-23 14:36:27 -07:00
Google Team Member 717993b14f Fix eval history cases can't be toggled
PiperOrigin-RevId: 762561171
2025-05-23 14:06:35 -07:00
Shangjie Chen d212e50c10 feat:Make VertexAiSessionService true async.
PiperOrigin-RevId: 762547133
2025-05-23 13:31:38 -07:00
Google Team Member 79681e3513 Write eval results locally from adk eval cli.
PiperOrigin-RevId: 762499588
2025-05-23 11:16:00 -07:00
Google Team Member 33921d524f Add click event to the image in chat window.
PiperOrigin-RevId: 762473152
2025-05-23 10:12:15 -07:00
Xiang (Sean) Zhou 92c37496d3 refactor: simplify toolset cleanup codes and extract common cleanup codes to utils which could be utilized by cli or client codes that directly call runners
PiperOrigin-RevId: 762463028
2025-05-23 09:49:19 -07:00
Wei Sun (Jack) b9b2c3fb54 chore: Adds autoformat.sh for formatting codebase.
PiperOrigin-RevId: 762301862
2025-05-23 00:41:18 -07:00
Stephen Smith b4c4f88c22 Copybara import of the project:
--
aa92081193ccf4710e5aefb93c715791eab2c2ef by Stephen Smith <stephen.smith@newfront.com>:

docs: Fix typos in issue templates.
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/880 from stephensmithnewfront:stephensmithnewfront/address-typos-in-issue-templates-pr 65b7f85e26fca76b3e076ef0b826189353e8ab13
PiperOrigin-RevId: 762259304
2025-05-22 21:57:09 -07:00
sudu 68de9adc18 Copybara import of the project:
--
004eb36c16b042ba2d8be0cca39b739c0c8ca6c1 by sudu <teric@outlook.com>:

Update tools.yaml

When search hotels with some LLM, the date format shows as bellow
```
**Check-in Date:** April 23, 2024
```
So i update the SQL in tools.yaml to be compatible with following instruction:
```sql
update checkin date to 'April 24,2024' and checkout date to 'April 26,2024' of Best Western Bern
```
--
1300688ee1407212658d57712c1ad6ee61b052fe by qidu <qidu@outlook.com>:

Fix the tools of `ToolboxToolset` initialization bugs for
`samples/toolbox_agent`

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/794 from qidu:main 5b3199da564efdd07915e24e75659055a17dad82
PiperOrigin-RevId: 762259287
2025-05-22 21:56:18 -07:00
Sasha Sobran 0026edc1c9 chore: update filtered schema parameters for Gemini API.
PiperOrigin-RevId: 762209590
2025-05-22 18:39:58 -07:00
GenkiNoguchi d3910e7b8c Copybara import of the project:
--
3fb305691e0ecc8b75d56d0b9ef43c97cef8f773 by ammmr <m.hoshii777@gmail.com>:

fix: add missing await in runner run_live
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/849 from ammmr:patch-missing-await-in-runner-run-live b75b5d0105f18eaa06a0d7fb7d608b05eb59b3c9
PiperOrigin-RevId: 762113004
2025-05-22 13:55:00 -07:00
Google Team Member 30e10c87c5 feat:Integrate audio player component in the chat window.
PiperOrigin-RevId: 762105827
2025-05-22 13:37:37 -07:00
Wei Sun (Jack) fe618c3d93 chore: use relative import in google_api_toolsets.py. Adds open source header for base_toolset.py
PiperOrigin-RevId: 762042799
2025-05-22 11:13:57 -07:00
Wei Sun (Jack) a63ea2735c chore: Removes unused import.
PiperOrigin-RevId: 762039696
2025-05-22 11:05:45 -07:00
Xiang (Sean) Zhou ff8a3c9b43 chore: reformat the codes using autoformat.sh
PiperOrigin-RevId: 762004002
2025-05-22 09:43:54 -07:00
Xiang (Sean) Zhou a2263b1808 refactor: refactor toolset to extract tool_filter logic to base class
PiperOrigin-RevId: 761828251
2025-05-21 23:54:33 -07:00
Wei Sun (Jack) e0851a1e57 chore: batch organize imports.
PiperOrigin-RevId: 761824628
2025-05-21 23:40:47 -07:00
Liang Wu 09f30afe10 fix: failed unit test for Gemini model headers.
Root cause is that GenAI SDK updated their logic of adding headers in the latest version.

PiperOrigin-RevId: 761751413
2025-05-21 18:40:16 -07:00
Google Team Member 4542af5650 Fix ollama issues with ChatCompletionAssistantToolCall
PiperOrigin-RevId: 761732316
2025-05-21 17:27:01 -07:00
Google Team Member 505d936007 The saved filename should include the original file name instead of a generated one.
PiperOrigin-RevId: 761727488
2025-05-21 17:09:47 -07:00
Hangfei Lin d7af2b2815 Copybara import of the project:
--
cd8c580cbd4a2dc1c49c690ea81c4111860aa52c by Hangfei Lin <hangfei@google.com>:

doc: Update README.md
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/842 from google:hangfei-patch-2 aa9842a43d8c464a8999beb2f6f5d491aa63ef6c
PiperOrigin-RevId: 761722188
2025-05-21 16:54:40 -07:00
Selcuk Gun 41b33d4a0a Move public_utils to utils in tests
Renamed conflicting utils.py as testing_utils.py

PiperOrigin-RevId: 761715808
2025-05-21 16:35:11 -07:00
Xiang (Sean) Zhou 09cb128cf9 fix: fix function tool name parsing
to fix https://github.com/google/adk-python/issues/824

PiperOrigin-RevId: 761703269
2025-05-21 16:00:09 -07:00
Xiang (Sean) Zhou b2a2b11776 ADK changes
PiperOrigin-RevId: 761650284
2025-05-21 13:39:16 -07:00
Wei Sun (Jack) 1773cdab96 chore: Batch organize imports with isort.
Command: `isort /src`
PiperOrigin-RevId: 761644934
2025-05-21 13:23:32 -07:00
Google Team Member 1807e73472 ADK changes
PiperOrigin-RevId: 761640805
2025-05-21 13:13:50 -07:00
Liang Wu 98f504cebe feat!: use BuiltInCodeExecutor in Runner for CFC.
The BuiltInCodeExecutionTool is deleted.

PiperOrigin-RevId: 761608273
2025-05-21 11:45:35 -07:00
Google Team Member 9928cafe32 Refactor eval results reporting with Eval Set Results manager.
PiperOrigin-RevId: 761601525
2025-05-21 11:30:40 -07:00
Selcuk Gun c5a0437745 Provide inject_session_state as public util method
This is useful for injecting artifacts and session state variable into instruction template typically in instruction providers.

PiperOrigin-RevId: 761595473
2025-05-21 11:15:20 -07:00
Google Team Member e060344e39 ADK changes
PiperOrigin-RevId: 761572168
2025-05-21 10:18:55 -07:00
Google Team Member 1561b0f15c Support fine-tuned gemini model path to use google_search_tool
PiperOrigin-RevId: 761543344
2025-05-21 08:59:19 -07:00
Google Team Member 62a543bd58 Add dynamicAuthConfig to ExecuteCustomQuery. Add sample agent for ApplicationIntegrationToolset which uses Integration Connectors with end user credentials.
PiperOrigin-RevId: 761356343
2025-05-20 21:45:27 -07:00
Xiang (Sean) Zhou cbdb5fc507 chore: fix ut for fast api server
PiperOrigin-RevId: 761350248
2025-05-20 21:17:39 -07:00
Amulya Bhatia 98727b4698 test: unit tests for code_executor_context.py
Copybara import of the project:

--
9e51865a6dd4de4d20088e8a7ac9f3a75501aa6b by Amulya Bhatia <amulya.bhatia@t-online.de>:

test: unit tests for code_executor_context.py
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/780 from iamulya:test-code-executor-context 907b1712e43b8ce90cd8786780bef863adfcc167
PiperOrigin-RevId: 761294975
2025-05-20 17:40:33 -07:00
Xiang (Sean) Zhou 76700d790b chore: add doc string for auto generated google api toolset
PiperOrigin-RevId: 761266870
2025-05-20 16:08:27 -07:00
Google Team Member fe01103b74 ADK changes
PiperOrigin-RevId: 761251203
2025-05-20 15:23:12 -07:00
Shangjie Chen 53b14325ce fix: Use sync request method in VertexAiSessionService. The api_client has it own event loop management.
PiperOrigin-RevId: 761250268
2025-05-20 15:20:21 -07:00
Google Team Member b299241b56 ADK changes
PiperOrigin-RevId: 761197136
2025-05-20 13:01:05 -07:00
Google Team Member 68f0fca832 ADK changes
PiperOrigin-RevId: 761190844
2025-05-20 12:44:05 -07:00
Wei Sun (Jack) bb00c9519d chore: Adds link to Java ADK in README.md
PiperOrigin-RevId: 761162478
2025-05-20 11:30:45 -07:00
Selcuk Gun 00798af881 Add usage_metadata for sse response with finish_reason
This is needed for covering the generation what ends with finish reason during streaming.

PiperOrigin-RevId: 761144245
2025-05-20 10:47:08 -07:00
166 changed files with 6118 additions and 1942 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ assignees: ''
---
** Please make sure you read the contribution guide and file the issues in the rigth place. **
** 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/)
**Describe the bug**
+1 -1
View File
@@ -7,7 +7,7 @@ assignees: ''
---
** Please make sure you read the contribution guide and file the issues in the rigth place. **
** 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.**
+31
View File
@@ -1,5 +1,36 @@
# Changelog
## 1.1.1
### Features
* Add BigQuery first-party tools. See [here](https://github.com/google/adk-python/commit/d6c6bb4b2489a8b7a4713e4747c30d6df0c07961) for more details.
## 1.1.0
### Features
* Extract agent loading logic from fast_api.py to a separate AgentLoader class and support more agent definition folder/file structure.
* Added audio play in web UI.
* Added input transcription support for live/streaming.
* Added support for storing eval run history locally in adk eval cli.
* Image artifacts can now be clicked directly in chat message to view.
* Left side panel can now be resized.
### Bug Fixes
* Avoid duplicating log in stderr.
* Align event filtering and ordering logic.
* Add handling for None param.annotation.
* Fixed several minor bugs regarding eval tab in web UI.
### Miscellaneous Chores
* Updates mypy config in pyproject.toml.
* Add google search agent in samples.
* Update filtered schema parameters for Gemini API.
* Adds autoformat.sh for formatting codebase.
## 1.0.0
### âš  BREAKING CHANGES
+11 -14
View File
@@ -13,8 +13,10 @@
</h3>
<h3 align="center">
Important Links:
<a href="https://google.github.io/adk-docs/">Docs</a> &
<a href="https://github.com/google/adk-samples">Samples</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>.
</h3>
</html>
@@ -38,6 +40,12 @@ Agent Development Kit (ADK) is a flexible and modular framework for developing a
- **Deploy Anywhere**: Easily containerize and deploy agents on Cloud Run or
scale seamlessly with Vertex AI Agent Engine.
## 🤖 Agent2Agent (A2A) Protocol and ADK Integration
For remote agent-to-agent communication, ADK integrates with the
[A2A protocol](https://github.com/google/A2A/).
See this [example](https://github.com/google/A2A/tree/main/samples/python/agents/google_adk)
for how they can work together.
## 🚀 Installation
@@ -123,16 +131,9 @@ adk eval \
samples_for_testing/hello_world/hello_world_eval_set_001.evalset.json
```
## 🤖 A2A and ADK integration
For remote agent-to-agent communication, ADK integrates with the
[A2A protocol](https://github.com/google/A2A/).
See this [example](https://github.com/google/A2A/tree/main/samples/python/agents/google_adk)
for how they can work together.
## 🤝 Contributing
We welcome contributions from the community! Whether it's bug reports, feature requests, documentation improvements, or code contributions, please see our
We welcome contributions from the community! Whether it's bug reports, feature requests, documentation improvements, or code contributions, please see our
- [General contribution guideline and flow](https://google.github.io/adk-docs/contributing-guide/#questions).
- Then if you want to contribute code, please read [Code Contributing Guidelines](./CONTRIBUTING.md) to get started.
@@ -140,10 +141,6 @@ We welcome contributions from the community! Whether it's bug reports, feature r
This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENSE) file for details.
## Preview
This feature is subject to the "Pre-GA Offerings Terms" in the General Service Terms section of the [Service Specific Terms](https://cloud.google.com/terms/service-terms#1). Pre-GA features are available "as is" and might have limited support. For more information, see the [launch stage descriptions](https://cloud.google.com/products?hl=en#product-launch-stages).
---
*Happy Agent Building!*
Executable
+54
View File
@@ -0,0 +1,54 @@
#!/bin/bash
# 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.
# Autoformat ADK codebase.
if ! command -v isort &> /dev/null
then
echo "isort not found, refer to CONTRIBUTING.md to set up dev environment first."
exit
fi
if ! command -v pyink &> /dev/null
then
echo "pyink not found, refer to CONTRIBUTING.md to set up dev environment first."
exit
fi
echo '---------------------------------------'
echo '| Organizing imports for src/...'
echo '---------------------------------------'
isort src/
echo 'All done! ✨ 🍰 ✨'
echo '---------------------------------------'
echo '| Organizing imports for tests/...'
echo '---------------------------------------'
isort tests/
echo 'All done! ✨ 🍰 ✨'
echo '---------------------------------------'
echo '| Auto-formatting src/...'
echo '---------------------------------------'
find -L src/ -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 {} +
+83
View File
@@ -0,0 +1,83 @@
# BigQuery Tools Sample
## Introduction
This sample agent demonstrates the BigQuery first-party tools in ADK,
distributed via the `google.adk.tools.bigquery` module. These tools include:
1. `list_dataset_ids`
Fetches BigQuery dataset ids present in a GCP project.
1. `get_dataset_info`
Fetches metadata about a BigQuery dataset.
1. `list_table_ids`
Fetches table ids present in a BigQuery dataset.
1. `get_table_info`
Fetches metadata about a BigQuery table.
1. `execute_sql`
Runs a SQL query in BigQuery.
## How to use
Set up environment variables in your `.env` file for using
[Google AI Studio](https://google.github.io/adk-docs/get-started/quickstart/#gemini---google-ai-studio)
or
[Google Cloud Vertex AI](https://google.github.io/adk-docs/get-started/quickstart/#gemini---google-cloud-vertex-ai)
for the LLM service for your agent. For example, for using Google AI Studio you
would set:
* GOOGLE_GENAI_USE_VERTEXAI=FALSE
* GOOGLE_API_KEY={your api key}
### With Application Default Credentials
This mode is useful for quick development when the agent builder is the only
user interacting with the agent. The tools are initialized with the default
credentials present on the machine running the agent.
1. Create application default credentials on the machine where the agent would
be running by following https://cloud.google.com/docs/authentication/provide-credentials-adc.
1. Set `RUN_WITH_ADC=True` in `agent.py` and run the agent
### With Interactive OAuth
1. Follow
https://developers.google.com/identity/protocols/oauth2#1.-obtain-oauth-2.0-credentials-from-the-dynamic_data.setvar.console_name.
to get your client id and client secret. Be sure to choose "web" as your client
type.
1. Follow https://developers.google.com/workspace/guides/configure-oauth-consent to add scope "https://www.googleapis.com/auth/bigquery".
1. Follow https://developers.google.com/identity/protocols/oauth2/web-server#creatingcred to add http://localhost/dev-ui/ to "Authorized redirect URIs".
Note: localhost here is just a hostname that you use to access the dev ui,
replace it with the actual hostname you use to access the dev ui.
1. For 1st run, allow popup for localhost in Chrome.
1. Configure your `.env` file to add two more variables before running the agent:
* OAUTH_CLIENT_ID={your client id}
* OAUTH_CLIENT_SECRET={your client secret}
Note: don't create a separate .env, instead put it to the same .env file that
stores your Vertex AI or Dev ML credentials
1. Set `RUN_WITH_ADC=False` in `agent.py` and run the agent
## Sample prompts
* which weather datasets exist in bigquery public data?
* tell me more about noaa_lightning
* which tables exist in the ml_datasets dataset?
* show more details about the penguins table
* compute penguins population per island.
+15
View File
@@ -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
+58
View File
@@ -0,0 +1,58 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
from google.adk.agents import llm_agent
from google.adk.tools.bigquery import BigQueryCredentialsConfig
from google.adk.tools.bigquery import BigQueryToolset
import google.auth
RUN_WITH_ADC = False
if RUN_WITH_ADC:
# Initialize the tools to use the application default credentials.
application_default_credentials, _ = google.auth.default()
credentials_config = BigQueryCredentialsConfig(
credentials=application_default_credentials
)
else:
# Initiaze the tools to do interactive OAuth
# The environment variables OAUTH_CLIENT_ID and OAUTH_CLIENT_SECRET
# must be set
credentials_config = BigQueryCredentialsConfig(
client_id=os.getenv("OAUTH_CLIENT_ID"),
client_secret=os.getenv("OAUTH_CLIENT_SECRET"),
scopes=["https://www.googleapis.com/auth/bigquery"],
)
bigquery_toolset = BigQueryToolset(credentials_config=credentials_config)
# The variable name `root_agent` determines what your root agent is for the
# debug CLI
root_agent = llm_agent.Agent(
model="gemini-2.0-flash",
name="hello_agent",
description=(
"Agent to answer questions about BigQuery data and models and execute"
" SQL queries."
),
instruction="""\
You are a data science agent with access to several BigQuery tools.
Make use of those tools to answer the user's questions.
""",
tools=[bigquery_toolset],
)
@@ -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,30 @@
# 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 google.genai import Client
from google.adk import Agent
from google.adk.tools import google_search
# Only Vertex AI supports image generation for now.
client = Client()
root_agent = Agent(
model='gemini-2.0-flash-001',
name='root_agent',
description="""an agent whose job it is to perform Google search queries and answer questions about the results.""",
instruction="""You are an agent whose job is to perform Google search queries and answer questions about the results.
""",
tools=[google_search],
)
@@ -0,0 +1,43 @@
# Agent with Long-Running Tools
This example demonstrates an agent using a long-running tool (`ask_for_approval`).
## Key Flow for Long-Running Tools
1. **Initial Call**: The agent calls the long-running tool (e.g., `ask_for_approval`).
2. **Initial Tool Response**: The tool immediately returns an initial response, typically indicating a "pending" status and a way to track the request (e.g., a `ticket-id`). This is sent back to the agent as a `types.FunctionResponse` (usually processed internally by the runner and then influencing the agent's next turn).
3. **Agent Acknowledges**: The agent processes this initial response and usually informs the user about the pending status.
4. **External Process/Update**: The long-running task progresses externally (e.g., a human approves the request).
5. **❗️Crucial Step: Provide Updated Tool Response❗️**:
* Once the external process completes or updates, your application **must** construct a new `types.FunctionResponse`.
* This response should use the **same `id` and `name`** as the original `FunctionCall` to the long-running tool.
* The `response` field within this `types.FunctionResponse` should contain the *updated data* (e.g., `{'status': 'approved', ...}`).
* Send this `types.FunctionResponse` back to the agent as a part within a new message using `role="user"`.
```python
# Example: After external approval
updated_tool_output_data = {
"status": "approved",
"ticket-id": ticket_id, # from original call
# ... other relevant updated data
}
updated_function_response_part = types.Part(
function_response=types.FunctionResponse(
id=long_running_function_call.id, # Original call ID
name=long_running_function_call.name, # Original call name
response=updated_tool_output_data,
)
)
# Send this back to the agent
await runner.run_async(
# ... session_id, user_id ...
new_message=types.Content(
parts=[updated_function_response_part], role="user"
),
)
```
6. **Agent Acts on Update**: The agent receives this message containing the `types.FunctionResponse` and, based on its instructions, proceeds with the next steps (e.g., calling another tool like `reimburse`).
**Why is this important?** The agent relies on receiving this subsequent `types.FunctionResponse` (provided in a message with `role="user"` containing the specific `Part`) to understand that the long-running task has concluded or its state has changed. Without it, the agent will remain unaware of the outcome of the pending task.
+8 -2
View File
@@ -22,14 +22,20 @@ from google.genai import types
def reimburse(purpose: str, amount: float) -> str:
"""Reimburse the amount of money to the employee."""
return {'status': 'ok'}
return {
'status': 'ok',
}
def ask_for_approval(
purpose: str, amount: float, tool_context: ToolContext
) -> dict[str, Any]:
"""Ask for approval for the reimbursement."""
return {'status': 'pending'}
return {
'status': 'pending',
'amount': amount,
'ticketId': 'reimbursement-ticket-001',
}
root_agent = Agent(
+194
View File
@@ -0,0 +1,194 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import asyncio
import agent
from dotenv import load_dotenv
from typing import Any
from typing import Union
from google.adk.agents import Agent
from google.adk.events import Event
from google.adk.runners import Runner
from google.adk.tools import LongRunningFunctionTool
from google.adk.sessions import InMemorySessionService
from google.genai import types
import os
from opentelemetry import trace
from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
from opentelemetry.sdk.trace import export
from opentelemetry.sdk.trace import TracerProvider
load_dotenv(override=True)
APP_NAME = "human_in_the_loop"
USER_ID = "1234"
SESSION_ID = "session1234"
session_service = InMemorySessionService()
async def main():
session = await session_service.create_session(
app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID
)
runner = Runner(
agent=agent.root_agent,
app_name=APP_NAME,
session_service=session_service,
)
async def call_agent(query: str):
content = types.Content(role="user", parts=[types.Part(text=query)])
print(f'>>> User Query: "{query}"')
print("--- Running agent's initial turn ---")
events_async = runner.run_async(
session_id=session.id, user_id=USER_ID, new_message=content
)
long_running_function_call: Union[types.FunctionCall, None] = None
initial_tool_response: Union[types.FunctionResponse, None] = None
ticket_id: Union[str, None] = None
async for event in events_async:
if event.content and event.content.parts:
for i, part in enumerate(event.content.parts):
if part.text:
print(f" Part {i} [Text]: {part.text.strip()}")
if part.function_call:
print(
f" Part {i} [FunctionCall]:"
f" {part.function_call.name}({part.function_call.args}) ID:"
f" {part.function_call.id}"
)
if not long_running_function_call and part.function_call.id in (
event.long_running_tool_ids or []
):
long_running_function_call = part.function_call
print(
" (Captured as long_running_function_call for"
f" '{part.function_call.name}')"
)
if part.function_response:
print(
f" Part {i} [FunctionResponse]: For"
f" '{part.function_response.name}', ID:"
f" {part.function_response.id}, Response:"
f" {part.function_response.response}"
)
if (
long_running_function_call
and part.function_response.id == long_running_function_call.id
):
initial_tool_response = part.function_response
if initial_tool_response.response:
ticket_id = initial_tool_response.response.get("ticketId")
print(
" (Captured as initial_tool_response for"
f" '{part.function_response.name}', Ticket ID: {ticket_id})"
)
print("--- End of agent's initial turn ---\n")
if (
long_running_function_call
and initial_tool_response
and initial_tool_response.response.get("status") == "pending"
):
print(f"--- Simulating external approval for ticket: {ticket_id} ---\n")
updated_tool_output_data = {
"status": "approved",
"ticketId": ticket_id,
"approver_feedback": "Approved by manager at " + str(
asyncio.get_event_loop().time()
),
}
updated_function_response_part = types.Part(
function_response=types.FunctionResponse(
id=long_running_function_call.id,
name=long_running_function_call.name,
response=updated_tool_output_data,
)
)
print(
"--- Sending updated tool result to agent for call ID"
f" {long_running_function_call.id}: {updated_tool_output_data} ---"
)
print("--- Running agent's turn AFTER receiving updated tool result ---")
async for event in runner.run_async(
session_id=session.id,
user_id=USER_ID,
new_message=types.Content(
parts=[updated_function_response_part], role="user"
),
):
if event.content and event.content.parts:
for i, part in enumerate(event.content.parts):
if part.text:
print(f" Part {i} [Text]: {part.text.strip()}")
if part.function_call:
print(
f" Part {i} [FunctionCall]:"
f" {part.function_call.name}({part.function_call.args}) ID:"
f" {part.function_call.id}"
)
if part.function_response:
print(
f" Part {i} [FunctionResponse]: For"
f" '{part.function_response.name}', ID:"
f" {part.function_response.id}, Response:"
f" {part.function_response.response}"
)
print("--- End of agent's turn AFTER receiving updated tool result ---")
elif long_running_function_call and not initial_tool_response:
print(
f"--- Long running function '{long_running_function_call.name}' was"
" called, but its initial response was not captured. ---"
)
elif not long_running_function_call:
print(
"--- No long running function call was detected in the initial"
" turn. ---"
)
await call_agent("Please reimburse $50 for meals")
print("=" * 70)
await call_agent("Please reimburse $200 for conference travel")
if __name__ == "__main__":
provider = TracerProvider()
project_id = os.environ.get("GOOGLE_CLOUD_PROJECT")
if not project_id:
raise ValueError("GOOGLE_CLOUD_PROJECT environment variable is not set.")
print("Tracing to project", project_id)
processor = export.BatchSpanProcessor(
CloudTraceSpanExporter(project_id=project_id)
)
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)
asyncio.run(main())
provider.force_flush()
print("Done tracing to project", project_id)
@@ -0,0 +1,75 @@
# Application Integration Agent Sample with End-User Credentials
## Introduction
This sample demonstrates how to use the `ApplicationIntegrationToolset` within
an ADK agent to interact with external applications using **end-user OAuth 2.0
credentials**. Specifically, this agent (`agent.py`) is configured to interact
with Google Calendar using a pre-configured Application Integration connection
and authenticating as the end user.
## Prerequisites
1. **Set up Integration Connection:**
* You need an existing
[Integration connection](https://cloud.google.com/integration-connectors/docs/overview)
configured to interact with Google Calendar APIs. Follow the
[documentation](https://google.github.io/adk-docs/tools/google-cloud-tools/#use-integration-connectors)
to provision the Integration Connector in Google Cloud. You will need
the `Connection Name`, `Project ID`, and `Location` of your connection.
* Ensure the connection is configured to use Google Calendar (e.g., by
enabling the `google-calendar-connector` or a similar connector).
2. **Configure OAuth 2.0 Client:**
* You need an OAuth 2.0 Client ID and Client Secret that is authorized to
access the required Google Calendar scopes (e.g.,
`https://www.googleapis.com/auth/calendar.readonly`). You can create
OAuth credentials in the Google Cloud Console under "APIs & Services"
-> "Credentials".
3. **Configure Environment Variables:**
* Create a `.env` file in the same directory as `agent.py` (or add to
your existing one).
* Add the following variables to the `.env` file, replacing the
placeholder values with your actual connection details:
```dotenv
CONNECTION_NAME=<YOUR_CALENDAR_CONNECTION_NAME>
CONNECTION_PROJECT=<YOUR_GOOGLE_CLOUD_PROJECT_ID>
CONNECTION_LOCATION=<YOUR_CONNECTION_LOCATION>
CLIENT_ID=<YOUR_OAUTH_CLIENT_ID>
CLIENT_SECRET=<YOUR_OAUTH_CLIENT_SECRET>
```
## End-User Authentication (OAuth 2.0)
This agent utilizes the `AuthCredential` and `OAuth2Auth` classes from the ADK
to handle authentication.
* It defines an OAuth 2.0 scheme (`oauth2_scheme`) based on Google Cloud's
OAuth endpoints and required scopes.
* It uses the `CLIENT_ID` and `CLIENT_SECRET` from the environment variables
(or hardcoded values in the sample) to configure `OAuth2Auth`.
* This `AuthCredential` is passed to the `ApplicationIntegrationToolset`,
enabling the tool to make authenticated API calls to Google Calendar on
behalf of the user running the agent. The ADK framework will typically
handle the OAuth flow (e.g., prompting the user for consent) when the tool
is first invoked.
## How to Use
1. **Install Dependencies:** Ensure you have the necessary libraries installed
(e.g., `google-adk`, `python-dotenv`).
2. **Run the Agent:** Execute the agent script from your terminal:
```bash
python agent.py
```
3. **Interact:** Once the agent starts, you can interact with it. If it's the
first time using the tool requiring OAuth, you might be prompted to go
through the OAuth consent flow in your browser. After successful
authentication, you can ask the agent to perform tasks.
## Sample Prompts
Here are some examples of how you can interact with the agent:
* `Can you list events from my primary calendar?`
@@ -0,0 +1 @@
from . import agent
@@ -0,0 +1,95 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
from dotenv import load_dotenv
from google.adk import Agent
from google.adk.auth import AuthCredential
from google.adk.auth import AuthCredentialTypes
from google.adk.auth import OAuth2Auth
from google.adk.tools.application_integration_tool.application_integration_toolset import ApplicationIntegrationToolset
from google.adk.tools.openapi_tool.auth.auth_helpers import dict_to_auth_scheme
from google.genai import types
# Load environment variables from .env file
load_dotenv()
connection_name = os.getenv("CONNECTION_NAME")
connection_project = os.getenv("CONNECTION_PROJECT")
connection_location = os.getenv("CONNECTION_LOCATION")
client_secret = os.getenv("CLIENT_SECRET")
client_id = os.getenv("CLIENT_ID")
oauth2_data_google_cloud = {
"type": "oauth2",
"flows": {
"authorizationCode": {
"authorizationUrl": "https://accounts.google.com/o/oauth2/auth",
"tokenUrl": "https://oauth2.googleapis.com/token",
"scopes": {
"https://www.googleapis.com/auth/cloud-platform": (
"View and manage your data across Google Cloud Platform"
" services"
),
"https://www.googleapis.com/auth/calendar.readonly": (
"View your calendars"
),
},
}
},
}
oauth2_scheme = dict_to_auth_scheme(oauth2_data_google_cloud)
auth_credential = AuthCredential(
auth_type=AuthCredentialTypes.OAUTH2,
oauth2=OAuth2Auth(
client_id=client_id,
client_secret=client_secret,
),
)
calendar_tool = ApplicationIntegrationToolset(
project=connection_project,
location=connection_location,
tool_name_prefix="calendar_tool",
connection=connection_name,
actions=["GET_calendars/%7BcalendarId%7D/events"],
tool_instructions="""
Use this tool to list events in a calendar. Get calendarId from the user and use it in tool as following example:
connectorInputPayload: { "Path parameters": { "calendarId": "primary" } }. Follow the schema correctly. Note its "Path parameters" and not "Path_parameters".
""",
auth_scheme=oauth2_scheme,
auth_credential=auth_credential,
)
root_agent = Agent(
model="gemini-2.0-flash",
name="data_processing_agent",
description="Agent that can list events in a calendar.",
instruction="""
Helps you with calendar related tasks.
""",
tools=calendar_tool.get_tools(),
generate_content_config=types.GenerateContentConfig(
safety_settings=[
types.SafetySetting(
category=types.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
threshold=types.HarmBlockThreshold.OFF,
),
]
),
)
@@ -0,0 +1,8 @@
This agent connects to a local MCP server via sse.
To run this agent, start the local MCP server first by :
```bash
uv run filesystem_server.py
```
@@ -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,58 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
from google.adk.agents.llm_agent import LlmAgent
from google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPServerParams
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset
from google.adk.tools.mcp_tool.mcp_toolset import SseServerParams
_allowed_path = os.path.dirname(os.path.abspath(__file__))
root_agent = LlmAgent(
model='gemini-2.0-flash',
name='enterprise_assistant',
instruction=f"""\
Help user accessing their file systems.
Allowed directory: {_allowed_path}
""",
tools=[
MCPToolset(
connection_params=StreamableHTTPServerParams(
url='http://localhost:3000/mcp',
),
# don't want agent to do write operation
# you can also do below
# tool_filter=lambda tool, ctx=None: tool.name
# not in [
# 'write_file',
# 'edit_file',
# 'create_directory',
# 'move_file',
# ],
tool_filter=[
'read_file',
'read_multiple_files',
'list_directory',
'directory_tree',
'search_files',
'get_file_info',
'list_allowed_directories',
],
)
],
)
@@ -0,0 +1,80 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import asyncio
import os
from pathlib import Path
import sys
from mcp.server.fastmcp import FastMCP
# Create an MCP server with a name
mcp = FastMCP("Filesystem Server", host="localhost", port=3000)
# Add a tool to read file contents
@mcp.tool(description="Read contents of a file")
def read_file(filepath: str) -> str:
"""Read and return the contents of a file."""
with open(filepath, "r") as f:
return f.read()
# Add a tool to list directory contents
@mcp.tool(description="List contents of a directory")
def list_directory(dirpath: str) -> list:
"""List all files and directories in the given directory."""
return os.listdir(dirpath)
# Add a tool to get current working directory
@mcp.tool(description="Get current working directory")
def get_cwd() -> str:
"""Return the current working directory."""
return str(Path.cwd())
# Graceful shutdown handler
async def shutdown(signal, loop):
"""Cleanup tasks tied to the service's shutdown."""
print(f"\nReceived exit signal {signal.name}...")
# Get all running tasks
tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]
# Cancel all tasks
for task in tasks:
task.cancel()
print(f"Cancelling {len(tasks)} outstanding tasks")
await asyncio.gather(*tasks, return_exceptions=True)
# Stop the loop
loop.stop()
print("Shutdown complete!")
# Main entry point with graceful shutdown handling
if __name__ == "__main__":
try:
# The MCP run function ultimately uses asyncio.run() internally
mcp.run(transport="streamable-http")
except KeyboardInterrupt:
print("\nServer shutting down gracefully...")
# The asyncio event loop has already been stopped by the KeyboardInterrupt
print("Server has been shut down.")
except Exception as e:
print(f"Unexpected error: {e}")
sys.exit(1)
finally:
print("Thank you for using the Filesystem MCP Server!")

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