Compare commits

...

99 Commits

Author SHA1 Message Date
Shangjie Chen 20c30d5819 chore: Bump version number to 1.10.0
PiperOrigin-RevId: 792206610
2025-08-07 10:05:01 -07:00
Yeesian Ng 83e5df7862 fix: Set the agent_framework when initializing module-based agent engine
PiperOrigin-RevId: 792170095
2025-08-07 08:27:15 -07:00
Google Team Member 25b2806301 fix: accommodate for open api schema that do not have any 'properties'
PiperOrigin-RevId: 792028582
2025-08-07 00:30:50 -07:00
Xuan Yang f6a022cda3 chore: only run ADK Answering Agent automatically when Q&A discussion is created
PiperOrigin-RevId: 792025646
2025-08-07 00:19:30 -07:00
Liang Wu e0a8355219 chore(config): add the public URL of JSON schema file to template
PiperOrigin-RevId: 791983175
2025-08-06 21:50:20 -07:00
Liang Wu d9ce2e691c feat(config): implement config and from_config for MCPToolset
The connection_params argument in the constructor is split into four arguments in the config class because some of them have identical fields. In order to identify which is which, a separate name is more convenient.

PiperOrigin-RevId: 791965995
2025-08-06 20:45:58 -07:00
Liang Wu dc193f7969 fix(config): fix adk create --type=config
Previously click didn't convert the input into the enum type.

PiperOrigin-RevId: 791922529
2025-08-06 18:10:21 -07:00
Xuan Yang 6277dae749 chore: add a disclaimer for the response from the answering agent
PiperOrigin-RevId: 791915465
2025-08-06 17:49:08 -07:00
Liang Wu ef837015f3 refactor(config): move BaseToolConfig to a separate file
PiperOrigin-RevId: 791841562
2025-08-06 14:18:20 -07:00
Google Team Member 54cc849de7 feat: Add metadata field to ADK BaseTool
PiperOrigin-RevId: 791790030
2025-08-06 12:11:42 -07:00
Liang Wu e73d71d324 feat(config): implement config and from_config for ExampleTool
Only list[Example] is supported in config. BaseExampleProvider will need to be used in code.

PiperOrigin-RevId: 791763913
2025-08-06 11:05:54 -07:00
Xiang (Sean) Zhou e528749a1c fix: lazy import VertexAiRagRetrieval
original codes try to eagerly import VertexAiRagRetrieval while it doesn't want to raise error if client try to import other names in this package and dependencies of VertexAiRagRetrieval is missing. so it swallow the import error which doesn't make sense, given vertex sdk is a must have for VertexAiRagRetrieval, we should fail fast.

this fix achieve the same purpose but fail fast if client try to import  VertexAiRagRetrieval from this package and miss certain dependencies (e.g. vertex sdk)

PiperOrigin-RevId: 791759776
2025-08-06 10:54:57 -07:00
Liang Wu 1686cc57c2 feat(config): implement configs and from_config() for CrewaiTool and LangchainTool
PiperOrigin-RevId: 791742964
2025-08-06 10:19:12 -07:00
Wei Sun (Jack) 53803522b6 refactor(config): Makes BaseAgent.from_config a final method and let sub-class to optionally override _parse_config to update kwargs if needed
This ensures that the pydantic hooks (e.g. model_validators) are triggered correctly.

PiperOrigin-RevId: 791545704
2025-08-05 23:53:25 -07:00
Liang Wu e3c2bf3062 chore: remove unused Example-related classes
PiperOrigin-RevId: 791538058
2025-08-05 23:26:06 -07:00
Liang Wu 2fff882fb0 feat(config): implement from_config() for BaseTool
PiperOrigin-RevId: 791520708
2025-08-05 22:26:23 -07:00
Xuan Yang a3b31ca950 chore: add the missing name for the ADK Answering Agent workflow
PiperOrigin-RevId: 791413949
2025-08-05 16:24:24 -07:00
Xuan Yang 8dc0c949af chore: add Github workflow config for the ADK Answering agent
PiperOrigin-RevId: 791407331
2025-08-05 16:07:09 -07:00
Hangfei Lin 71fbc9275b feat: Implement Live Session Resumption
Previous implementation doesn't pass the actual handle to server. Now we cache the handle and pass it over when reconnection happens.

To enable:
    run_config = RunConfig(
        session_resumption=types.SessionResumptionConfig(transparent=True)
    )

PiperOrigin-RevId: 791308462
2025-08-05 11:50:54 -07:00
Xiang (Sean) Zhou 423542a43f fix: shared default plugin manager and cost manager instances among multiple invocations
PiperOrigin-RevId: 791303349
2025-08-05 11:38:52 -07:00
Xiang (Sean) Zhou 37dae9b631 chore: Import AGENT_CARD_WELL_KNOWN_PATH from adk instead of from a2a directly
thus let adk handle import problem for a2a, e.g. python version need to be > 3.10 etc.

PiperOrigin-RevId: 791273137
2025-08-05 10:28:37 -07:00
Liang Wu 0e28d64712 feat(tools): create enterprise_web_search_tool as a tool instance
There is no argument for the tool, so just like google_search, we should make it an easy-to-use tool instance.

PiperOrigin-RevId: 791266806
2025-08-05 10:11:37 -07:00
Xiang (Sean) Zhou 6da6c2a44c fix: using async lock for accessing shared object in parallel executions and update tests for testing various type of functions
1. given we are running parallel functions in one event loop (one thread) , we should use async lock instead of thread lock
2. test three kind of functions:
  a. sync function
  b. async function that doesn't yield
  c. async function that yield

PiperOrigin-RevId: 791255012
2025-08-05 09:45:04 -07:00
Wei Sun (Jack) 8ef2177658 test: Fixes adk cli options and method parameters mismatching and adds a unit test for future proof checking
The test will fail if `@option` list and method parameter don't match.

Future proof test for #2328

PiperOrigin-RevId: 791022512
2025-08-04 21:08:57 -07:00
Google Team Member 97318bcd19 fix: correct type annotation
Overridden `supported_models` should be a `classmethod` rather than a `staticmethod`.

PiperOrigin-RevId: 790989895
2025-08-04 19:25:52 -07:00
Xuan Yang 283303032a chore: update the prompt to make the ADK Answering Agent more objective
PiperOrigin-RevId: 790882938
2025-08-04 13:59:59 -07:00
nikkie e369c283b3 fix: typo againt (in adk run --replay help)
Merge https://github.com/google/adk-python/pull/2327

`adk run --help` (adk 1.9.0)

```
  --replay FILE      The json file that contains the initial state of the
                     session and user queries. A new session will be created
                     using this state. And user queries are run againt the
                     newly created session. Users cannot continue to interact
                     with the agent.
```

```
$ git grep againt
src/google/adk/cli/cli_tools_click.py:        " queries are run againt the newly created session. Users cannot"
```

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2327 from ftnext:fix-typo-run-replay-help 77cae65a235d9119810fe3d209910562672713c8
PiperOrigin-RevId: 790872246
2025-08-04 13:32:41 -07:00
Xuan Yang 74589a1db7 chore: make LlmRequest.LiveConnectConfig field default to a factory to avoid sharing a mutable instance
PiperOrigin-RevId: 790854215
2025-08-04 12:43:34 -07:00
Wei Sun (Jack) e41dbccf7f fix(cli): Fixes adk deploy cloud_run cli
Fixes #2328

PiperOrigin-RevId: 790775592
2025-08-04 09:16:26 -07:00
Carol Zheng d620bcb384 fix: Remove thoughts from contents in llm requests
Merge https://github.com/google/adk-python/pull/2320

Fix #843

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2320 from CAROLZXYZXY:cazheng/fix-843 5b4a4b256928cb766a44a3e18d4300b7ee5f779f
PiperOrigin-RevId: 790592793
2025-08-03 22:48:25 -07:00
Xiang (Sean) Zhou 90b9193a20 chore: Add sample agent for testing parallel functions execution
PiperOrigin-RevId: 790208057
2025-08-02 14:53:03 -07:00
Xiang (Sean) Zhou 57cd41f424 feat: Support parallel execution of parallel function calls
PiperOrigin-RevId: 790182046
2025-08-02 12:28:19 -07:00
Divyansh Shukla 7556ebc76a feat: Allow max tokens to be customizable in Claude
PiperOrigin-RevId: 789901925
2025-08-01 14:43:57 -07:00
Wei Sun (Jack) 2bb20411f4 feat(config): Adds BaseAgent.config_type field to indicate the config for the current agent and removes if-else branches against LlmAgent/LoopAgent/... in config_agent_utils::from_config
This makes the logic work with any user-defined agent with user-defined XxxAgentConfig.

PiperOrigin-RevId: 789845354
2025-08-01 12:02:07 -07:00
Xiang (Sean) Zhou 86a44873e9 fix: Annotate response type as None for transfer_to_agent tool and set empty Schema as response schema when tool has no response annotation
1. if a function has no return type annotation, we should treat it as returning any type
2. we use empty schema (with `type` as None) to indicate no type constraints and this is already supported by model server

PiperOrigin-RevId: 789808104
2025-08-01 10:22:28 -07:00
Xiang (Sean) Zhou faadef167e fix: incompatible a2a sdk changes
a. camel case to snake case
b. A2ACardResolver moved to different module

PiperOrigin-RevId: 789807686
2025-08-01 10:20:37 -07:00
Google Team Member bead607364 chore: Hide the ask_data_insights tool until the API is publicly available
PiperOrigin-RevId: 789806535
2025-08-01 10:17:13 -07:00
Xuan Yang 041f04e89c chore: change LlmRequest.config's default value to be types.GenerateContentConfig() instead of None
PiperOrigin-RevId: 789792582
2025-08-01 09:36:20 -07:00
Google Team Member 16a15c8709 docs: fix typos
PiperOrigin-RevId: 789660536
2025-08-01 01:31:41 -07:00
Liang Wu 9656ccc407 feat(config): add GenerateContentConfig to LlmAgentConfig
PiperOrigin-RevId: 789631181
2025-07-31 23:33:36 -07:00
Liang Wu db975dfe2a chore: prevent triggering of _load_from_yaml_config in AgentLoader
PiperOrigin-RevId: 789502695
2025-07-31 15:59:41 -07:00
Wei Sun (Jack) 0ccbf6f2f8 chore: Bump version and update CHANGELOG for v1.9.0
PiperOrigin-RevId: 789494584
2025-07-31 15:34:18 -07:00
Ankur Sharma a54c7024cf fix: Re-adding eval related changes
Due to reasons that are being investigated, some of the recent changes got unintentionally reverted. We are adding those back in this PR.

PiperOrigin-RevId: 789384063
2025-07-31 10:18:39 -07:00
Shangjie Chen 1cfe6e9ffe chore: Remove unnecessary flags
PiperOrigin-RevId: 789379877
2025-07-31 10:07:39 -07:00
Shangjie Chen 314d6a4f95 fix: Return session state in list_session API endpoint
Resolves https://github.com/google/adk-python/issues/2193
Resolves https://github.com/google/adk-python/issues/781

PiperOrigin-RevId: 789143973
2025-07-30 19:53:46 -07:00
Alejandro Cruzado-Ruiz 247fd2066c chore: replace module import for BaseAgent in local_eval_service
PiperOrigin-RevId: 789136339
2025-07-30 19:19:17 -07:00
Shangjie Chen 7d06fb735e chore: Move create_session log to where the session is actually created
PiperOrigin-RevId: 789094066
2025-07-30 16:46:25 -07:00
Liang Wu d5dcef2cf0 fix(config): forbid extra fields in AgentToolConfig
PiperOrigin-RevId: 789038376
2025-07-30 14:13:48 -07:00
Xuan Yang 6191412b07 fix: keep existing header values while merging tracking headers for llm_request.config.http_options in Gemini.generate_content_async
PiperOrigin-RevId: 789013693
2025-07-30 13:09:56 -07:00
Xuan Yang 3be1bb37d9 fix: use pull_request_target event as the trigger of PR triaging agent
GitHub workflows triggered by `pull_request` events from forked repositories do not have access to secrets by default due to security considerations.

PiperOrigin-RevId: 789011890
2025-07-30 13:05:15 -07:00
Google Team Member 7c9b0a2567 feat: add chat first-party tool
This tool answers questions about structured data in BigQuery using natural language.

PiperOrigin-RevId: 789000987
2025-07-30 12:35:20 -07:00
Liang Wu 0c6086cb15 chore: remove redundant definition for adk deploy gke command
PiperOrigin-RevId: 788758843
2025-07-29 22:47:56 -07:00
hsuyuming 9db5d9a3e8 fix: Unable to acquire impersonated credentials
Merge https://github.com/google/adk-python/pull/2003

add scope "https://www.googleapis.com/auth/cloud-platform" within google.auth.default

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2003 from hsuyuming:fix/issue_2001_support_impersonated_credential 8874a367273aca98460f7f250bfc4690f883ebbe
PiperOrigin-RevId: 788656025
2025-07-29 16:42:25 -07:00
Xiang (Sean) Zhou de6ebddcd2 chore: Replace selcukg with genquan
PiperOrigin-RevId: 788590913
2025-07-29 13:48:54 -07:00
Liang Wu bcac9ba44c feat(config): add --type flag to adk create to allow starting with config
Updated the `adk create` default model version to gemini-2.5-flash.

PiperOrigin-RevId: 788589859
2025-07-29 13:45:11 -07:00
Xiang (Sean) Zhou 2f73cfde18 chore: Fix the long running test cases
The test test_token_exchange_not_supported was slow because of an incorrect monkeypatch target. The test was patching google.adk.auth.auth_handler.AUTHLIB_AVAILABLE, but the actual OAuth2 exchange logic uses a different AUTHLIB_AVAILABLE variable in google.adk.auth.exchanger.oauth2_credential_exchanger.
What was happening:
Test set auth_handler.AUTHLIB_AVAILABLE = False
AuthHandler.exchange_auth_token() called OAuth2CredentialExchanger.exchange()
But oauth2_credential_exchanger.AUTHLIB_AVAILABLE was still True
The exchanger attempted real OAuth2 token exchange with client.fetch_token()
This made actual network calls to OAuth2 endpoints, causing timeouts and delays

PiperOrigin-RevId: 788576949
2025-07-29 13:14:28 -07:00
Xuan Yang ec8dd5721a fix: merge tracking headers even when llm_request.config.http_options is not set in Gemini.generate_content_async
PiperOrigin-RevId: 788568620
2025-07-29 12:52:32 -07:00
Xuan Yang 646eb42533 chore: add Github workflow config for the ADK PR triaging agent
PiperOrigin-RevId: 788519884
2025-07-29 10:49:35 -07:00
Google Team Member bf72426af2 fix: runner was expecting Event object instead of Content object when using early exist feature
PiperOrigin-RevId: 788516645
2025-07-29 10:41:19 -07:00
Holt Skinner 9c0721beaa fix: Update agent_card_builder to follow grammar rules
Merge https://github.com/google/adk-python/pull/2226

Fixes #2223

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2226 from holtskinner:a2a-fixes ff556224e4071a8287a9ced19f645f0edd9916ef
PiperOrigin-RevId: 788512608
2025-07-29 10:32:32 -07:00
Xuan Yang 5eff66a132 chore: create an initial prototype agent to triage pull requests
This agent will post a comment if the PR is not following our contribution guides or add a label and reviewer for the PR if it passes the guide check.

PiperOrigin-RevId: 788511767
2025-07-29 10:29:35 -07:00
Google Team Member 282d67f253 fix: import cli's artifact dependencies directly
PiperOrigin-RevId: 788488501
2025-07-29 09:34:19 -07:00
Xiang (Sean) Zhou 3432b22172 fix: Copy the original function call args before passing it to callback or tools to avoid being modified
PiperOrigin-RevId: 788462897
2025-07-29 08:20:48 -07:00
Yifan Wang af35e2673f chore: WIP endpoint
PiperOrigin-RevId: 788232652
2025-07-28 18:06:03 -07:00
Xuan Yang f68d4d5cd0 chore: add the missing license header for a2a/__init__.py
PiperOrigin-RevId: 788227196
2025-07-28 17:50:26 -07:00
Google Team Member 16e8419e32 fix: restore bigquery sample agent to runnable form
A previous change of import paths had rendered the agent not-runnable out of the box. This change fixes that.

PiperOrigin-RevId: 788221276
2025-07-28 17:31:50 -07:00
Xiang (Sean) Zhou 0c855877c5 docs: Update documents about the information of viber coding
PiperOrigin-RevId: 788217734
2025-07-28 17:18:53 -07:00
Yeesian Ng f29ab5db05 feat: Respect the .ae_ignore file when deploying to agent engine
PiperOrigin-RevId: 788052720
2025-07-28 10:01:54 -07:00
Liang Wu f1889ae440 feat(config): support ADK built-in and custom tools in config
PiperOrigin-RevId: 787735915
2025-07-27 11:14:04 -07:00
Ankur Sharma c69dcf8779 feat: Added an Fast API new endpoint to serve eval metric info
This endpoint could be used by ADK Web to dynamically know:
- What are the available eval metrics in an App
- A description of those metrics
- A value range supported by those metrics

We also update the metric registry to make it mandatory to supply these details. The goal is to improve usability and interpretability of the eval metrics.

PiperOrigin-RevId: 787277695
2025-07-25 16:25:20 -07:00
Wei Sun (Jack) ec7d9b0ff6 chore(config): Moves agent configs to separate python files
PiperOrigin-RevId: 787245794
2025-07-25 14:40:49 -07:00
Yeesian Ng 6419a2aa9b fix: Switch from agent_engine_name to agent_engine_id for updating instances
PiperOrigin-RevId: 787179405
2025-07-25 11:24:10 -07:00
Yifan Wang b83b0a6eec chore: experiment endpoint
PiperOrigin-RevId: 787164869
2025-07-25 10:44:09 -07:00
Wei Sun (Jack) a3ff21eb0b feat(config): Adds CustomAgentConfig to support user-defined agents in config
PiperOrigin-RevId: 787148485
2025-07-25 09:56:10 -07:00
qieqieplus 1778490e64 fix: Fix unsafe_local_code_executor for import scope
Merge https://github.com/google/adk-python/pull/869

How to reproduce the error:

```
from google.adk.code_executors import UnsafeLocalCodeExecutor
from google.adk.code_executors.code_execution_utils import CodeExecutionInput

result = UnsafeLocalCodeExecutor().execute_code(
    invocation_context=None,
    code_execution_input=CodeExecutionInput(
        code='''
import math

def pi():
    return math.pi

print(pi())
'''
    )
)

print(result)
```

output:
```
CodeExecutionResult(stdout='', stderr="name 'math' is not defined", output_files=[])
```
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/869 from qieqieplus:main 63f557bbd3b7aa5c2801f5cc9e022d3364177308
PiperOrigin-RevId: 787145189
2025-07-25 09:45:34 -07:00
Vicente Ferrara a858d79b3a feat: cli funcionality to deploy an Agent to a running GKE cluster
Merge https://github.com/google/adk-python/pull/1607

- Added CLI functionality so that we can deploy and Agent onto a GKE cluster
- Related documentation https://github.com/google/adk-docs/pull/445

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/1607 from vicentefb:GkeDeployAgent 42f35d93b0a5df5f6dbeb9ee7f869cde51e2f6eb
PiperOrigin-RevId: 786857789
2025-07-24 15:32:23 -07:00
Lam Nguyen c8f8b4a20a fix: Fix incorrect token count mapping in telemetry
Merge https://github.com/google/adk-python/pull/2109

Fixes #2105

## Problem
  When integrating Google ADK with Langfuse using the @observe
  decorator, the usage details displayed in Langfuse web UI were
  incorrect.

  The root cause was in the telemetry implementation where
  total_token_count was being mapped to gen_ai.usage.output_tokens
  instead of candidates_token_count.

  - Expected mapping:
    - candidates_token_count → completion_tokens (output tokens)
    - prompt_token_count → prompt_tokens (input tokens)

  - Previous incorrect mapping:
    - total_token_count → completion_tokens (wrong!)
    - prompt_token_count → prompt_tokens (correct)

  ## Solution
  Updated trace_call_llm function in telemetry.py to use
  candidates_token_count for output token tracking instead of
  total_token_count, ensuring proper token count reporting to
  observability tools like Langfuse.

  ## Testing plan
  - Updated test expectations in test_telemetry.py
  - Verified telemetry tests pass
  - Manual verification with Langfuse integration

  ## Screenshots
  **Before**

<img width="1187" height="329" alt="Screenshot from 2025-07-22 20-20-33" src="https://github.com/user-attachments/assets/ad5fc957-64a2-4524-bd31-0cebb15a5270" />

  **After**

<img width="1187" height="329" alt="Screenshot from 2025-07-22 20-21-40" src="https://github.com/user-attachments/assets/3920df2a-be75-47e0-9bd0-f961bb72c838" />

 _Notes_: From the screenshot, there's another problem: thoughts_token_count field is not mapped, but this should be another issue imo

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2109 from tl-nguyen:fix-telemetry-token-count-mapping 3d043f558b5f8bcb2c6e0370e2cc4c0ff25d1f4a
PiperOrigin-RevId: 786827802
2025-07-24 14:01:03 -07:00
Shangjie Chen 11037fc133 chore: Filter event with only functions, thought_signatures when adding to memory
PiperOrigin-RevId: 786825628
2025-07-24 13:54:37 -07:00
Yeesian Ng 206a13271e feat: Add a CLI option to update an agent engine instance
PiperOrigin-RevId: 786816663
2025-07-24 13:27:49 -07:00
Xuan Yang 3be0882c63 feat: add -v, --verbose flag to enable DEBUG logging as a shortcut for --log_level DEBUG
PiperOrigin-RevId: 786797394
2025-07-24 12:30:30 -07:00
Alejandro Cruzado-Ruiz e176f03e8f feat: modularize fast_api.py to allow simpler construction of API Server
PiperOrigin-RevId: 786763344
2025-07-24 10:53:13 -07:00
Kavin Kumar B 7206e0a0eb fix: eval module not found exception string
Merge https://github.com/google/adk-python/pull/2148

This PR fixes #2071 exception string from `pip install google-adk[eval]` to `pip install "google-adk[eval]"` which makes it compatible for all the bash, zsh and other terminals

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2148 from kavinkumar807:fix-module-not-found-exception-string-in-eval 914281006a0e162665c0933d0c0ee0c37eb397cf
PiperOrigin-RevId: 786752261
2025-07-24 10:23:03 -07:00
Yeesian Ng fbe6a7b8d3 fix: Add absolutize_imports option when deploying to agent engine
PiperOrigin-RevId: 786749263
2025-07-24 10:15:35 -07:00
Google Team Member bfc203a92f feat: modularize fast_api.py to allow simpler construction of API Server
PiperOrigin-RevId: 786646546
2025-07-24 04:03:19 -07:00
Hangfei Lin 5e8aa15a50 feat: add support for session resumption(only transparent mode) config to run_config
This commit adds support for the session resumption configuration in the run_config.
The SessionResumptionConfig is added to RunConfig to allow the user to set up a configuration for session resumption(only transparent mode for now).

There are two modes of session resumption: manual and transparent. In manual mode, you have to manually bookkeeping the session information and restarts the session which is tricky to do right now. In transparent mode, the server does the bookkeeping for you and no hassle on ADK side. For now, the transparent mode should be enough.

Also, added the relevant unit tests to check that every possible configuration is set properly and the run_config is correctly populated.
This is needed for supporting the new session resumption feature.

PiperOrigin-RevId: 786549455
2025-07-23 21:37:00 -07:00
Che Liu 16392984c5 feat: Expose Gemini RetryOptions to client
google.genai SDK has introduced a new retry_options. This change exposes this configuration
to ADK users

Usage:

```python
root_agent = Agent(
    model=Gemini(
        model='gemini-2.0-flash',
        retry_options=types.HttpRetryOptions(
	    initial_delay=1,
	    attempts=2
	    # ... Retry options from google.genai
	),
	# ...
    ),
```
PiperOrigin-RevId: 786472564
2025-07-23 16:50:09 -07:00
Che Liu 20537e8bfa feat: Add sample plugin for logging
This plugin helps printing all critical events in the console. It is not a replacement
of existing logging in ADK. It rather helps terminal based debugging by showing all logs in the console, and serves as a
simple demo so everyone could develop their own plugins.

PiperOrigin-RevId: 786470637
2025-07-23 16:43:32 -07:00
Che Liu 00afaaf2fc feat: add new callbacks to handle tool and model errors
This CL add new callbacks in plugin system:
- `on_tool_error_callback`
- `on_model_error_callback`

This allow the user to create plugins that can handle errors.

PiperOrigin-RevId: 786469646
2025-07-23 16:40:09 -07:00
Alejandro Cruzado-Ruiz dfc25c17a9 feat: modularize fast_api.py to allow simpler construction of API Server
PiperOrigin-RevId: 786467758
2025-07-23 16:33:54 -07:00
Ariz Chang 32ae882a49 feat: Add camel case converter for agents
PiperOrigin-RevId: 786465205
2025-07-23 16:27:03 -07:00
Ankur Sharma 884c201958 chore: Release 1.8.0
PiperOrigin-RevId: 786462899
2025-07-23 16:19:49 -07:00
Andrew Larimer 70c461686e fix: add space to allow adk deploy cloud_run --a2a
Merge https://github.com/google/adk-python/pull/2138

This missing space leads to an error when deploying to cloud_run that says "No option --a2a/apps/agents"

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2138 from andrewlarimer:fix--add-space-to-allow-adk-deploy-cloud_run---a2a 47831f10e1f7f6c27b5f6b8c102b2f7db4619778
PiperOrigin-RevId: 786459787
2025-07-23 16:09:49 -07:00
Ankur Sharma 1355bd643b feat: Refactored AgentEvaluator and updated it to use LocalEvalService
With this change we ensure that all three eval entry points, web, cli and pytest use the common LocalEvalService.

Updates to web and cli happened in a previous change.

PiperOrigin-RevId: 786445632
2025-07-23 15:27:26 -07:00
Xiang (Sean) Zhou 927c75f0ee chore: Replace imports by importing from actual module instead of from package (__init__.py)
PiperOrigin-RevId: 786342250
2025-07-23 10:49:43 -07:00
Xiang (Sean) Zhou 430b82024f chore: Fixed flaky test_update_credential_with_tokens unittest
PiperOrigin-RevId: 786340983
2025-07-23 10:45:01 -07:00
Xiang (Sean) Zhou 70266abfc2 chore: fix UT failures of test_google_llm.py
Mainly it's due to GenAI sdk changed their header of genai SDK versions, we have UT to verify that ADK or ADK users won't override their headers. Updated the header accordingly in the UT.

PiperOrigin-RevId: 786334741
2025-07-23 10:31:16 -07:00
Holt Skinner a911469616 chore: Update a2a-sdk to 0.2.16
Convert a2a types to use snake_case fields

https://github.com/a2aproject/a2a-python/releases/tag/v0.2.16

PiperOrigin-RevId: 786279179
2025-07-23 07:54:03 -07:00
Michael Timblin ce7253f63f fix: Use correct type for actions parameter in ApplicationIntegrationToolset
Merge https://github.com/google/adk-python/pull/2102

Addresses https://github.com/google/adk-python/issues/2101

I've ran `pytest ./tests/unittests`, and all tests passed

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2102 from manifoldtimblin:fix-type-hint 6d4ab724ff07688158d3d121a78b2c00493a26a7
PiperOrigin-RevId: 786046567
2025-07-22 16:49:45 -07:00
Alejandro Cruzado-Ruiz bda3df2480 feat: Refactor AgentLoader into base class and add InMemory impl alongside existing filesystem impl
PiperOrigin-RevId: 786008518
2025-07-22 14:58:32 -07:00
262 changed files with 14742 additions and 2639 deletions
@@ -0,0 +1,44 @@
name: ADK Answering Agent for Discussions
on:
discussion:
types: [created]
jobs:
agent-answer-questions:
if: github.event.discussion.category.name == 'Q&A'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- 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
- 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
OWNER: 'google'
REPO: 'adk-python'
INTERACTIVE: 0
DISCUSSION_NUMBER: ${{ github.event.discussion.number }}
PYTHONPATH: contributing/samples
run: python -m adk_answering_agent.main
+38
View File
@@ -0,0 +1,38 @@
name: ADK Pull Request Triaging Agent
on:
pull_request_target:
types: [opened, reopened, edited]
jobs:
agent-triage-pull-request:
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests google-adk
- name: Run Triaging Script
env:
GITHUB_TOKEN: ${{ secrets.ADK_TRIAGE_AGENT }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
GOOGLE_GENAI_USE_VERTEXAI: 0
OWNER: 'google'
REPO: 'adk-python'
PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }}
INTERACTIVE: ${{ vars.PR_TRIAGE_INTERACTIVE }}
PYTHONPATH: contributing/samples
run: python -m adk_pr_triaging_agent.main
+113
View File
@@ -1,5 +1,118 @@
# Changelog
## [1.10.0](https://github.com/google/adk-python/compare/v1.9.0...v1.10.0) (2025-08-07)
### Features
* [Live] Implement Live Session Resumption ([71fbc92](https://github.com/google/adk-python/commit/71fbc9275b3d74700ec410cb4155ba0cb18580b7))
* [Tool] Support parallel execution of parallel function calls ([57cd41f](https://github.com/google/adk-python/commit/57cd41f424b469fb834bb8f2777b5f7be9aa6cdf))
* [Models] Allow max tokens to be customizable in Claude ([7556ebc](https://github.com/google/adk-python/commit/7556ebc76abd3c776922c2803aed831661cf7f82))
* [Tool] Create enterprise_web_search_tool as a tool instance ([0e28d64](https://github.com/google/adk-python/commit/0e28d64712e481cfd3b964be0166f529657024f6))
### Bug Fixes
* Fix shared default plugin manager and cost manager instances among multiple invocations ([423542a](https://github.com/google/adk-python/commit/423542a43fb8316195e9f79d97f87593751bebd3))
* Correct the type annotation in anthropic_llm implementation ([97318bc](https://github.com/google/adk-python/commit/97318bcd199acdacadfe8664da3fbfc3c806cdd2))
* Fix adk deploy cloud_run cli, which was broken in v1.9.0 ([e41dbcc](https://github.com/google/adk-python/commit/e41dbccf7f610e249108f9321f60f71fe2cc10f4))
* Remove thoughts from contents in llm requests from history contents ([d620bcb](https://github.com/google/adk-python/commit/d620bcb384d3068228ea2059fb70274e68e69682))
* Annotate response type as None for transfer_to_agent tool ([86a4487](https://github.com/google/adk-python/commit/86a44873e9b2dfc7e62fa31a9ac3be57c0bbff7b))
* Fix incompatible a2a sdk changes ([faadef1](https://github.com/google/adk-python/commit/faadef167ee8e4dd1faf4da5685a577c3155556e))
* Fix adk cli options and method parameters mismatching ([8ef2177](https://github.com/google/adk-python/commit/8ef2177658fbfc74b1a74b0c3ea8150bae866796))
### Improvements
* Add Github workflow config for the ADK Answering agent ([8dc0c94](https://github.com/google/adk-python/commit/8dc0c949afb9024738ff7ac1b2c19282175c3200))
* Import AGENT_CARD_WELL_KNOWN_PATH from adk instead of from a2a directly ([37dae9b](https://github.com/google/adk-python/commit/37dae9b631db5060770b66fce0e25cf0ffb56948))
* Make `LlmRequest.LiveConnectConfig` field default to a factory ([74589a1](https://github.com/google/adk-python/commit/74589a1db7df65e319d1ad2f0676ee0cf5d6ec1d))
* Update the prompt to make the ADK Answering Agent more objective ([2833030](https://github.com/google/adk-python/commit/283303032a174d51b8d72f14df83c794d66cb605))
* Add sample agent for testing parallel functions execution ([90b9193](https://github.com/google/adk-python/commit/90b9193a20499b8dd7f57d119cda4c534fcfda10))
* Hide the ask_data_insights tool until the API is publicly available ([bead607](https://github.com/google/adk-python/commit/bead607364be7ac8109357c9d3076d9b345e9e8a))
* Change `LlmRequest.config`'s default value to be `types.GenerateContentConfig()` ([041f04e](https://github.com/google/adk-python/commit/041f04e89cee30532facccce4900d10f1b8c69ce))
* Prevent triggering of _load_from_yaml_config in AgentLoader ([db975df](https://github.com/google/adk-python/commit/db975dfe2a09a6d056d02bc03c1247ac10f6da7d))
### Documentation
* Fix typos ([16a15c8](https://github.com/google/adk-python/commit/16a15c8709b47c9bebe7cffe888e8e7e48ec605a))
## [1.9.0](https://github.com/google/adk-python/compare/v1.8.0...v1.9.0) (2025-07-31)
### Features
* [CLI] Add `-v`, `--verbose` flag to enable DEBUG logging as a shortcut for `--log_level DEBUG` ([3be0882](https://github.com/google/adk-python/commit/3be0882c63bf9b185c34bcd17e03769b39f0e1c5))
* [CLI] Add a CLI option to update an agent engine instance ([206a132](https://github.com/google/adk-python/commit/206a13271e5f1bb0bb8114b3bb82f6ec3f030cd7))
* [CLI] Modularize fast_api.py to allow simpler construction of API Server ([bfc203a](https://github.com/google/adk-python/commit/bfc203a92fdfbc4abaf776e76dca50e7ca59127b), [dfc25c1](https://github.com/google/adk-python/commit/dfc25c17a98aaad81e1e2f140db83d17cd78f393), [e176f03](https://github.com/google/adk-python/commit/e176f03e8fe13049187abd0f14e63afca9ccff01))
* [CLI] Refactor AgentLoader into base class and add InMemory impl alongside existing filesystem impl ([bda3df2](https://github.com/google/adk-python/commit/bda3df24802d0456711a5cd05544aea54a13398d))
* [CLI] Respect the .ae_ignore file when deploying to agent engine ([f29ab5d](https://github.com/google/adk-python/commit/f29ab5db0563a343d6b8b437a12557c89b7fc98b))
* [Core] Add new callbacks to handle tool and model errors ([00afaaf](https://github.com/google/adk-python/commit/00afaaf2fc18fba85709754fb1037bb47f647243))
* [Core] Add sample plugin for logging ([20537e8](https://github.com/google/adk-python/commit/20537e8bfa31220d07662dad731b4432799e1802))
* [Core] Expose Gemini RetryOptions to client ([1639298](https://github.com/google/adk-python/commit/16392984c51b02999200bd4f1d6781d5ec9054de))
* [Evals] Added an Fast API new endpoint to serve eval metric info ([c69dcf8](https://github.com/google/adk-python/commit/c69dcf87795c4fa2ad280b804c9b0bd3fa9bf06f))
* [Evals] Refactored AgentEvaluator and updated it to use LocalEvalService ([1355bd6](https://github.com/google/adk-python/commit/1355bd643ba8f7fd63bcd6a7284cc48e325d138e))
### Bug Fixes
* Add absolutize_imports option when deploying to agent engine ([fbe6a7b](https://github.com/google/adk-python/commit/fbe6a7b8d3a431a1d1400702fa534c3180741eb3))
* Add space to allow adk deploy cloud_run --a2a ([70c4616](https://github.com/google/adk-python/commit/70c461686ec2c60fcbaa384a3f1ea2528646abba))
* Copy the original function call args before passing it to callback or tools to avoid being modified ([3432b22](https://github.com/google/adk-python/commit/3432b221727b52af2682d5bf3534d533a50325ef))
* Eval module not found exception string ([7206e0a](https://github.com/google/adk-python/commit/7206e0a0eb546a66d47fb411f3fa813301c56f42))
* Fix incorrect token count mapping in telemetry ([c8f8b4a](https://github.com/google/adk-python/commit/c8f8b4a20a886a17ce29abd1cfac2858858f907d))
* Import cli's artifact dependencies directly ([282d67f](https://github.com/google/adk-python/commit/282d67f253935af56fae32428124a385f812c67d))
* Keep existing header values while merging tracking headers for `llm_request.config.http_options` in `Gemini.generate_content_async` ([6191412](https://github.com/google/adk-python/commit/6191412b07c3b5b5a58cf7714e475f63e89be847))
* Merge tracking headers even when `llm_request.config.http_options` is not set in `Gemini.generate_content_async` ([ec8dd57](https://github.com/google/adk-python/commit/ec8dd5721aa151cfc033cc3aad4733df002ae9cb))
* Restore bigquery sample agent to runnable form ([16e8419](https://github.com/google/adk-python/commit/16e8419e32b54298f782ba56827e5139effd8780))
* Return session state in list_session API endpoint ([314d6a4](https://github.com/google/adk-python/commit/314d6a4f95c6d37c7da3afbc7253570564623322))
* Runner was expecting Event object instead of Content object when using early exist feature ([bf72426](https://github.com/google/adk-python/commit/bf72426af2bfd5c2e21c410005842e48b773deb3))
* Unable to acquire impersonated credentials ([9db5d9a](https://github.com/google/adk-python/commit/9db5d9a3e87d363c1bac0f3d8e45e42bd5380d3e))
* Update `agent_card_builder` to follow grammar rules ([9c0721b](https://github.com/google/adk-python/commit/9c0721beaa526a4437671e6cc70915073be835e3)), closes [#2223](https://github.com/google/adk-python/issues/2223)
* Use correct type for actions parameter in ApplicationIntegrationToolset ([ce7253f](https://github.com/google/adk-python/commit/ce7253f63ff8e78bccc7805bd84831f08990b881))
### Documentation
* Update documents about the information of vibe coding ([0c85587](https://github.com/google/adk-python/commit/0c855877c57775ad5dad930594f9f071164676da))
## [1.8.0](https://github.com/google/adk-python/compare/v1.7.0...v1.8.0) (2025-07-23)
### Features
* [Core]Add agent card builder ([18f5bea](https://github.com/google/adk-python/commit/18f5bea411b3b76474ff31bfb2f62742825b45e5))
* [Core]Add an to_a2a util to convert adk agent to A2A ASGI application ([a77d689](https://github.com/google/adk-python/commit/a77d68964a1c6b7659d6117d57fa59e43399e0c2))
* [Core]Add camel case converter for agents ([0e173d7](https://github.com/google/adk-python/commit/0e173d736334f8c6c171b3144ac6ee5b7125c846))
* [Evals]Use LocalEvalService to run all evals in cli and web ([d1f182e](https://github.com/google/adk-python/commit/d1f182e8e68c4a5a4141592f3f6d2ceeada78887))
* [Evals]Enable FinalResponseMatchV2 metric as an experiment ([36e45cd](https://github.com/google/adk-python/commit/36e45cdab3bbfb653eee3f9ed875b59bcd525ea1))
* [Models]Add support for `model-optimizer-*` family of models in vertex ([ffe2bdb](https://github.com/google/adk-python/commit/ffe2bdbe4c2ea86cc7924eb36e8e3bb5528c0016))
* [Services]Added a sample for History Management ([67284fc](https://github.com/google/adk-python/commit/67284fc46667b8c2946762bc9234a8453d48a43c))
* [Services]Support passing fully qualified agent engine resource name when constructing session service and memory service ([2e77804](https://github.com/google/adk-python/commit/2e778049d0a675e458f4e
35fe4104ca1298dbfcf))
* [Tools]Add ComputerUseToolset ([083dcb4](https://github.com/google/adk-python/commit/083dcb44650eb0e6b70219ede731f2fa78ea7d28))
* [Tools]Allow toolset to process llm_request before tools returned by it ([3643b4a](https://github.com/google/adk-python/commit/3643b4ae196fd9e38e52d5dc9d1cd43ea0733d36))
* [Tools]Support input/output schema by fully-qualified code reference ([dfee06a](https://github.com/google/adk-python/commit/dfee06ac067ea909251d6fb016f8331065d430e9))
* [Tools]Enhance LangchainTool to accept more forms of functions ([0ec69d0](https://github.com/google/adk-python/commit/0ec69d05a4016adb72abf9c94f2e9ff4bdd1848c))
### Bug Fixes
* **Attention**: Logging level for some API requests and responses was moved from `INFO` to `DEBUG` ([ff31f57](https://github.com/google/adk-python/commit/ff31f57dc95149f8f309f83f2ec983ef40f1122c))
* Please set `--log_level=DEBUG`, if you are interested in having those API request and responses in logs.
* Add buffer to the write file option ([f2caf2e](https://github.com/google/adk-python/commit/f2caf2eecaf0336495fb42a2166b1b79e57d82d8))
* Allow current sub-agent to finish execution before exiting the loop agent due to a sub-agent's escalation. ([2aab1cf](https://github.com/google/adk-python/commit/2aab1cf98e1d0e8454764b549fac21475a633409))
* Check that `mean_score` is a valid float value ([65cb6d6](https://github.com/google/adk-python/commit/65cb6d6bf3278e6c3529938a7b932e3ef6d6c2ae))
* Handle non-json-serializable values in the `execute_sql` tool ([13ff009](https://github.com/google/adk-python/commit/13ff009d34836a80f107cb43a632df15f7c215e4))
* Raise `NotFoundError` in `list_eval_sets` function when app_name doesn't exist ([b17d8b6](https://github.com/google/adk-python/commit/b17d8b6e362a5b2a1b6a2dd0cff5e27a71c27925))
* Fixed serialization of tools with nested schema ([53df35e](https://github.com/google/adk-python/commit/53df35ee58599e9816bd4b9c42ff48457505e599))
* Set response schema for function tools that returns `None` ([33ac838](https://github.com/google/adk-python/commit/33ac8380adfff46ed8a7d518ae6f27345027c074))
* Support path level parameters for open_api_spec_parser ([6f01660](https://github.com/google/adk-python/commit/6f016609e889bb0947877f478de0c5729cfcd0c3))
* Use correct type for actions parameter in ApplicationIntegrationToolset ([ce7253f](https://github.com/google/adk-python/commit/ce7253f63ff8e78bccc7805bd84831f08990b881))
* Use the same word extractor for query and event contents in InMemoryMemoryService ([1c4c887](https://github.com/google/adk-python/commit/1c4c887bec9326aad2593f016540160d95d03f33))
### Documentation
* Fix missing toolbox-core dependency and improve installation guide ([2486349](https://github.com/google/adk-python/commit/24863492689f36e3c7370be40486555801858bac))
## 1.7.0 (2025-07-16)
### Features
+4
View File
@@ -210,3 +210,7 @@ All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
information on using pull requests.
# Vibe Coding
If you want to contribute by leveraging viber coding, the AGENTS.md (https://github.com/google/adk-python/tree/main/AGENTS.md) could be used as context to your LLM.
+4
View File
@@ -138,6 +138,10 @@ We welcome contributions from the community! Whether it's bug reports, feature r
- [General contribution guideline and flow](https://google.github.io/adk-docs/contributing-guide/).
- Then if you want to contribute code, please read [Code Contributing Guidelines](./CONTRIBUTING.md) to get started.
## Vibe Coding
If you are to develop agent via vibe coding the [llms.txt](./llms.txt) and the [llms-full.txt](./llms-full.txt) can be used as context to LLM. While the former one is a summarized one and the later one has the full information in case your LLM has big enough context window.
## đź“„ License
This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENSE) file for details.
+2 -2
View File
@@ -13,11 +13,11 @@
# limitations under the License.
from google.adk.agents import Agent
from google.adk.agents.llm_agent import Agent
from google.adk.agents.remote_a2a_agent import AGENT_CARD_WELL_KNOWN_PATH
from google.adk.agents.remote_a2a_agent import RemoteA2aAgent
from google.adk.tools.langchain_tool import LangchainTool
from langchain_community.tools import YouTubeSearchTool
from langchain_community.tools.youtube.search import YouTubeSearchTool
# Instantiate the tool
langchain_yt_tool = YouTubeSearchTool()
+1 -1
View File
@@ -14,7 +14,7 @@
import random
from google.adk.agents import Agent
from google.adk.agents.llm_agent import Agent
from google.adk.agents.remote_a2a_agent import AGENT_CARD_WELL_KNOWN_PATH
from google.adk.agents.remote_a2a_agent import RemoteA2aAgent
from google.adk.tools.example_tool import ExampleTool
@@ -13,7 +13,7 @@
# limitations under the License.
from google.adk import Agent
from google.adk.agents.llm_agent import Agent
from google.adk.agents.remote_a2a_agent import AGENT_CARD_WELL_KNOWN_PATH
from google.adk.agents.remote_a2a_agent import RemoteA2aAgent
from google.genai import types
@@ -15,8 +15,8 @@
from typing import Any
from google.adk import Agent
from google.adk.tools import ToolContext
from google.adk.tools.long_running_tool import LongRunningFunctionTool
from google.adk.tools.tool_context import ToolContext
from google.genai import types
+1 -1
View File
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from a2a.utils.constants import AGENT_CARD_WELL_KNOWN_PATH
from google.adk.agents.remote_a2a_agent import AGENT_CARD_WELL_KNOWN_PATH
from google.adk.agents.remote_a2a_agent import RemoteA2aAgent
root_agent = RemoteA2aAgent(
@@ -21,8 +21,8 @@ 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 google.adk.agents import Agent
from google.adk.tools import VertexAiSearchTool
from google.adk.agents.llm_agent import Agent
from google.adk.tools.vertex_ai_search_tool import VertexAiSearchTool
import requests
if IS_INTERACTIVE:
@@ -137,6 +137,12 @@ def add_comment_to_discussion(
}
}
"""
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)
@@ -247,10 +253,8 @@ root_agent = Agent(
* {APPROVAL_INSTRUCTION}
* 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.
* **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.
* Include a bolded note (e.g. "Response from ADK Answering Agent") in your comment
to indicate this comment was added by an ADK Answering Agent.
* Have an empty line between the note and the rest of your response.
* Inlclude 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.
+1 -1
View File
@@ -20,7 +20,7 @@ import time
import agent
from google.adk.agents.run_config import RunConfig
from google.adk.runners import InMemoryRunner
from google.adk.sessions import Session
from google.adk.sessions.session import Session
from google.genai import types
@@ -0,0 +1,68 @@
# ADK Pull Request Triaging Assistant
The ADK Pull Request (PR) Triaging Assistant is a Python-based agent designed to help manage and triage GitHub pull requests for the `google/adk-python` repository. It uses a large language model to analyze new and unlabelled pull requests, recommend appropriate labels, assign a reviewer, and check contribution guides based on a predefined set of rules.
This agent can be operated in two distinct modes:
* an interactive mode for local use
* a fully automated GitHub Actions workflow.
---
## Interactive Mode
This mode allows you to run the agent locally to review its recommendations in real-time before any changes are made to your repository's pull requests.
### Features
* **Web Interface**: The agent's interactive mode can be rendered in a web browser using the ADK's `adk web` command.
* **User Approval**: In interactive mode, the agent is instructed to ask for your confirmation before applying a label or posting a comment to a GitHub pull request.
### Running in Interactive Mode
To run the agent in interactive mode, first set the required environment variables. Then, execute the following command in your terminal:
```bash
adk web
```
This will start a local server and provide a URL to access the agent's web interface in your browser.
---
## GitHub Workflow Mode
For automated, hands-off PR triaging, the agent can be integrated directly into your repository's CI/CD pipeline using a GitHub Actions workflow.
### Workflow Triggers
The GitHub workflow is configured to run on specific triggers:
* **Pull Request Events**: The workflow executes automatically whenever a new PR is `opened` or an existing one is `reopened` or `edited`.
### Automated Labeling
When running as part of the GitHub workflow, the agent operates non-interactively. It identifies and applies the best label or posts a comment directly without requiring user approval. This behavior is configured by setting the `INTERACTIVE` environment variable to `0` in the workflow file.
### Workflow Configuration
The workflow is defined in a YAML file (`.github/workflows/pr-triage.yml`). This file contains the steps to check out the code, set up the Python environment, install dependencies, and run the triaging script with the necessary environment variables and secrets.
---
## Setup and Configuration
Whether running in interactive or workflow mode, the agent requires the following setup.
### Dependencies
The agent requires the following Python libraries.
```bash
pip install --upgrade pip
pip install google-adk
```
### Environment Variables
The following environment variables are required for the agent to connect to the necessary services.
* `GITHUB_TOKEN`: **(Required)** A GitHub Personal Access Token with `pull_requests:write` permissions. Needed for both interactive and workflow modes.
* `GOOGLE_API_KEY`: **(Required)** Your API key for the Gemini API. Needed for both interactive and workflow modes.
* `OWNER`: The GitHub organization or username that owns the repository (e.g., `google`). Needed for both modes.
* `REPO`: The name of the GitHub repository (e.g., `adk-python`). Needed for both modes.
* `INTERACTIVE`: Controls the agent's interaction mode. For the automated workflow, this is set to `0`. For interactive mode, it should be set to `1` or left unset.
For local execution in interactive mode, you can place these variables in a `.env` file in the project's root directory. For the GitHub workflow, they should be configured as repository secrets.
@@ -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,317 @@
# 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 pathlib import Path
from typing import Any
from adk_pr_triaging_agent.settings import BOT_LABEL
from adk_pr_triaging_agent.settings import GITHUB_BASE_URL
from adk_pr_triaging_agent.settings import IS_INTERACTIVE
from adk_pr_triaging_agent.settings import OWNER
from adk_pr_triaging_agent.settings import REPO
from adk_pr_triaging_agent.utils import error_response
from adk_pr_triaging_agent.utils import get_diff
from adk_pr_triaging_agent.utils import post_request
from adk_pr_triaging_agent.utils import read_file
from adk_pr_triaging_agent.utils import run_graphql_query
from google.adk import Agent
import requests
LABEL_TO_OWNER = {
"documentation": "polong-lin",
"services": "DeanChensj",
"tools": "seanzhou1023",
"eval": "ankursharmas",
"live": "hangfei",
"models": "genquan9",
"tracing": "Jacksunwei",
"core": "Jacksunwei",
"web": "wyf7107",
}
CONTRIBUTING_MD = read_file(
Path(__file__).resolve().parents[3] / "CONTRIBUTING.md"
)
APPROVAL_INSTRUCTION = (
"Do not ask for user approval for labeling or commenting! If you can't find"
" appropriate labels for the PR, do not label it."
)
if IS_INTERACTIVE:
APPROVAL_INSTRUCTION = (
"Only label or comment when the user approves the labeling or commenting!"
)
def get_pull_request_details(pr_number: int) -> str:
"""Get the details of the specified pull request.
Args:
pr_number: number of the Github pull request.
Returns:
The status of this request, with the details when successful.
"""
print(f"Fetching details for PR #{pr_number} from {OWNER}/{REPO}")
query = """
query($owner: String!, $repo: String!, $prNumber: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $prNumber) {
id
title
body
author {
login
}
labels(last: 10) {
nodes {
name
}
}
files(last: 50) {
nodes {
path
}
}
comments(last: 50) {
nodes {
id
body
createdAt
author {
login
}
}
}
commits(last: 50) {
nodes {
commit {
url
message
}
}
}
statusCheckRollup {
state
contexts(last: 20) {
nodes {
... on StatusContext {
context
state
targetUrl
}
... on CheckRun {
name
status
conclusion
detailsUrl
}
}
}
}
}
}
}
"""
variables = {"owner": OWNER, "repo": REPO, "prNumber": pr_number}
url = f"{GITHUB_BASE_URL}/repos/{OWNER}/{REPO}/pulls/{pr_number}"
try:
response = run_graphql_query(query, variables)
if "errors" in response:
return error_response(str(response["errors"]))
pr = response.get("data", {}).get("repository", {}).get("pullRequest")
if not pr:
return error_response(f"Pull Request #{pr_number} not found.")
# Filter out main merge commits.
original_commits = pr.get("commits", {}).get("nodes", {})
if original_commits:
filtered_commits = [
commit_node
for commit_node in original_commits
if not commit_node["commit"]["message"].startswith(
"Merge branch 'main' into"
)
]
pr["commits"]["nodes"] = filtered_commits
# Get diff of the PR and truncate it to avoid exceeding the maximum tokens.
pr["diff"] = get_diff(url)[:10000]
return {"status": "success", "pull_request": pr}
except requests.exceptions.RequestException as e:
return error_response(str(e))
def add_label_and_reviewer_to_pr(pr_number: int, label: str) -> dict[str, Any]:
"""Adds a specified label and requests a review from a mapped reviewer on a PR.
Args:
pr_number: the number of the Github pull request
label: the label to add
Returns:
The the status of this request, with the applied label and assigned
reviewer when successful.
"""
print(f"Attempting to add label '{label}' and a reviewer to PR #{pr_number}")
if label not in LABEL_TO_OWNER:
return error_response(
f"Error: Label '{label}' is not an allowed label. Will not apply."
)
# Pull Request is a special issue in Github, so we can use issue url for PR.
label_url = (
f"{GITHUB_BASE_URL}/repos/{OWNER}/{REPO}/issues/{pr_number}/labels"
)
label_payload = [label, BOT_LABEL]
try:
response = post_request(label_url, label_payload)
except requests.exceptions.RequestException as e:
return error_response(f"Error: {e}")
owner = LABEL_TO_OWNER.get(label, None)
if not owner:
return {
"status": "warning",
"message": (
f"{response}\n\nLabel '{label}' does not have an owner. Will not"
" assign."
),
"applied_label": label,
}
reviewer_url = f"{GITHUB_BASE_URL}/repos/{OWNER}/{REPO}/pulls/{pr_number}/requested_reviewers"
reviewer_payload = {"reviewers": [owner]}
try:
post_request(reviewer_url, reviewer_payload)
except requests.exceptions.RequestException as e:
return {
"status": "warning",
"message": f"Reviewer not assigned: {e}",
"applied_label": label,
}
return {
"status": "success",
"applied_label": label,
"assigned_reviewer": owner,
}
def add_comment_to_pr(pr_number: int, comment: str) -> dict[str, Any]:
"""Add the specified comment to the given PR number.
Args:
pr_number: the number of the Github pull request
comment: the comment to add
Returns:
The the status of this request, with the applied comment when successful.
"""
print(f"Attempting to add comment '{comment}' to issue #{pr_number}")
# Pull Request is a special issue in Github, so we can use issue url for PR.
url = f"{GITHUB_BASE_URL}/repos/{OWNER}/{REPO}/issues/{pr_number}/comments"
payload = {"body": comment}
try:
post_request(url, payload)
except requests.exceptions.RequestException as e:
return error_response(f"Error: {e}")
return {
"status": "success",
"added_comment": comment,
}
root_agent = Agent(
model="gemini-2.5-pro",
name="adk_pr_triaging_assistant",
description="Triage ADK pull requests.",
instruction=f"""
# 1. Identity
You are a Pull Request (PR) triaging bot for the Github {REPO} repo with the owner {OWNER}.
# 2. Responsibilities
Your core responsibility includes:
- Get the pull request details.
- Add a label to the pull request.
- Assign a reviewer to the pull request.
- Check if the pull request is following the contribution guidelines.
- Add a comment to the pull request if it's not following the guidelines.
**IMPORTANT: {APPROVAL_INSTRUCTION}**
# 3. Guidelines & Rules
Here are the rules for labeling:
- If the PR is about documentations, label it with "documentation".
- If it's about session, memory, artifacts services, label it with "services"
- If it's about UI/web, label it with "web"
- If it's related to tools, label it with "tools"
- If it's about agent evalaution, then label it with "eval".
- If it's about streaming/live, label it with "live".
- 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 you can't find a appropriate labels for the PR, follow the previous instruction that starts with "IMPORTANT:".
Here is the contribution guidelines:
`{CONTRIBUTING_MD}`
Here are the guidelines for checking if the PR is following the guidelines:
- The "statusCheckRollup" in the pull request details may help you to identify if the PR is following some of the guidelines (e.g. CLA compliance).
Here are the guidelines for the comment:
- **Be Polite and Helpful:** Start with a friendly tone.
- **Be Specific:** Clearly list only the sections from the contribution guidelines that are still missing.
- **Address the Author:** Mention the PR author by their username (e.g., `@username`).
- **Provide Context:** Explain *why* the information or action is needed.
- **Do not be repetitive:** If you have already commented on an PR asking for information, do not comment again unless new information has been added and it's still incomplete.
- **Identify yourself:** Include a bolded note (e.g. "Response from ADK Triaging Agent") in your comment to indicate this comment was added by an ADK Answering Agent.
**Example Comment for a PR:**
> **Response from ADK Triaging Agent**
>
> Hello @[pr-author-username], thank you for creating this PR!
>
> This PR is a bug fix, could you please associate the github issue with this PR? If there is no existing issue, could you please create one?
>
> In addition, could you please provide logs or screenshot after the fix is applied?
>
> This information will help reviewers to review your PR more efficiently. Thanks!
# 4. Steps
When you are given a PR, here are the steps you should take:
- Call the `get_pull_request_details` tool to get the details of the PR.
- Skip the PR (i.e. do not label or comment) if the PR is closed or is labeled with "{BOT_LABEL}" or "google-contributior".
- Check if the PR is following the contribution guidelines.
- If it's not following the guidelines, recommend or add a comment to the PR that points to the contribution guidelines (https://github.com/google/adk-python/blob/main/CONTRIBUTING.md).
- If it's following the guidelines, recommend or add a label to the PR.
# 5. Output
Present the followings in an easy to read format highlighting PR number and your label.
- The PR summary in a few sentence
- The label you recommended or added with the justification
- The owner of the label if you assigned a reviewer to the PR
- The comment you recommended or added to the PR with the justification
""",
tools=[
get_pull_request_details,
add_label_and_reviewer_to_pr,
add_comment_to_pr,
],
)
@@ -0,0 +1,65 @@
# 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 time
from adk_pr_triaging_agent import agent
from adk_pr_triaging_agent.settings import OWNER
from adk_pr_triaging_agent.settings import PULL_REQUEST_NUMBER
from adk_pr_triaging_agent.settings import REPO
from adk_pr_triaging_agent.utils import call_agent_async
from adk_pr_triaging_agent.utils import parse_number_string
from google.adk.runners import InMemoryRunner
APP_NAME = "adk_pr_triaging_app"
USER_ID = "adk_pr_triaging_user"
async def main():
runner = InMemoryRunner(
agent=agent.root_agent,
app_name=APP_NAME,
)
session = await runner.session_service.create_session(
app_name=APP_NAME, user_id=USER_ID
)
pr_number = parse_number_string(PULL_REQUEST_NUMBER)
if not pr_number:
print(
f"Error: Invalid pull request number received: {PULL_REQUEST_NUMBER}."
)
return
prompt = f"Please triage pull request #{pr_number}!"
response = await call_agent_async(runner, USER_ID, session.id, prompt)
print(f"<<<< Agent Final Output: {response}\n")
if __name__ == "__main__":
start_time = time.time()
print(
f"Start triaging {OWNER}/{REPO} pull request #{PULL_REQUEST_NUMBER} at"
f" {time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(start_time))}"
)
print("-" * 80)
asyncio.run(main())
print("-" * 80)
end_time = time.time()
print(
"Triaging finished at"
f" {time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(end_time))}",
)
print("Total script execution time:", f"{end_time - start_time:.2f} seconds")
@@ -0,0 +1,33 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
from dotenv import load_dotenv
load_dotenv(override=True)
GITHUB_BASE_URL = "https://api.github.com"
GITHUB_GRAPHQL_URL = GITHUB_BASE_URL + "/graphql"
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
if not GITHUB_TOKEN:
raise ValueError("GITHUB_TOKEN environment variable not set")
OWNER = os.getenv("OWNER", "google")
REPO = os.getenv("REPO", "adk-python")
BOT_LABEL = os.getenv("BOT_LABEL", "bot triaged")
PULL_REQUEST_NUMBER = os.getenv("PULL_REQUEST_NUMBER")
IS_INTERACTIVE = os.environ.get("INTERACTIVE", "1").lower() in ["true", "1"]
@@ -0,0 +1,120 @@
# 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 sys
from typing import Any
from adk_pr_triaging_agent.settings import GITHUB_GRAPHQL_URL
from adk_pr_triaging_agent.settings import GITHUB_TOKEN
from google.adk.agents.run_config import RunConfig
from google.adk.runners import Runner
from google.genai import types
import requests
headers = {
"Authorization": f"token {GITHUB_TOKEN}",
"Accept": "application/vnd.github.v3+json",
}
diff_headers = {
"Authorization": f"token {GITHUB_TOKEN}",
"Accept": "application/vnd.github.v3.diff",
}
def run_graphql_query(query: str, variables: dict[str, Any]) -> dict[str, Any]:
"""Executes a GraphQL query."""
payload = {"query": query, "variables": variables}
response = requests.post(
GITHUB_GRAPHQL_URL, headers=headers, json=payload, timeout=60
)
response.raise_for_status()
return response.json()
def get_request(url: str, params: dict[str, Any] | None = None) -> Any:
"""Executes a GET request."""
if params is None:
params = {}
response = requests.get(url, headers=headers, params=params, timeout=60)
response.raise_for_status()
return response.json()
def get_diff(url: str) -> str:
"""Executes a GET request for a diff."""
response = requests.get(url, headers=diff_headers)
response.raise_for_status()
return response.text
def post_request(url: str, payload: Any) -> dict[str, Any]:
"""Executes a POST request."""
response = requests.post(url, headers=headers, json=payload, timeout=60)
response.raise_for_status()
return response.json()
def error_response(error_message: str) -> dict[str, Any]:
"""Returns an error response."""
return {"status": "error", "error_message": error_message}
def read_file(file_path: str) -> str:
"""Read the content of the given file."""
try:
with open(file_path, "r") as f:
return f.read()
except FileNotFoundError:
print(f"Error: File not found: {file_path}.")
return ""
def parse_number_string(number_str: str | None, default_value: int = 0) -> int:
"""Parse a number from the given string."""
if not number_str:
return default_value
try:
return int(number_str)
except ValueError:
print(
f"Warning: Invalid number string: {number_str}. Defaulting to"
f" {default_value}.",
file=sys.stderr,
)
return default_value
async def call_agent_async(
runner: Runner, user_id: str, session_id: str, prompt: str
) -> str:
"""Call the agent asynchronously with the user's prompt."""
content = types.Content(
role="user", parts=[types.Part.from_text(text=prompt)]
)
final_response_text = ""
async for event in runner.run_async(
user_id=user_id,
session_id=session_id,
new_message=content,
run_config=RunConfig(save_input_blobs_as_artifacts=False),
):
if event.content and event.content.parts:
if text := "".join(part.text or "" for part in event.content.parts):
if event.author != "user":
final_response_text += text
return final_response_text
@@ -23,7 +23,7 @@ from adk_triaging_agent.utils import error_response
from adk_triaging_agent.utils import get_request
from adk_triaging_agent.utils import patch_request
from adk_triaging_agent.utils import post_request
from google.adk import Agent
from google.adk.agents.llm_agent import Agent
import requests
LABEL_TO_OWNER = {
@@ -34,7 +34,7 @@ LABEL_TO_OWNER = {
"tools": "seanzhou1023",
"eval": "ankursharmas",
"live": "hangfei",
"models": "selcukgun",
"models": "genquan9",
"tracing": "Jacksunwei",
"core": "Jacksunwei",
"web": "wyf7107",
+5 -5
View File
@@ -14,10 +14,10 @@
import os
from google.adk.agents import llm_agent
from google.adk.auth import AuthCredentialTypes
from google.adk.tools.bigquery import BigQueryCredentialsConfig
from google.adk.tools.bigquery import BigQueryToolset
from google.adk.agents.llm_agent import LlmAgent
from google.adk.auth.auth_credential import AuthCredentialTypes
from google.adk.tools.bigquery.bigquery_credentials import BigQueryCredentialsConfig
from google.adk.tools.bigquery.bigquery_toolset import BigQueryToolset
from google.adk.tools.bigquery.config import BigQueryToolConfig
from google.adk.tools.bigquery.config import WriteMode
import google.auth
@@ -62,7 +62,7 @@ bigquery_toolset = BigQueryToolset(
# The variable name `root_agent` determines what your root agent is for the
# debug CLI
root_agent = llm_agent.Agent(
root_agent = LlmAgent(
model="gemini-2.0-flash",
name="bigquery_agent",
description=(

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