Compare commits

...

96 Commits

Author SHA1 Message Date
Xiang (Sean) Zhou 832a633351 chore: Enhance a2a part converters
a. fix binary data conversion
b. support thoughts, code execution result, executable codes conversion

PiperOrigin-RevId: 775827259
2025-06-25 13:58:49 -07:00
Xuan Yang 738d1a8b84 chore: create an agent to check issue format and content for bugs and feature requests
This agent will pose a comment to ask for more information according to the template if necessary.

PiperOrigin-RevId: 775742256
2025-06-25 10:20:45 -07:00
Hangfei Lin 5306ddad4d chore: Release 1.5.0
PiperOrigin-RevId: 775742049
2025-06-25 10:19:20 -07:00
Xiang (Sean) Zhou a623467299 chore: Enhance a2a context id parsing and construction logic
PiperOrigin-RevId: 775718282
2025-06-25 09:19:16 -07:00
Xiang (Sean) Zhou f54b9b6ad1 chore: Add unit tests for contents.py
PiperOrigin-RevId: 775713101
2025-06-25 09:06:01 -07:00
Google Team Member 6729edd08e refactor: Rename the Google API based bigquery sample agent
This change renames the sample agent based on the Google API based tools to reflect the larger purpose and avoid confusion with the built-in BigQuery tools. In addition, it also renames the root agent in the BigQuery sample agent to "bigquery_agent"

PiperOrigin-RevId: 775655226
2025-06-25 06:05:32 -07:00
Xiang (Sean) Zhou 917a8a19f7 chore: Adapt oauth calendar agent to use authenticated tool
PiperOrigin-RevId: 775433950
2025-06-24 16:46:20 -07:00
Aditya Mulik ef3c745d65 fix: typo fix in sample agent instruction
Merge https://github.com/google/adk-python/pull/1623

fix: minor typo fix in the agent instruction
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/1623 from adityamulik:minor_typo_fix 12ea09ae397b5c5e2a9ada48017cd1ca14add72e
PiperOrigin-RevId: 775433411
2025-06-24 16:44:34 -07:00
Shangjie Chen 88a4402d14 chore: Do not send api request when session does not have events
PiperOrigin-RevId: 775423356
2025-06-24 16:15:23 -07:00
Dave Bunten 09f1269bf7 ci(tests): leverage official uv action for install
Merge https://github.com/google/adk-python/pull/1547

This PR replaces the `curl`-based installation of `uv` to instead use the [official GitHub Action from Astral](https://github.com/astral-sh/setup-uv).

Closes #1545

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/1547 from d33bs:use-uv-action 05ab7a138cbb5babee30ea81e83f26064e041529
PiperOrigin-RevId: 775402484
2025-06-24 15:18:18 -07:00
Google Team Member 9e473e0abd fix: Include current turn context when include_contents='none'
The intended behavior for include_contents='none' is to:
- Exclude conversation history from previous turns
- Still include current turn context (user input, tool calls/responses within current turn)

https://google.github.io/adk-docs/agents/llm-agents/#managing-context-include_contents

This resolves https://github.com/google/adk-python/issues/1124

PiperOrigin-RevId: 775400036
2025-06-24 15:11:33 -07:00
Xiang (Sean) Zhou acbdca0d84 fix: Make raw_auth_credential and exchanged_auth_credential optional given their default value is None
PiperOrigin-RevId: 775397286
2025-06-24 15:03:54 -07:00
Google Team Member ed7a21e189 chore: Update google-genai package and related deps to latest
PiperOrigin-RevId: 775394737
2025-06-24 14:57:56 -07:00
Liang Wu a1e1441115 fix: update contributing links
Merge https://github.com/google/adk-python/pull/1528

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/1528 from google:doc ec8325e126aba7257de73ab26d8d3a30064859b4
PiperOrigin-RevId: 775383121
2025-06-24 14:27:25 -07:00
Genquan Duan f33e0903b2 feat: Add ADK examples for litellm with add_function_to_prompt
Add examples for for https://github.com/google/adk-python/issues/1273

PiperOrigin-RevId: 775352677
2025-06-24 13:08:44 -07:00
Shangjie Chen abc89d2c81 feat: Add implementation of VertexAiMemoryBankService and support in FastAPI endpoint
PiperOrigin-RevId: 775327151
2025-06-24 11:57:03 -07:00
Google Team Member 00cc8cd643 feat: Add Vertex Express mode compatibility for VertexAiSessionService
PiperOrigin-RevId: 775317848
2025-06-24 11:33:37 -07:00
Joseph Pagadora 9597a446fd feat: Add rouge_score library to ADK eval dependencies, and implement RougeEvaluator that is computes ROUGE-1 for "response_match_score" metric
PiperOrigin-RevId: 774949712
2025-06-23 15:31:03 -07:00
Joseph Pagadora fa025d7559 feat: Add a new option eval_storage_uri in adk web & adk eval to specify GCS bucket to store eval data
PiperOrigin-RevId: 774947795
2025-06-23 15:24:57 -07:00
Xiang (Sean) Zhou 120cbabeb2 refactor: Rename long util function name in runner.py and move it to functions.py
PiperOrigin-RevId: 774880990
2025-06-23 12:24:10 -07:00
Xiang (Sean) Zhou 29cd183aa1 chore: Add credential service backed by session state
PiperOrigin-RevId: 774878336
2025-06-23 12:16:03 -07:00
avidelatm bd67e8480f fix: make LiteLLM streaming truly asynchronous
Merge https://github.com/google/adk-python/pull/1451

## Description
Fixes https://github.com/google/adk-python/issues/1306 by using `async for` with `await self.llm_client.acompletion()` instead of synchronous `for` loop.

## Changes
- Updated test mocks to properly handle async streaming by creating an async generator
- Ensured proper parameter handling to avoid duplicate stream parameter

## Testing Plan
- All unit tests now pass with the async streaming implementation
- Verified with `pytest tests/unittests/models/test_litellm.py` that all streaming tests pass
- Manually tested with a sample agent using LiteLLM to confirm streaming works properly

# Test Evidence:
https://youtu.be/hSp3otI79DM

Let me know if you need anything else from me for this PR

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/1451 from avidelatm:fix/litellm-async-streaming d35b9dc90b2fd6fad44c3869de0fda2514e50055
PiperOrigin-RevId: 774835130
2025-06-23 10:30:12 -07:00
Keisuke Oohashi ea69c9093a feat: add usage span attributes to telemetry (#356)
Merge https://github.com/google/adk-python/pull/1079

Fixes part of #356

Add usage attributes to span.
Note: Since the handling of GenAI event bodies in OpenTelemetry has not yet been determined, I have temporarily added only attributes related to usage.
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/1079 from soundTricker:feature/356-support-more-opentelemetry-semantics 99a9d0352b4bca165baa645440e39ce7199f072b
PiperOrigin-RevId: 774834279
2025-06-23 10:28:14 -07:00
Shangjie Chen f033e405c1 chore: Clarify the behavior of Event.invocation_id
PiperOrigin-RevId: 774827874
2025-06-23 10:12:16 -07:00
bck-ob-gh 3b1d9a8a3e fix: Use starred tuple unpacking on GCS artifact blob names
Merges https://github.com/google/adk-python/pull/1471

Fixes google#1436

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/1471 from bck-ob-gh:main 4c4f2b66ab1e6fde8b1a9d2b914dcb24040db144
PiperOrigin-RevId: 774809270
2025-06-23 09:24:31 -07:00
Xiang (Sean) Zhou 7c670f638b chore: Send user message to the agent that returned a corresponding function call if user message is a function response
PiperOrigin-RevId: 773895971
2025-06-20 17:15:29 -07:00
Xiang (Sean) Zhou fb13963ded chore: Add request converter to convert a2a request to ADK request
PiperOrigin-RevId: 773894462
2025-06-20 17:09:05 -07:00
Shangjie Chen 2fd8feb65d chore: Support allow_origins in cloud_run deployment
Also reorganize the fast_api_common_options. This resolves https://github.com/google/adk-python/issues/1444.

PiperOrigin-RevId: 773890111
2025-06-20 16:54:08 -07:00
Shangjie Chen 8677d5c8dc chore: bump version number to 1.4.2
PiperOrigin-RevId: 773867075
2025-06-20 15:41:50 -07:00
Shangjie Chen 4d72d31b13 fix: Add type checking to handle different response type of genai API client
Fixes https://github.com/google/adk-python/issues/1514

PiperOrigin-RevId: 773838035
2025-06-20 14:12:55 -07:00
Xiang (Sean) Zhou 742478fdb7 chore: Add event converters to convert adk event to a2a event (WIP)
PiperOrigin-RevId: 773795427
2025-06-20 12:14:12 -07:00
Xiang (Sean) Zhou ffcba70686 chore: skip mcp and a2a tests for python 3.9
PiperOrigin-RevId: 773785385
2025-06-20 11:46:31 -07:00
Google Team Member 2f716ada7f fix: Allow more credentials types for BigQuery tools
This change accepts the `google.auth.credentials.Credentials` type for `BigQueryCredentialsConfig`, so any subclass of that, including `google.oauth2.credentials.Credentials` would work to integrate with BigQuery service. This opens up a whole range of possibilities, such as using service account credentials to deploy an agent using these tools.

PiperOrigin-RevId: 773190440
2025-06-18 22:02:09 -07:00
Shangjie Chen 17beb32880 chore: Bump version number and update changelog for 1.4.1 release
PiperOrigin-RevId: 773148349
2025-06-18 19:08:43 -07:00
Xiang (Sean) Zhou 7f8dc8927a chore: fix the mcp_sse_agent
PiperOrigin-RevId: 773140021
2025-06-18 18:32:06 -07:00
Xiang (Sean) Zhou 9a1115c504 chore: Remove service account support
given it was not correctly supported.

PiperOrigin-RevId: 773137317
2025-06-18 18:19:05 -07:00
Xiang (Sean) Zhou 913d771d6d chore: Raise meaningful errors when importing a2a modules for python 3.9
PiperOrigin-RevId: 773128206
2025-06-18 17:43:11 -07:00
Hangfei Lin 58e07cae83 fix: Fix tracing for live
the original code passed in wrong args. now fixed.
tested locally.

PiperOrigin-RevId: 773108589
2025-06-18 16:35:16 -07:00
Xiang (Sean) Zhou 157d9be88d feat: Enable MCP Tool Auth (Experimental)
PiperOrigin-RevId: 773002759
2025-06-18 11:44:02 -07:00
Xiang (Sean) Zhou 18a541c8fa chore: Ignore mcp_tool ut tests for python 3.9 given mcp sdk only supports 3.10+
PiperOrigin-RevId: 772999037
2025-06-18 11:34:10 -07:00
Xiang (Sean) Zhou dcea7767c6 feat: Add Authenticated Tool (Experimental)
PiperOrigin-RevId: 772992074
2025-06-18 11:17:01 -07:00
Xiang (Sean) Zhou 2c739ab581 chore: Add Credential Manager for managing tools credential (Experimental)
PiperOrigin-RevId: 772986051
2025-06-18 11:01:38 -07:00
Xiang (Sean) Zhou 9a207cb832 refactor: Refactor oauth2_credential_exchanger to exchanger and refresher separately
PiperOrigin-RevId: 772979993
2025-06-18 10:46:39 -07:00
Xiang (Sean) Zhou a17ebe6ebd chore: Add a credential refresher registry
PiperOrigin-RevId: 772747251
2025-06-17 21:12:30 -07:00
Xiang (Sean) Zhou 55201cb6a1 chore: Add credential exchanger registry (Experimentals)
PiperOrigin-RevId: 772713412
2025-06-17 19:09:21 -07:00
Xiang (Sean) Zhou 0a9625317a refactor: Adapt service account credential exchanger to base credential exchanger interface
PiperOrigin-RevId: 772710438
2025-06-17 18:55:46 -07:00
Google Team Member f9fa7841df chore: add google-adk/{version} to bigquery user agent
PiperOrigin-RevId: 772703504
2025-06-17 18:31:34 -07:00
Xiang (Sean) Zhou 5f89a469ec chore: Add credential service to runner and invocation context
PiperOrigin-RevId: 772697298
2025-06-17 18:08:43 -07:00
Google Team Member 6d174eba30 fix: Set explicit project in the BigQuery client
This change sets an explicit project id in the BigQuery client from the conversation context. Without this the client was trying to set a project from the environment's application default credentials and running into issues where application default credentials is not available.

PiperOrigin-RevId: 772695883
2025-06-17 18:04:41 -07:00
Xiang (Sean) Zhou c04adaade1 chore: Add in memory credential service (Experimental)
PiperOrigin-RevId: 772534962
2025-06-17 10:45:49 -07:00
Google Team Member 1ae176ad2f fix: update conversion between Celsius and Fahrenheit
#non-breaking
The correct conversion from 25 degrees Celsius is 77 degrees Fahrenheit. The previous value of 41 was wrong.

PiperOrigin-RevId: 772528757
2025-06-17 10:31:36 -07:00
SimonWei 694b71256c fix: agent generate config error (#1450) 2025-06-17 08:48:00 -07:00
Xiang (Sean) Zhou e1812797ad chore: Add A2A Part converter (WIP)
PiperOrigin-RevId: 772282116
2025-06-16 20:35:09 -07:00
Xiang (Sean) Zhou c755cf23c5 chore: Ignore a2a ut tests for python 3.9 given a2a-sdk only supports 3.10+
PiperOrigin-RevId: 772270172
2025-06-16 19:53:46 -07:00
Xiang (Sean) Zhou 94caccc148 refactor: Extract util method from OAuth2 credential fetcher for reuse
Context: we'd like to separate fetcher into exchanger and refresher later. This cl help to extract the common utility that will be used by both exchanger and refresher.

PiperOrigin-RevId: 772257995
2025-06-16 19:03:16 -07:00
Xiang (Sean) Zhou 476805d5b9 chore: Add a2a extra dependency for github UT workflows
PiperOrigin-RevId: 772251530
2025-06-16 18:34:18 -07:00
Xiang (Sean) Zhou e2a81365ec chore: Add a base credential refresher interface
PiperOrigin-RevId: 772248299
2025-06-16 18:21:35 -07:00
Xiang (Sean) Zhou 28dfcd2512 chore: Add experimental decorator to Oauth2 credential fethcer
PiperOrigin-RevId: 772236406
2025-06-16 17:36:48 -07:00
Xiang (Sean) Zhou a6b1baa61b chore: Add base credential exchanger (Experimental)
PiperOrigin-RevId: 772227201
2025-06-16 17:05:20 -07:00
Liang Wu e384fa4ad7 chore: fix previously skipped isort issue
PiperOrigin-RevId: 772224853
2025-06-16 16:57:46 -07:00
Google Team Member aafa80bd85 fix: stream in litellm + adk and add corresponding integration tests
Fixes https://github.com/google/adk-python/issues/1368

PiperOrigin-RevId: 772218385
2025-06-16 16:37:14 -07:00
Xiang (Sean) Zhou 4bda245171 chore: fix oauth_calendar_agent example
PiperOrigin-RevId: 772214855
2025-06-16 16:26:59 -07:00
Xiang (Sean) Zhou 31b81a342d chore: Update streamable http mcp example agent
PiperOrigin-RevId: 772213323
2025-06-16 16:22:09 -07:00
Google Team Member fef8778429 fix: liteLLM test failures
Fix liteLLM test failures for function call responses.

PiperOrigin-RevId: 772212629
2025-06-16 16:20:23 -07:00
Hangfei Lin fe1d5aa439 feat: add enable_affective_dialog and proactivity to run_config and llm_request
PiperOrigin-RevId: 772175206
2025-06-16 14:35:47 -07:00
Hangfei Lin 8201f9aebd chore: Added live-streaming sample agent
Also added a readme.

PiperOrigin-RevId: 772120698
2025-06-16 12:07:09 -07:00
Xuan Yang 1cfc555e70 ADK changes
PiperOrigin-RevId: 772078053
2025-06-16 10:20:23 -07:00
SimonWei badbcbd7a4 fix: agent generate config err (#1305)
* fix: agent generate config err

* fix: resovle comment

---------

Co-authored-by: Hangfei Lin <hangfei@google.com>
Co-authored-by: genquan9 <49327371+genquan9@users.noreply.github.com>
2025-06-16 09:40:41 -07:00
Google Team Member 675faefc67 feat: Allow data_store_specs pass into ADK VAIS built-in tool
PiperOrigin-RevId: 772039465
2025-06-16 08:35:30 -07:00
Xiang (Sean) Zhou a4d432a9e6 chore: Add Service Account Credential Exchanger (Experimental)
PiperOrigin-RevId: 771507089
2025-06-14 13:39:50 -07:00
Xiang (Sean) Zhou d1bda9d946 chore: Allow working_in_progress feature for unittests
PiperOrigin-RevId: 771500394
2025-06-14 12:56:02 -07:00
Xiang (Sean) Zhou a19d617ed8 chore: Add experimental decorator to BigQuery tools
PiperOrigin-RevId: 771359886
2025-06-13 23:32:33 -07:00
Xiang (Sean) Zhou 8ebf229c47 chore: Add base credential service interface (WIP)
PiperOrigin-RevId: 771358480
2025-06-13 23:24:30 -07:00
Xiang (Sean) Zhou b51a1f45fd chore: support @working_in_progress and @expremental without calling parameters
PiperOrigin-RevId: 771357728
2025-06-13 23:18:43 -07:00
Xiang (Sean) Zhou 8932106246 chore: Raise error when using features decorated by working_in_progress decorator
set environment variable ADK_ALLOW_WIP_FEATURES=true can bypass it.
working_in_progress features are not working. ADK users are not supposed to set this environment variable.

PiperOrigin-RevId: 771333335
2025-06-13 21:17:31 -07:00
Google Team Member 233fd20243 feat: Add import session API in the fast API
PiperOrigin-RevId: 771279971
2025-06-13 17:26:08 -07:00
Xiang (Sean) Zhou d129fd636b chore: Update the comments of MCPTool
PiperOrigin-RevId: 771234262
2025-06-13 14:53:41 -07:00
Selcuk Gun 131957c531 chore: Triaging agent improvements & github workflow
* modified list issues to only return unlabelled open issues
* added github workflow to run on schedule and issue open/reopen
* interactive/workflow modes
* readme document

PiperOrigin-RevId: 771152306
2025-06-13 10:56:32 -07:00
Google Team Member 8e285874da feat: Add integration tests for litellm with and without turn on add_function_to_prompt
Add experiments for https://github.com/google/adk-python/issues/1273

PiperOrigin-RevId: 771145715
2025-06-13 10:37:09 -07:00
Google Team Member cb55970969 refactor: Simplify agent_tool.py
PiperOrigin-RevId: 771135917
2025-06-13 10:10:31 -07:00
Xiang (Sean) Zhou 40b15ad278 refactor: enhance mcp tool session management
1. remove unnecessary cached session instance in mcp toolset
2. move session reinitialization logic from mcp tool and mcp toolset to mcp session manager
3. add lock for the code block of session creation to avoid race conditions

PiperOrigin-RevId: 770949529
2025-06-12 23:11:06 -07:00
Xiang (Sean) Zhou dbdeb49090 chore: Add a2a-sdk to pyproject.toml
PiperOrigin-RevId: 770908052
2025-06-12 20:30:14 -07:00
Xiang (Sean) Zhou 177980106b feat: Support refresh access token automatically for rest_api_tool
1. let auth_handler.py to utilize the oauth2 credential fetcher to exchange token
2. restructure tool_auth_handler.py to support refresh token

PiperOrigin-RevId: 770901469
2025-06-12 20:07:43 -07:00
Xiang (Sean) Zhou c5b063f1ff feat: Add an Oauth2 credential fetcher to exchange and refresh oauth2 token
PiperOrigin-RevId: 770892243
2025-06-12 19:40:16 -07:00
Google Team Member 313f1b0913 chore: Add all missing direct deps to pyproject.toml
PiperOrigin-RevId: 770887371
2025-06-12 19:21:13 -07:00
Google Team Member b2fc7740b3 fix: Support project-based gemini model path to use google_search_tool
PiperOrigin-RevId: 770858301
2025-06-12 17:30:45 -07:00
Xiang (Sean) Zhou 29e4ca9152 chore: Add empty a2a package
PiperOrigin-RevId: 770851838
2025-06-12 17:07:05 -07:00
Xiang (Sean) Zhou 4ccda99e8e fix: Merge custom http options with adk specific http options in model api request
PiperOrigin-RevId: 770836112
2025-06-12 16:20:31 -07:00
GenkiNoguchi d22920bd7f feat: support realtime input config
Merge https://github.com/google/adk-python/pull/981

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

This pull request introduces a new configuration option, `realtime_input_config`, to the `RunConfig` class.

**Reason for this change:**

Currently, there is no direct way to configure real-time audio input behaviors, such as Voice Activity Detection (VAD), for live agents through the `RunConfig`. The Gemini API documentation (specifically [Configure automatic VAD](https://ai.google.dev/gemini-api/docs/live#configure-automatic-vad)) outlines parameters for VAD that users may want to customize.

This change enables users to pass these real-time input configurations, providing more granular control over the audio input for live agents.

**Changes made:**

- Added a new optional field `realtime_input_config: Optional[types.RealtimeInputConfig]` to the `RunConfig` class.
- The docstring for `realtime_input_config` has been added to explain its purpose.

**Example Usage (Conceptual):**

While the specific structure of `types.RealtimeInputConfig` would define the exact parameters, a user might configure it like this:

```python
# (Assuming types.RealtimeInputConfig and types.VadConfig are defined elsewhere)
# import your_project.types as types

run_config = RunConfig(
    # ... other configurations ...
    realtime_input_config=types.RealtimeInputConfig(
        automatic_activity_detection =types.AutomaticActivityDetection(
            # VAD specific parameters like sensitivity, endpoint_duration_millis etc.
            # based on https://ai.google.dev/gemini-api/docs/live#configure-automatic-vad
        )
        # Potentially other real-time input settings could be added here in the future
    )
)
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/981 from ammmr:patch-add-realtime-input-config b2e17fbf5742d264029ad49bf632422b5c5b1e0a
PiperOrigin-RevId: 770797640
2025-06-12 14:33:05 -07:00
Google Team Member 2ff9b1f639 test: Add unit tests for execute_sql tool
This change introduces unit tests in which the behavior of the tool is asserted for various query types in various write modes through a mocked BigQuery client.

PiperOrigin-RevId: 770653117
2025-06-12 07:52:41 -07:00
Google Team Member 0a5cf45a75 feat: Implement GcsEvalSetResultsManager to handle storage of eval sets on GCS, and refactor eval set results manager
Eval results will be stored as json files under `gs://{bucket_name}/{app_name}/evals/eval_history/`

PiperOrigin-RevId: 770499242
2025-06-11 23:41:54 -07:00
Google Team Member 1551bd4f4d feat: Re-factor some eval sets manager logic, and implement GcsEvalSetsManager to handle storage of eval sets on GCS
Eval sets will be stored as json files under `gs://{bucket_name}/{app_name}/evals/eval_sets/`

PiperOrigin-RevId: 770487129
2025-06-11 23:02:53 -07:00
Koichi Shiraishi bbceb4f2e8 fix: remove unnecessary double quote on Claude docstring
Merge https://github.com/google/adk-python/pull/1266

Subject says it all.

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/1266 from zchee:fix-docstring b5ceacc82b45398e9421c0f9a1a4d6352d12e21a
PiperOrigin-RevId: 770478088
2025-06-11 22:33:07 -07:00
Wei Sun (Jack) b08bdbcd7f chore: Fixes the sample of example_tool
The `Example` objects should be strong typed.

PiperOrigin-RevId: 770476132
2025-06-11 22:27:25 -07:00
Shangjie Chen fc65873d7c chore: Set agent_engine_id in the service constructor, also use the agent_engine_id field instead of overriding app_name in FastAPI endpoint
PiperOrigin-RevId: 770427903
2025-06-11 19:46:25 -07:00
Wei Sun (Jack) a7ea374dfb chore: Update isort config to prevent vscode flickering
PiperOrigin-RevId: 770406033
2025-06-11 18:25:35 -07:00
169 changed files with 15708 additions and 1031 deletions
+3
View File
@@ -31,5 +31,8 @@ If applicable, add screenshots to help explain your problem.
- Python version(python -V):
- ADK version(pip show google-adk):
**Model Information:**
For example, which model is being used.
**Additional context**
Add any other context about the problem here.
+14 -6
View File
@@ -36,18 +36,26 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Install the latest version of uv
uses: astral-sh/setup-uv@v6
- name: Install dependencies
run: |
uv venv .venv
source .venv/bin/activate
uv sync --extra test --extra eval
uv sync --extra test --extra eval --extra a2a
- name: Run unit tests with pytest
run: |
source .venv/bin/activate
pytest tests/unittests \
--ignore=tests/unittests/artifacts/test_artifact_service.py \
--ignore=tests/unittests/tools/google_api_tool/test_googleapi_to_openapi_converter.py
if [[ "${{ matrix.python-version }}" == "3.9" ]]; then
pytest tests/unittests \
--ignore=tests/unittests/a2a \
--ignore=tests/unittests/tools/mcp_tool \
--ignore=tests/unittests/artifacts/test_artifact_service.py \
--ignore=tests/unittests/tools/google_api_tool/test_googleapi_to_openapi_converter.py
else
pytest tests/unittests \
--ignore=tests/unittests/artifacts/test_artifact_service.py \
--ignore=tests/unittests/tools/google_api_tool/test_googleapi_to_openapi_converter.py
fi
+43
View File
@@ -0,0 +1,43 @@
name: ADK Issue Triaging Agent
on:
issues:
types: [opened, reopened]
schedule:
- cron: '0 */6 * * *' # every 6h
jobs:
agent-triage-issues:
runs-on: ubuntu-latest
permissions:
issues: 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'
INTERACTIVE: 0
EVENT_NAME: ${{ github.event_name }} # 'issues', 'schedule', etc.
ISSUE_NUMBER: ${{ github.event.issue.number }}
ISSUE_TITLE: ${{ github.event.issue.title }}
ISSUE_BODY: ${{ github.event.issue.body }}
ISSUE_COUNT_TO_PROCESS: '3' # Process 3 issues at a time on schedule
run: python contributing/samples/adk_triaging_agent/main.py
+1
View File
@@ -82,6 +82,7 @@ log/
.env.development.local
.env.test.local
.env.production.local
uv.lock
# Google Cloud specific
.gcloudignore
+79
View File
@@ -1,5 +1,84 @@
# Changelog
## [1.5.0](https://github.com/google/adk-python/compare/v1.4.2...v1.5.0) (2025-06-25)
### Features
* Add a new option `eval_storage_uri` in adk web & adk eval to specify GCS bucket to store eval data ([fa025d7](https://github.com/google/adk-python/commit/fa025d755978e1506fa0da1fecc49775bebc1045))
* Add ADK examples for litellm with add_function_to_prompt ([f33e090](https://github.com/google/adk-python/commit/f33e0903b21b752168db3006dd034d7d43f7e84d))
* Add implementation of VertexAiMemoryBankService and support in FastAPI endpoint ([abc89d2](https://github.com/google/adk-python/commit/abc89d2c811ba00805f81b27a3a07d56bdf55a0b))
* Add rouge_score library to ADK eval dependencies, and implement RougeEvaluator that is computes ROUGE-1 for "response_match_score" metric ([9597a44](https://github.com/google/adk-python/commit/9597a446fdec63ad9e4c2692d6966b14f80ff8e2))
* Add usage span attributes to telemetry ([#356](https://github.com/google/adk-python/issues/356)) ([ea69c90](https://github.com/google/adk-python/commit/ea69c9093a16489afdf72657136c96f61c69cafd))
* Add Vertex Express mode compatibility for VertexAiSessionService ([00cc8cd](https://github.com/google/adk-python/commit/00cc8cd6433fc45ecfc2dbaa04dbbc1a81213b4d))
### Bug Fixes
* Include current turn context when include_contents='none' ([9e473e0](https://github.com/google/adk-python/commit/9e473e0abdded24e710fd857782356c15d04b515))
* Make LiteLLM streaming truly asynchronous ([bd67e84](https://github.com/google/adk-python/commit/bd67e8480f6e8b4b0f8c22b94f15a8cda1336339))
* Make raw_auth_credential and exchanged_auth_credential optional given their default value is None ([acbdca0](https://github.com/google/adk-python/commit/acbdca0d8400e292ba5525931175e0d6feab15f1))
* Minor typo fix in the agent instruction ([ef3c745](https://github.com/google/adk-python/commit/ef3c745d655538ebd1ed735671be615f842341a8))
* Typo fix in sample agent instruction ([ef3c745](https://github.com/google/adk-python/commit/ef3c745d655538ebd1ed735671be615f842341a8))
* Update contributing links ([a1e1441](https://github.com/google/adk-python/commit/a1e14411159fd9f3e114e15b39b4949d0fd6ecb1))
* Use starred tuple unpacking on GCS artifact blob names ([3b1d9a8](https://github.com/google/adk-python/commit/3b1d9a8a3e631ca2d86d30f09640497f1728986c))
### Chore
* Do not send api request when session does not have events ([88a4402](https://github.com/google/adk-python/commit/88a4402d142672171d0a8ceae74671f47fa14289))
* Leverage official uv action for install([09f1269](https://github.com/google/adk-python/commit/09f1269bf7fa46ab4b9324e7f92b4f70ffc923e5))
* Update google-genai package and related deps to latest([ed7a21e](https://github.com/google/adk-python/commit/ed7a21e1890466fcdf04f7025775305dc71f603d))
* Add credential service backed by session state([29cd183](https://github.com/google/adk-python/commit/29cd183aa1b47dc4f5d8afe22f410f8546634abc))
* Clarify the behavior of Event.invocation_id([f033e40](https://github.com/google/adk-python/commit/f033e405c10ff8d86550d1419a9d63c0099182f9))
* Send user message to the agent that returned a corresponding function call if user message is a function response([7c670f6](https://github.com/google/adk-python/commit/7c670f638bc17374ceb08740bdd057e55c9c2e12))
* Add request converter to convert a2a request to ADK request([fb13963](https://github.com/google/adk-python/commit/fb13963deda0ff0650ac27771711ea0411474bf5))
* Support allow_origins in cloud_run deployment ([2fd8feb](https://github.com/google/adk-python/commit/2fd8feb65d6ae59732fb3ec0652d5650f47132cc))
## [1.4.2](https://github.com/google/adk-python/compare/v1.4.1...v1.4.2) (2025-06-20)
### Bug Fixes
* Add type checking to handle different response type of genai API client ([4d72d31](https://github.com/google/adk-python/commit/4d72d31b13f352245baa72b78502206dcbe25406))
* This fixes the broken VertexAiSessionService
* Allow more credentials types for BigQuery tools ([2f716ad](https://github.com/google/adk-python/commit/2f716ada7fbcf8e03ff5ae16ce26a80ca6fd7bf6))
## [1.4.1](https://github.com/google/adk-python/compare/v1.3.0...v1.4.1) (2025-06-18)
### Features
* Add Authenticated Tool (Experimental) ([dcea776](https://github.com/google/adk-python/commit/dcea7767c67c7edfb694304df32dca10b74c9a71))
* Add enable_affective_dialog and proactivity to run_config and llm_request ([fe1d5aa](https://github.com/google/adk-python/commit/fe1d5aa439cc56b89d248a52556c0a9b4cbd15e4))
* Add import session API in the fast API ([233fd20](https://github.com/google/adk-python/commit/233fd2024346abd7f89a16c444de0cf26da5c1a1))
* Add integration tests for litellm with and without turning on add_function_to_prompt ([8e28587](https://github.com/google/adk-python/commit/8e285874da7f5188ea228eb4d7262dbb33b1ae6f))
* Allow data_store_specs pass into ADK VAIS built-in tool ([675faef](https://github.com/google/adk-python/commit/675faefc670b5cd41991939fe0fc604df331111a))
* Enable MCP Tool Auth (Experimental) ([157d9be](https://github.com/google/adk-python/commit/157d9be88d92f22320604832e5a334a6eb81e4af))
* Implement GcsEvalSetResultsManager to handle storage of eval sets on GCS, and refactor eval set results manager ([0a5cf45](https://github.com/google/adk-python/commit/0a5cf45a75aca7b0322136b65ca5504a0c3c7362))
* Re-factor some eval sets manager logic, and implement GcsEvalSetsManager to handle storage of eval sets on GCS ([1551bd4](https://github.com/google/adk-python/commit/1551bd4f4d7042fffb497d9308b05f92d45d818f))
* Support real time input config ([d22920b](https://github.com/google/adk-python/commit/d22920bd7f827461afd649601326b0c58aea6716))
* Support refresh access token automatically for rest_api_tool ([1779801](https://github.com/google/adk-python/commit/177980106b2f7be9a8c0a02f395ff0f85faa0c5a))
### Bug Fixes
* Fix Agent generate config err ([#1305](https://github.com/google/adk-python/issues/1305)) ([badbcbd](https://github.com/google/adk-python/commit/badbcbd7a464e6b323cf3164d2bcd4e27cbc057f))
* Fix Agent generate config error ([#1450](https://github.com/google/adk-python/issues/1450)) ([694b712](https://github.com/google/adk-python/commit/694b71256c631d44bb4c4488279ea91d82f43e26))
* Fix liteLLM test failures ([fef8778](https://github.com/google/adk-python/commit/fef87784297b806914de307f48c51d83f977298f))
* Fix tracing for live ([58e07ca](https://github.com/google/adk-python/commit/58e07cae83048d5213d822be5197a96be9ce2950))
* Merge custom http options with adk specific http options in model api request ([4ccda99](https://github.com/google/adk-python/commit/4ccda99e8ec7aa715399b4b83c3f101c299a95e8))
* Remove unnecessary double quote on Claude docstring ([bbceb4f](https://github.com/google/adk-python/commit/bbceb4f2e89f720533b99cf356c532024a120dc4))
* Set explicit project in the BigQuery client ([6d174eb](https://github.com/google/adk-python/commit/6d174eba305a51fcf2122c0fd481378752d690ef))
* Support streaming in litellm + adk and add corresponding integration tests ([aafa80b](https://github.com/google/adk-python/commit/aafa80bd85a49fb1c1a255ac797587cffd3fa567))
* Support project-based gemini model path to use google_search_tool ([b2fc774](https://github.com/google/adk-python/commit/b2fc7740b363a4e33ec99c7377f396f5cee40b5a))
* Update conversion between Celsius and Fahrenheit ([1ae176a](https://github.com/google/adk-python/commit/1ae176ad2fa2b691714ac979aec21f1cf7d35e45))
### Chores
* Set `agent_engine_id` in the VertexAiSessionService constructor, also use the `agent_engine_id` field instead of overriding `app_name` in FastAPI endpoint ([fc65873](https://github.com/google/adk-python/commit/fc65873d7c31be607f6cd6690f142a031631582a))
## [1.3.0](https://github.com/google/adk-python/compare/v1.2.1...v1.3.0) (2025-06-11)
+1 -1
View File
@@ -200,7 +200,7 @@ For any changes that impact user-facing documentation (guides, API reference, tu
## Contributing Resources
[Contributing folder](https://github.com/google/adk-python/tree/main/contributing/samples) has resources that is helpful for contributors.
[Contributing folder](https://github.com/google/adk-python/tree/main/contributing) has resources that is helpful for contributors.
## Code reviews
+1 -1
View File
@@ -135,7 +135,7 @@ adk eval \
## 🤝 Contributing
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).
- [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.
## đź“„ License
@@ -0,0 +1,241 @@
# 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_issue_formatting_agent.settings import GITHUB_BASE_URL
from adk_issue_formatting_agent.settings import IS_INTERACTIVE
from adk_issue_formatting_agent.settings import OWNER
from adk_issue_formatting_agent.settings import REPO
from adk_issue_formatting_agent.utils import error_response
from adk_issue_formatting_agent.utils import get_request
from adk_issue_formatting_agent.utils import post_request
from adk_issue_formatting_agent.utils import read_file
from google.adk import Agent
import requests
BUG_REPORT_TEMPLATE = read_file(
Path(__file__).parent / "../../../../.github/ISSUE_TEMPLATE/bug_report.md"
)
FREATURE_REQUEST_TEMPLATE = read_file(
Path(__file__).parent
/ "../../../../.github/ISSUE_TEMPLATE/feature_request.md"
)
APPROVAL_INSTRUCTION = (
"**Do not** wait or ask for user approval or confirmation for adding the"
" comment."
)
if IS_INTERACTIVE:
APPROVAL_INSTRUCTION = (
"Ask for user approval or confirmation for adding the comment."
)
def list_open_issues(issue_count: int) -> dict[str, Any]:
"""List most recent `issue_count` numer of open issues in the repo.
Args:
issue_count: number of issues to return
Returns:
The status of this request, with a list of issues when successful.
"""
url = f"{GITHUB_BASE_URL}/search/issues"
query = f"repo:{OWNER}/{REPO} is:open is:issue"
params = {
"q": query,
"sort": "created",
"order": "desc",
"per_page": issue_count,
"page": 1,
}
try:
response = get_request(url, params)
except requests.exceptions.RequestException as e:
return error_response(f"Error: {e}")
issues = response.get("items", None)
return {"status": "success", "issues": issues}
def get_issue(issue_number: int) -> dict[str, Any]:
"""Get the details of the specified issue number.
Args:
issue_number: issue number of the Github issue.
Returns:
The status of this request, with the issue details when successful.
"""
url = f"{GITHUB_BASE_URL}/repos/{OWNER}/{REPO}/issues/{issue_number}"
try:
response = get_request(url)
except requests.exceptions.RequestException as e:
return error_response(f"Error: {e}")
return {"status": "success", "issue": response}
def add_comment_to_issue(issue_number: int, comment: str) -> dict[str, any]:
"""Add the specified comment to the given issue number.
Args:
issue_number: issue number of the Github issue
comment: 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 #{issue_number}")
url = f"{GITHUB_BASE_URL}/repos/{OWNER}/{REPO}/issues/{issue_number}/comments"
payload = {"body": comment}
try:
response = post_request(url, payload)
except requests.exceptions.RequestException as e:
return error_response(f"Error: {e}")
return {
"status": "success",
"added_comment": response,
}
def list_comments_on_issue(issue_number: int) -> dict[str, any]:
"""List all comments on the given issue number.
Args:
issue_number: issue number of the Github issue
Returns:
The the status of this request, with the list of comments when successful.
"""
print(f"Attempting to list comments on issue #{issue_number}")
url = f"{GITHUB_BASE_URL}/repos/{OWNER}/{REPO}/issues/{issue_number}/comments"
try:
response = get_request(url)
except requests.exceptions.RequestException as e:
return error_response(f"Error: {e}")
return {"status": "success", "comments": response}
root_agent = Agent(
model="gemini-2.5-pro",
name="adk_issue_formatting_assistant",
description="Check ADK issue format and content.",
instruction=f"""
# 1. IDENTITY
You are an AI assistant designed to help maintain the quality and consistency of issues in our GitHub repository.
Your primary role is to act as a "GitHub Issue Format Validator." You will analyze new and existing **open** issues
to ensure they contain all the necessary information as required by our templates. You are helpful, polite,
and precise in your feedback.
# 2. CONTEXT & RESOURCES
* **Repository:** You are operating on the GitHub repository `{OWNER}/{REPO}`.
* **Bug Report Template:** (`{BUG_REPORT_TEMPLATE}`)
* **Feature Request Template:** (`{FREATURE_REQUEST_TEMPLATE}`)
# 3. CORE MISSION
Your goal is to check if a GitHub issue, identified as either a "bug" or a "feature request,"
contains all the information required by the corresponding template. If it does not, your job is
to post a single, helpful comment asking the original author to provide the missing information.
{APPROVAL_INSTRUCTION}
**IMPORTANT NOTE:**
* You add one comment at most each time you are invoked.
* Don't proceed to other issues which are not the target issues.
* Don't take any action on closed issues.
# 4. BEHAVIORAL RULES & LOGIC
## Step 1: Identify Issue Type & Applicability
Your first task is to determine if the issue is a valid target for validation.
1. **Assess Content Intent:** You must perform a quick semantic check of the issue's title, body, and comments.
If you determine the issue's content is fundamentally *not* a bug report or a feature request
(for example, it is a general question, a request for help, or a discussion prompt), then you must ignore it.
2. **Exit Condition:** If the issue does not clearly fall into the categories of "bug" or "feature request"
based on both its labels and its content, **take no action**.
## Step 2: Analyze the Issue Content
If you have determined the issue is a valid bug or feature request, your analysis depends on whether it has comments.
**Scenario A: Issue has NO comments**
1. Read the main body of the issue.
2. Compare the content of the issue body against the required headings/sections in the relevant template (Bug or Feature).
3. Check for the presence of content under each heading. A heading with no content below it is considered incomplete.
4. If one or more sections are missing or empty, proceed to Step 3.
5. If all sections are filled out, your task is complete. Do nothing.
**Scenario B: Issue HAS one or more comments**
1. First, analyze the main issue body to see which sections of the template are filled out.
2. Next, read through **all** the comments in chronological order.
3. As you read the comments, check if the information provided in them satisfies any of the template sections that were missing from the original issue body.
4. After analyzing the body and all comments, determine if any required sections from the template *still* remain unaddressed.
5. If one or more sections are still missing information, proceed to Step 3.
6. If the issue body and comments *collectively* provide all the required information, your task is complete. Do nothing.
## Step 3: Formulate and Post a Comment (If Necessary)
If you determined in Step 2 that information is missing, you must post a **single comment** on the issue.
Please include a bolded note in your comment that this comment was added by an ADK agent.
**Comment Guidelines:**
* **Be Polite and Helpful:** Start with a friendly tone.
* **Be Specific:** Clearly list only the sections from the template that are still missing. Do not list sections that have already been filled out.
* **Address the Author:** Mention the issue author by their username (e.g., `@username`).
* **Provide Context:** Explain *why* the information is needed (e.g., "to help us reproduce the bug" or "to better understand your request").
* **Do not be repetitive:** If you have already commented on an issue asking for information, do not comment again unless new information has been added and it's still incomplete.
**Example Comment for a Bug Report:**
> **Response from ADK Agent**
>
> Hello @[issue-author-username], thank you for submitting this issue!
>
> To help us investigate and resolve this bug effectively, could you please provide the missing details for the following sections of our bug report template:
>
> * **To Reproduce:** (Please provide the specific steps required to reproduce the behavior)
> * **Desktop (please complete the following information):** (Please provide OS, Python version, and ADK version)
>
> This information will give us the context we need to move forward. Thanks!
**Example Comment for a Feature Request:**
> **Response from ADK Agent**
>
> Hi @[issue-author-username], thanks for this great suggestion!
>
> To help our team better understand and evaluate your feature request, could you please provide a bit more information on the following section:
>
> * **Is your feature request related to a problem? Please describe.**
>
> We look forward to hearing more about your idea!
# 5. FINAL INSTRUCTION
Execute this process for the given GitHub issue. Your final output should either be **[NO ACTION]**
if the issue is complete or invalid, or **[POST COMMENT]** followed by the exact text of the comment you will post.
Please include your justification for your decision in your output.
""",
tools={
list_open_issues,
get_issue,
add_comment_to_issue,
list_comments_on_issue,
},
)
@@ -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_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")
EVENT_NAME = os.getenv("EVENT_NAME")
ISSUE_NUMBER = os.getenv("ISSUE_NUMBER")
ISSUE_COUNT_TO_PROCESS = os.getenv("ISSUE_COUNT_TO_PROCESS")
IS_INTERACTIVE = os.environ.get("INTERACTIVE", "1").lower() in ["true", "1"]
@@ -0,0 +1,53 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Any
from adk_issue_formatting_agent.settings import GITHUB_TOKEN
import requests
headers = {
"Authorization": f"token {GITHUB_TOKEN}",
"Accept": "application/vnd.github.v3+json",
}
def get_request(
url: str, params: dict[str, Any] | None = None
) -> dict[str, Any]:
if params is None:
params = {}
response = requests.get(url, headers=headers, params=params, timeout=60)
response.raise_for_status()
return response.json()
def post_request(url: str, payload: Any) -> dict[str, Any]:
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]:
return {"status": "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 ""
@@ -0,0 +1,67 @@
# ADK Issue Triaging Assistant
The ADK Issue Triaging Assistant is a Python-based agent designed to help manage and triage GitHub issues for the `google/adk-python` repository. It uses a large language model to analyze new and unlabelled issues, recommend appropriate labels based on a predefined set of rules, and apply them.
This agent can be operated in two distinct modes: an interactive mode for local use or as 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 issues.
### 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 to a GitHub issue.
### 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 issue 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:
1. **Issue Events**: The workflow executes automatically whenever a new issue is `opened` or an existing one is `reopened`.
2. **Scheduled Runs**: The workflow also runs on a recurring schedule (every 6 hours) to process any unlabelled issues that may have been missed.
### Automated Labeling
When running as part of the GitHub workflow, the agent operates non-interactively. It identifies the best label and applies it 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/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 requests
```
### 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 `issues: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.
+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
@@ -13,61 +13,73 @@
# limitations under the License.
import os
import random
import time
from google.adk import Agent
from google.adk.tools.tool_context import ToolContext
from google.genai import types
import requests
# Read the PAT from the environment variable
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") # Ensure you've set this in your shell
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
if not GITHUB_TOKEN:
raise ValueError("GITHUB_TOKEN environment variable not set")
# Repository information
OWNER = "google"
REPO = "adk-python"
OWNER = os.getenv("OWNER", "google")
REPO = os.getenv("REPO", "adk-python")
BOT_LABEL = os.getenv("BOT_LABEL", "bot_triaged")
# Base URL for the GitHub API
BASE_URL = "https://api.github.com"
# Headers including the Authorization header
headers = {
"Authorization": f"token {GITHUB_TOKEN}",
"Accept": "application/vnd.github.v3+json",
}
ALLOWED_LABELS = [
"documentation",
"services",
"question",
"tools",
"eval",
"live",
"models",
"tracing",
"core",
"web",
]
def list_issues(per_page: int):
def is_interactive():
return os.environ.get("INTERACTIVE", "1").lower() in ["true", "1"]
def list_issues(issue_count: int):
"""
Generator to list all issues for the repository by handling pagination.
Args:
per_page: number of pages to return per page.
issue_count: number of issues to return
"""
state = "open"
# only process the 1st page for testing for now
page = 1
results = []
url = ( # :contentReference[oaicite:16]{index=16}
f"{BASE_URL}/repos/{OWNER}/{REPO}/issues"
)
# Warning: let's only handle max 10 issues at a time to avoid bad results
params = {"state": state, "per_page": per_page, "page": page}
response = requests.get(url, headers=headers, params=params)
response.raise_for_status() # :contentReference[oaicite:17]{index=17}
issues = response.json()
query = f"repo:{OWNER}/{REPO} is:open is:issue no:label"
unlabelled_issues = []
url = f"{BASE_URL}/search/issues"
params = {
"q": query,
"sort": "created",
"order": "desc",
"per_page": issue_count,
"page": 1,
}
response = requests.get(url, headers=headers, params=params, timeout=60)
response.raise_for_status()
json_response = response.json()
issues = json_response.get("items", None)
if not issues:
return []
for issue in issues:
# Skip pull requests (issues API returns PRs as well)
if "pull_request" in issue:
continue
results.append(issue)
return results
if not issue.get("labels", None) or len(issue["labels"]) == 0:
unlabelled_issues.append(issue)
return unlabelled_issues
def add_label_to_issue(issue_number: str, label: str):
@@ -78,41 +90,56 @@ def add_label_to_issue(issue_number: str, label: str):
issue_number: issue number of the Github issue, in string foramt.
label: label to assign
"""
print(f"Attempting to add label '{label}' to issue #{issue_number}")
if label not in ALLOWED_LABELS:
error_message = (
f"Error: Label '{label}' is not an allowed label. Will not apply."
)
print(error_message)
return {"status": "error", "message": error_message, "applied_label": None}
url = f"{BASE_URL}/repos/{OWNER}/{REPO}/issues/{issue_number}/labels"
payload = [label]
response = requests.post(url, headers=headers, json=payload)
payload = [label, BOT_LABEL]
response = requests.post(url, headers=headers, json=payload, timeout=60)
response.raise_for_status()
return response.json()
approval_instruction = (
"Only label them when the user approves the labeling!"
if is_interactive()
else (
"Do not ask for user approval for labeling! If you can't find a"
" appropriate labels for the issue, do not label it."
)
)
root_agent = Agent(
model="gemini-2.5-pro-preview-05-06",
name="adk_triaging_assistant",
description="Triage ADK issues.",
instruction="""
You are a Github adk-python repo triaging bot. You will help get issues, and label them.
instruction=f"""
You are a Github adk-python repo triaging bot. You will help get issues, and recommend a label.
IMPORTANT: {approval_instruction}
Here are the rules for labeling:
- If the user is asking about documentation-related questions, label it with "documentation".
- If it's about session, memory services, label it with "services"
- If it's about UI/web, label it with "question"
- If it's about UI/web, label it with "web"
- If the user is asking about a question, label it with "question"
- 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 issue, return the issues to user to decide.
- If you can't find a appropriate labels for the issue, follow the previous instruction that starts with "IMPORTANT:".
Present the followings in an easy to read format highlighting issue number and your label.
- the issue summary in a few sentence
- your label recommendation and justification
""",
tools=[
list_issues,
add_label_to_issue,
],
generate_content_config=types.GenerateContentConfig(
safety_settings=[
types.SafetySetting( # avoid false alarm about rolling dice.
category=types.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
threshold=types.HarmBlockThreshold.OFF,
),
]
),
)
@@ -0,0 +1,164 @@
# 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
import time
import agent
from dotenv import load_dotenv
from google.adk.agents.run_config import RunConfig
from google.adk.runners import InMemoryRunner
from google.adk.sessions import Session
from google.genai import types
import requests
load_dotenv(override=True)
OWNER = os.getenv("OWNER", "google")
REPO = os.getenv("REPO", "adk-python")
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
BASE_URL = "https://api.github.com"
headers = {
"Authorization": f"token {GITHUB_TOKEN}",
"Accept": "application/vnd.github.v3+json",
}
if not GITHUB_TOKEN:
print(
"Warning: GITHUB_TOKEN environment variable not set. API calls might"
" fail."
)
async def fetch_specific_issue_details(issue_number: int):
"""Fetches details for a single issue if it's unlabelled."""
if not GITHUB_TOKEN:
print("Cannot fetch issue details: GITHUB_TOKEN is not set.")
return None
url = f"{BASE_URL}/repos/{OWNER}/{REPO}/issues/{issue_number}"
print(f"Fetching details for specific issue: {url}")
try:
response = requests.get(url, headers=headers, timeout=60)
response.raise_for_status()
issue_data = response.json()
if not issue_data.get("labels") or len(issue_data["labels"]) == 0:
print(f"Issue #{issue_number} is unlabelled. Proceeding.")
return {
"number": issue_data["number"],
"title": issue_data["title"],
"body": issue_data.get("body", ""),
}
else:
print(f"Issue #{issue_number} is already labelled. Skipping.")
return None
except requests.exceptions.RequestException as e:
print(f"Error fetching issue #{issue_number}: {e}")
if hasattr(e, "response") and e.response is not None:
print(f"Response content: {e.response.text}")
return None
async def main():
app_name = "triage_app"
user_id_1 = "triage_user"
runner = InMemoryRunner(
agent=agent.root_agent,
app_name=app_name,
)
session_11 = await runner.session_service.create_session(
app_name=app_name, user_id=user_id_1
)
async def run_agent_prompt(session: Session, prompt_text: str):
content = types.Content(
role="user", parts=[types.Part.from_text(text=prompt_text)]
)
print(f"\n>>>> Agent Prompt: {prompt_text}")
final_agent_response_parts = []
async for event in runner.run_async(
user_id=user_id_1,
session_id=session.id,
new_message=content,
run_config=RunConfig(save_input_blobs_as_artifacts=False),
):
if event.content.parts and event.content.parts[0].text:
print(f"** {event.author} (ADK): {event.content.parts[0].text}")
if event.author == agent.root_agent.name:
final_agent_response_parts.append(event.content.parts[0].text)
print(f"<<<< Agent Final Output: {''.join(final_agent_response_parts)}\n")
event_name = os.getenv("EVENT_NAME")
issue_number_str = os.getenv("ISSUE_NUMBER")
if event_name == "issues" and issue_number_str:
print(f"EVENT: Processing specific issue due to '{event_name}' event.")
try:
issue_number = int(issue_number_str)
specific_issue = await fetch_specific_issue_details(issue_number)
if specific_issue:
prompt = (
f"A new GitHub issue #{specific_issue['number']} has been opened or"
f" reopened. Title: \"{specific_issue['title']}\"\nBody:"
f" \"{specific_issue['body']}\"\n\nBased on the rules, recommend an"
" appropriate label and its justification."
" Then, use the 'add_label_to_issue' tool to apply the label "
"directly to this issue."
f" The issue number is {specific_issue['number']}."
)
await run_agent_prompt(session_11, prompt)
else:
print(
f"No unlabelled issue details found for #{issue_number} or an error"
" occurred. Skipping agent interaction."
)
except ValueError:
print(f"Error: Invalid ISSUE_NUMBER received: {issue_number_str}")
else:
print(f"EVENT: Processing batch of issues (event: {event_name}).")
issue_count_str = os.getenv("ISSUE_COUNT_TO_PROCESS", "3")
try:
num_issues_to_process = int(issue_count_str)
except ValueError:
print(f"Warning: Invalid ISSUE_COUNT_TO_PROCESS. Defaulting to 3.")
num_issues_to_process = 3
prompt = (
f"List the first {num_issues_to_process} unlabelled open issues from"
f" the {OWNER}/{REPO} repository. For each issue, provide a summary,"
" recommend a label with justification, and then use the"
" 'add_label_to_issue' tool to apply the recommended label directly."
)
await run_agent_prompt(session_11, prompt)
if __name__ == "__main__":
start_time = time.time()
print(
"Script start time:",
time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(start_time)),
)
print("------------------------------------")
asyncio.run(main())
end_time = time.time()
print("------------------------------------")
print(
"Script end time:",
time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(end_time)),
)
print("Total script execution time:", f"{end_time - start_time:.2f} seconds")
@@ -31,7 +31,7 @@ root_agent = Agent(
model='gemini-2.0-flash',
name='log_agent',
description='Log user query.',
instruction="""Always log the user query and reploy "kk, I've logged."
instruction="""Always log the user query and reply "kk, I've logged."
""",
tools=[log_query],
generate_content_config=types.GenerateContentConfig(
+19 -4
View File
@@ -40,13 +40,28 @@ would set:
### 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.
user interacting with the agent. The tools are run with these credentials.
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
1. Set `CREDENTIALS_TYPE=None` in `agent.py`
1. Run the agent
### With Service Account Keys
This mode is useful for quick development when the agent builder wants to run
the agent with service account credentials. The tools are run with these
credentials.
1. Create service account key by following https://cloud.google.com/iam/docs/service-account-creds#user-managed-keys.
1. Set `CREDENTIALS_TYPE=AuthCredentialTypes.SERVICE_ACCOUNT` in `agent.py`
1. Download the key file and replace `"service_account_key.json"` with the path
1. Run the agent
### With Interactive OAuth
@@ -72,7 +87,7 @@ type.
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
1. Set `CREDENTIALS_TYPE=AuthCredentialTypes.OAUTH2` in `agent.py` and run the agent
## Sample prompts
+20 -9
View File
@@ -15,24 +15,21 @@
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.tools.bigquery.config import BigQueryToolConfig
from google.adk.tools.bigquery.config import WriteMode
import google.auth
RUN_WITH_ADC = False
# Define an appropriate credential type
CREDENTIALS_TYPE = AuthCredentialTypes.OAUTH2
# Define BigQuery tool config
tool_config = BigQueryToolConfig(write_mode=WriteMode.ALLOWED)
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:
if CREDENTIALS_TYPE == AuthCredentialTypes.OAUTH2:
# Initiaze the tools to do interactive OAuth
# The environment variables OAUTH_CLIENT_ID and OAUTH_CLIENT_SECRET
# must be set
@@ -40,6 +37,20 @@ else:
client_id=os.getenv("OAUTH_CLIENT_ID"),
client_secret=os.getenv("OAUTH_CLIENT_SECRET"),
)
elif CREDENTIALS_TYPE == AuthCredentialTypes.SERVICE_ACCOUNT:
# Initialize the tools to use the credentials in the service account key.
# If this flow is enabled, make sure to replace the file path with your own
# service account key file
# https://cloud.google.com/iam/docs/service-account-creds#user-managed-keys
creds, _ = google.auth.load_credentials_from_file("service_account_key.json")
credentials_config = BigQueryCredentialsConfig(credentials=creds)
else:
# Initialize the tools to use the application default credentials.
# https://cloud.google.com/docs/authentication/provide-credentials-adc
application_default_credentials, _ = google.auth.default()
credentials_config = BigQueryCredentialsConfig(
credentials=application_default_credentials
)
bigquery_toolset = BigQueryToolset(
credentials_config=credentials_config, bigquery_tool_config=tool_config
@@ -49,7 +60,7 @@ bigquery_toolset = BigQueryToolset(
# debug CLI
root_agent = llm_agent.Agent(
model="gemini-2.0-flash",
name="hello_agent",
name="bigquery_agent",
description=(
"Agent to answer questions about BigQuery data and models and execute"
" SQL queries."
@@ -1,45 +1,40 @@
# BigQuery Sample
# Google API Tools Sample
## Introduction
This sample tests and demos the BigQuery support in ADK via two tools:
This sample tests and demos Google API tools available in the
`google.adk.tools.google_api_tool` module. We pick the following BigQuery API
tools for this sample agent:
* 1. bigquery_datasets_list:
1. `bigquery_datasets_list`: List user's datasets.
List user's datasets.
2. `bigquery_datasets_get`: Get a dataset's details.
* 2. bigquery_datasets_get:
Get a dataset's details.
3. `bigquery_datasets_insert`: Create a new dataset.
* 3. bigquery_datasets_insert:
Create a new dataset.
4. `bigquery_tables_list`: List all tables in a dataset.
* 4. bigquery_tables_list:
List all tables in a dataset.
5. `bigquery_tables_get`: Get a table's details.
* 5. bigquery_tables_get:
Get a table's details.
* 6. bigquery_tables_insert:
Insert a new table into a dataset.
6. `bigquery_tables_insert`: Insert a new table into a dataset.
## How to use
* 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.
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.
* 2. Configure your `.env` file to add two variables:
2. Configure your `.env` file to add two variables:
* OAUTH_CLIENT_ID={your client id}
* OAUTH_CLIENT_SECRET={your client secret}
Note: don't create a separate `.env` file , instead put it to the same `.env` file that stores your Vertex AI or Dev ML credentials
* 3. Follow https://developers.google.com/identity/protocols/oauth2/web-server#creatingcred to add http://localhost/dev-ui/ to "Authorized redirect URIs".
3. 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.
* 4. For 1st run, allow popup for localhost in Chrome.
4. For 1st run, allow popup for localhost in Chrome.
## Sample prompt
@@ -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

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