You've already forked adk-python
mirror of
https://github.com/encounter/adk-python.git
synced 2026-07-09 18:19:28 -07:00
Compare commits
108 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8677d5c8dc | |||
| 4d72d31b13 | |||
| 742478fdb7 | |||
| ffcba70686 | |||
| 2f716ada7f | |||
| 17beb32880 | |||
| 7f8dc8927a | |||
| 9a1115c504 | |||
| 913d771d6d | |||
| 58e07cae83 | |||
| 157d9be88d | |||
| 18a541c8fa | |||
| dcea7767c6 | |||
| 2c739ab581 | |||
| 9a207cb832 | |||
| a17ebe6ebd | |||
| 55201cb6a1 | |||
| 0a9625317a | |||
| f9fa7841df | |||
| 5f89a469ec | |||
| 6d174eba30 | |||
| c04adaade1 | |||
| 1ae176ad2f | |||
| 694b71256c | |||
| e1812797ad | |||
| c755cf23c5 | |||
| 94caccc148 | |||
| 476805d5b9 | |||
| e2a81365ec | |||
| 28dfcd2512 | |||
| a6b1baa61b | |||
| e384fa4ad7 | |||
| aafa80bd85 | |||
| 4bda245171 | |||
| 31b81a342d | |||
| fef8778429 | |||
| fe1d5aa439 | |||
| 8201f9aebd | |||
| 1cfc555e70 | |||
| badbcbd7a4 | |||
| 675faefc67 | |||
| a4d432a9e6 | |||
| d1bda9d946 | |||
| a19d617ed8 | |||
| 8ebf229c47 | |||
| b51a1f45fd | |||
| 8932106246 | |||
| 233fd20243 | |||
| d129fd636b | |||
| 131957c531 | |||
| 8e285874da | |||
| cb55970969 | |||
| 40b15ad278 | |||
| dbdeb49090 | |||
| 177980106b | |||
| c5b063f1ff | |||
| 313f1b0913 | |||
| b2fc7740b3 | |||
| 29e4ca9152 | |||
| 4ccda99e8e | |||
| d22920bd7f | |||
| 2ff9b1f639 | |||
| 0a5cf45a75 | |||
| 1551bd4f4d | |||
| bbceb4f2e8 | |||
| b08bdbcd7f | |||
| fc65873d7c | |||
| a7ea374dfb | |||
| f46b73b0cb | |||
| b2f319f440 | |||
| 0c4054200f | |||
| c9e265551a | |||
| 416dc6feed | |||
| 9df3f725bd | |||
| 6c999caa41 | |||
| 77f44a4e45 | |||
| 484b33ef10 | |||
| aaf1f9b930 | |||
| 8e438f2752 | |||
| fa110c22f2 | |||
| 088200072f | |||
| bf47a8bf7d | |||
| cf5d7016a0 | |||
| f38c08b305 | |||
| 3ae6ce10bc | |||
| 13f98c396a | |||
| be7120831a | |||
| 4942f19f3f | |||
| 8488ff052f | |||
| a09781142a | |||
| 309a656f49 | |||
| 9abb8414da | |||
| 3b1f2ae9bf | |||
| 6ed635190c | |||
| d1b058707e | |||
| 54367dcc56 | |||
| fe1de7b103 | |||
| 078ac842d7 | |||
| 4b1c218cbe | |||
| 7dbd8c6a5b | |||
| b7ebb6947f | |||
| 4269cac953 | |||
| 19dbe244e0 | |||
| c224626ae1 | |||
| 1d8d1e0a4e | |||
| 0d232588e6 | |||
| cf689fd528 | |||
| 78bfce4d89 |
@@ -0,0 +1,113 @@
|
||||
# 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.
|
||||
|
||||
name: "Check file contents"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.py'
|
||||
|
||||
jobs:
|
||||
check-file-contents:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: Check for logger pattern in all changed Python files
|
||||
run: |
|
||||
git fetch origin ${{ github.base_ref }}
|
||||
CHANGED_FILES=$(git diff --diff-filter=ACMR --name-only origin/${{ github.base_ref }}...HEAD | grep -E '\.py$' || true)
|
||||
if [ -n "$CHANGED_FILES" ]; then
|
||||
echo "Changed Python files to check:"
|
||||
echo "$CHANGED_FILES"
|
||||
echo ""
|
||||
|
||||
# Check for 'logger = logging.getLogger(__name__)' in changed .py files.
|
||||
# The grep command will exit with a non-zero status code if the pattern is not found.
|
||||
# We invert the exit code with ! so the step succeeds if the pattern is NOT found.
|
||||
set +e
|
||||
FILES_WITH_FORBIDDEN_LOGGER=$(grep -lE 'logger = logging\.getLogger\(__name__\)' $CHANGED_FILES)
|
||||
GREP_EXIT_CODE=$?
|
||||
set -e
|
||||
|
||||
# grep exits with 0 if matches are found, 1 if no matches are found.
|
||||
# A non-zero exit code other than 1 indicates an error.
|
||||
if [ $GREP_EXIT_CODE -eq 0 ]; then
|
||||
echo "❌ Found forbidden use of 'logger = logging.getLogger(__name__)'. Please use 'logger = logging.getLogger('google_adk.' + __name__)' instead."
|
||||
echo "The following files contain the forbidden pattern:"
|
||||
echo "$FILES_WITH_FORBIDDEN_LOGGER"
|
||||
exit 1
|
||||
elif [ $GREP_EXIT_CODE -eq 1 ]; then
|
||||
echo "✅ No instances of 'logger = logging.getLogger(__name__)' found in changed Python files."
|
||||
fi
|
||||
else
|
||||
echo "✅ No relevant Python files found."
|
||||
fi
|
||||
|
||||
- name: Check for import pattern in certain changed Python files
|
||||
run: |
|
||||
git fetch origin ${{ github.base_ref }}
|
||||
CHANGED_FILES=$(git diff --diff-filter=ACMR --name-only origin/${{ github.base_ref }}...HEAD | grep -E '\.py$' | grep -v -E '__init__.py$|version.py$|tests/.*|contributing/samples/' || true)
|
||||
if [ -n "$CHANGED_FILES" ]; then
|
||||
echo "Changed Python files to check:"
|
||||
echo "$CHANGED_FILES"
|
||||
echo ""
|
||||
|
||||
# Use grep -L to find files that DO NOT contain the pattern.
|
||||
# This command will output a list of non-compliant files.
|
||||
FILES_MISSING_IMPORT=$(grep -L 'from __future__ import annotations' $CHANGED_FILES)
|
||||
|
||||
# Check if the list of non-compliant files is empty
|
||||
if [ -z "$FILES_MISSING_IMPORT" ]; then
|
||||
echo "✅ All modified Python files include 'from __future__ import annotations'."
|
||||
exit 0
|
||||
else
|
||||
echo "❌ The following files are missing 'from __future__ import annotations':"
|
||||
echo "$FILES_MISSING_IMPORT"
|
||||
echo "This import is required to allow forward references in type annotations without quotes."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "✅ No relevant Python files found."
|
||||
fi
|
||||
|
||||
- name: Check for import from cli package in certain changed Python files
|
||||
run: |
|
||||
git fetch origin ${{ github.base_ref }}
|
||||
CHANGED_FILES=$(git diff --diff-filter=ACMR --name-only origin/${{ github.base_ref }}...HEAD | grep -E '\.py$' | grep -v -E 'cli/.*|tests/.*|contributing/samples/' || true)
|
||||
if [ -n "$CHANGED_FILES" ]; then
|
||||
echo "Changed Python files to check:"
|
||||
echo "$CHANGED_FILES"
|
||||
echo ""
|
||||
|
||||
set +e
|
||||
FILES_WITH_FORBIDDEN_IMPORT=$(grep -lE '^from.*cli.*import.*$' $CHANGED_FILES)
|
||||
GREP_EXIT_CODE=$?
|
||||
set -e
|
||||
|
||||
if [[ $GREP_EXIT_CODE -eq 0 ]]; then
|
||||
echo "❌ Do not import from the cli package outside of the cli package. If you need to reuse the code elsewhere, please move the code outside of the cli package."
|
||||
echo "The following files contain the forbidden pattern:"
|
||||
echo "$FILES_WITH_FORBIDDEN_IMPORT"
|
||||
exit 1
|
||||
else
|
||||
echo "✅ No instances of importing from the cli package found in relevant changed Python files."
|
||||
fi
|
||||
else
|
||||
echo "✅ No relevant Python files found."
|
||||
fi
|
||||
@@ -43,11 +43,19 @@ jobs:
|
||||
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
|
||||
@@ -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,5 +1,97 @@
|
||||
# Changelog
|
||||
|
||||
## [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)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add memory_service option to CLI ([416dc6f](https://github.com/google/adk-python/commit/416dc6feed26e55586d28f8c5132b31413834c88))
|
||||
* Add support for display_name and description when deploying to agent engine ([aaf1f9b](https://github.com/google/adk-python/commit/aaf1f9b930d12657bfc9b9d0abd8e2248c1fc469))
|
||||
* Dev UI: Trace View
|
||||
* New trace tab which contains all traces grouped by user messages
|
||||
* Click each row will open corresponding event details
|
||||
* Hover each row will highlight the corresponding message in dialog
|
||||
* Dev UI: Evaluation
|
||||
* Evaluation Configuration: users can now configure custom threshold for the metrics used for each eval run ([d1b0587](https://github.com/google/adk-python/commit/d1b058707eed72fd4987d8ec8f3b47941a9f7d64))
|
||||
* Each eval case added can now be viewed and edited. Right now we only support edit of text.
|
||||
* Show the used metric in evaluation history ([6ed6351](https://github.com/google/adk-python/commit/6ed635190c86d5b2ba0409064cf7bcd797fd08da))
|
||||
* Tool enhancements:
|
||||
* Add url_context_tool ([fe1de7b](https://github.com/google/adk-python/commit/fe1de7b10326a38e0d5943d7002ac7889c161826))
|
||||
* Support to customize timeout for mcpstdio connections ([54367dc](https://github.com/google/adk-python/commit/54367dcc567a2b00e80368ea753a4fc0550e5b57))
|
||||
* Introduce write protected mode to BigQuery tools ([6c999ca](https://github.com/google/adk-python/commit/6c999caa41dca3a6ec146ea42b0a794b14238ec2))
|
||||
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Agent Engine deployment:
|
||||
* Correct help text formatting for `adk deploy agent_engine` ([13f98c3](https://github.com/google/adk-python/commit/13f98c396a2fa21747e455bb5eed503a553b5b22))
|
||||
* Handle project and location in the .env properly when deploying to Agent Engine ([0c40542](https://github.com/google/adk-python/commit/0c4054200fd50041f0dce4b1c8e56292b99a8ea8))
|
||||
* Fix broken agent graphs ([3b1f2ae](https://github.com/google/adk-python/commit/3b1f2ae9bfdb632b52e6460fc5b7c9e04748bd50))
|
||||
* Forward `__annotations__` to the fake func for FunctionTool inspection ([9abb841](https://github.com/google/adk-python/commit/9abb8414da1055ab2f130194b986803779cd5cc5))
|
||||
* Handle the case when agent loading error doesn't have msg attribute in agent loader ([c224626](https://github.com/google/adk-python/commit/c224626ae189d02e5c410959b3631f6bd4d4d5c1))
|
||||
* Prevent agent_graph.py throwing when workflow agent is root agent ([4b1c218](https://github.com/google/adk-python/commit/4b1c218cbe69f7fb309b5a223aa2487b7c196038))
|
||||
* Remove display_name for non-Vertex file uploads ([cf5d701](https://github.com/google/adk-python/commit/cf5d7016a0a6ccf2b522df6f2d608774803b6be4))
|
||||
|
||||
|
||||
### Documentation
|
||||
|
||||
* Add DeepWiki badge to README ([f38c08b](https://github.com/google/adk-python/commit/f38c08b3057b081859178d44fa2832bed46561a9))
|
||||
* Update code example in tool declaration to reflect BigQuery artifact description ([3ae6ce1](https://github.com/google/adk-python/commit/3ae6ce10bc5a120c48d84045328c5d78f6eb85d4))
|
||||
|
||||
|
||||
## [1.2.1](https://github.com/google/adk-python/compare/v1.2.0...v1.2.1) (2025-06-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Import deprecated from typing_extensions ([068df04](https://github.com/google/adk-python/commit/068df04bcef694725dd36e09f4476b5e67f1b456))
|
||||
|
||||
|
||||
## [1.2.0](https://github.com/google/adk-python/compare/v1.1.1...v1.2.0) (2025-06-04)
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
[](LICENSE)
|
||||
[](https://github.com/google/adk-python/actions/workflows/python-unit-tests.yml)
|
||||
[](https://www.reddit.com/r/agentdevelopmentkit/)
|
||||
[](https://deepwiki.com/google/adk-python)
|
||||
|
||||
<html>
|
||||
<h2 align="center">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
This folder host resources for ADK contributors, for example, testing samples etc.
|
||||
|
||||
# Samples
|
||||
## Samples
|
||||
|
||||
Samples folder host samples to test different features. The samples are usually minimal and simplistic to test one or a few scenarios.
|
||||
|
||||
@@ -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.
|
||||
Regular → Executable
@@ -0,0 +1,145 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
|
||||
from google.adk import Agent
|
||||
import requests
|
||||
|
||||
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")
|
||||
|
||||
BASE_URL = "https://api.github.com"
|
||||
|
||||
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 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:
|
||||
issue_count: number of issues to return
|
||||
|
||||
"""
|
||||
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:
|
||||
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):
|
||||
"""
|
||||
Add the specified label to the given issue number.
|
||||
|
||||
Args:
|
||||
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, 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=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 "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, 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,
|
||||
],
|
||||
)
|
||||
@@ -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")
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -15,20 +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
|
||||
|
||||
|
||||
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:
|
||||
# Define BigQuery tool config
|
||||
tool_config = BigQueryToolConfig(write_mode=WriteMode.ALLOWED)
|
||||
|
||||
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
|
||||
@@ -36,8 +37,24 @@ 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_toolset = BigQueryToolset(
|
||||
credentials_config=credentials_config, bigquery_tool_config=tool_config
|
||||
)
|
||||
|
||||
# The variable name `root_agent` determines what your root agent is for the
|
||||
# debug CLI
|
||||
|
||||
@@ -70,12 +70,22 @@ async def main():
|
||||
if event.content.parts and event.content.parts[0].text:
|
||||
print(f'** {event.author}: {event.content.parts[0].text}')
|
||||
|
||||
async def check_rolls_in_state(rolls_size: int):
|
||||
session = await runner.session_service.get_session(
|
||||
app_name=app_name, user_id=user_id_1, session_id=session_11.id
|
||||
)
|
||||
assert len(session.state['rolls']) == rolls_size
|
||||
for roll in session.state['rolls']:
|
||||
assert roll > 0 and roll <= 100
|
||||
|
||||
start_time = time.time()
|
||||
print('Start time:', start_time)
|
||||
print('------------------------------------')
|
||||
await run_prompt(session_11, 'Hi')
|
||||
await run_prompt(session_11, 'Roll a die with 100 sides')
|
||||
await check_rolls_in_state(1)
|
||||
await run_prompt(session_11, 'Roll a die again with 100 sides.')
|
||||
await check_rolls_in_state(2)
|
||||
await run_prompt(session_11, 'What numbers did I got?')
|
||||
await run_prompt_bytes(session_11, 'Hi bytes')
|
||||
print(
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import random
|
||||
|
||||
from google.adk.agents import Agent
|
||||
from google.adk.examples.example import Example
|
||||
from google.adk.tools.example_tool import ExampleTool
|
||||
from google.genai import types
|
||||
|
||||
@@ -66,43 +67,47 @@ def check_prime(nums: list[int]) -> str:
|
||||
)
|
||||
|
||||
|
||||
example_tool = ExampleTool([
|
||||
{
|
||||
"input": {
|
||||
"role": "user",
|
||||
"parts": [{"text": "Roll a 6-sided die."}],
|
||||
},
|
||||
"output": [
|
||||
{"role": "model", "parts": [{"text": "I rolled a 4 for you."}]}
|
||||
],
|
||||
},
|
||||
{
|
||||
"input": {
|
||||
"role": "user",
|
||||
"parts": [{"text": "Is 7 a prime number?"}],
|
||||
},
|
||||
"output": [{
|
||||
"role": "model",
|
||||
"parts": [{"text": "Yes, 7 is a prime number."}],
|
||||
}],
|
||||
},
|
||||
{
|
||||
"input": {
|
||||
"role": "user",
|
||||
"parts": [{"text": "Roll a 10-sided die and check if it's prime."}],
|
||||
},
|
||||
"output": [
|
||||
{
|
||||
"role": "model",
|
||||
"parts": [{"text": "I rolled an 8 for you."}],
|
||||
},
|
||||
{
|
||||
"role": "model",
|
||||
"parts": [{"text": "8 is not a prime number."}],
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
example_tool = ExampleTool(
|
||||
examples=[
|
||||
Example(
|
||||
input=types.UserContent(
|
||||
parts=[types.Part(text="Roll a 6-sided die.")]
|
||||
),
|
||||
output=[
|
||||
types.ModelContent(
|
||||
parts=[types.Part(text="I rolled a 4 for you.")]
|
||||
)
|
||||
],
|
||||
),
|
||||
Example(
|
||||
input=types.UserContent(
|
||||
parts=[types.Part(text="Is 7 a prime number?")]
|
||||
),
|
||||
output=[
|
||||
types.ModelContent(
|
||||
parts=[types.Part(text="Yes, 7 is a prime number.")]
|
||||
)
|
||||
],
|
||||
),
|
||||
Example(
|
||||
input=types.UserContent(
|
||||
parts=[
|
||||
types.Part(
|
||||
text="Roll a 10-sided die and check if it's prime."
|
||||
)
|
||||
]
|
||||
),
|
||||
output=[
|
||||
types.ModelContent(
|
||||
parts=[types.Part(text="I rolled an 8 for you.")]
|
||||
),
|
||||
types.ModelContent(
|
||||
parts=[types.Part(text="8 is not a prime number.")]
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
prime_agent = Agent(
|
||||
name="prime_agent",
|
||||
|
||||
@@ -1 +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,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
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
# 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 random
|
||||
|
||||
from google.adk import Agent
|
||||
from google.adk.tools.tool_context import ToolContext
|
||||
from google.genai import types
|
||||
|
||||
|
||||
def roll_die(sides: int, tool_context: ToolContext) -> int:
|
||||
"""Roll a die and return the rolled result.
|
||||
|
||||
Args:
|
||||
sides: The integer number of sides the die has.
|
||||
|
||||
Returns:
|
||||
An integer of the result of rolling the die.
|
||||
"""
|
||||
result = random.randint(1, sides)
|
||||
if not 'rolls' in tool_context.state:
|
||||
tool_context.state['rolls'] = []
|
||||
|
||||
tool_context.state['rolls'] = tool_context.state['rolls'] + [result]
|
||||
return result
|
||||
|
||||
|
||||
async def check_prime(nums: list[int]) -> str:
|
||||
"""Check if a given list of numbers are prime.
|
||||
|
||||
Args:
|
||||
nums: The list of numbers to check.
|
||||
|
||||
Returns:
|
||||
A str indicating which number is prime.
|
||||
"""
|
||||
primes = set()
|
||||
for number in nums:
|
||||
number = int(number)
|
||||
if number <= 1:
|
||||
continue
|
||||
is_prime = True
|
||||
for i in range(2, int(number**0.5) + 1):
|
||||
if number % i == 0:
|
||||
is_prime = False
|
||||
break
|
||||
if is_prime:
|
||||
primes.add(number)
|
||||
return (
|
||||
'No prime numbers found.'
|
||||
if not primes
|
||||
else f"{', '.join(str(num) for num in primes)} are prime numbers."
|
||||
)
|
||||
|
||||
|
||||
root_agent = Agent(
|
||||
model='gemini-2.0-flash-live-preview-04-09', # for Vertex project
|
||||
# model='gemini-2.0-flash-live-001', # for AI studio key
|
||||
name='hello_world_agent',
|
||||
description=(
|
||||
'hello world agent that can roll a dice of 8 sides and check prime'
|
||||
' numbers.'
|
||||
),
|
||||
instruction="""
|
||||
You roll dice and answer questions about the outcome of the dice rolls.
|
||||
You can roll dice of different sizes.
|
||||
You can use multiple tools in parallel by calling functions in parallel(in one request and in one round).
|
||||
It is ok to discuss previous dice roles, and comment on the dice rolls.
|
||||
When you are asked to roll a die, you must call the roll_die tool with the number of sides. Be sure to pass in an integer. Do not pass in a string.
|
||||
You should never roll a die on your own.
|
||||
When checking prime numbers, call the check_prime tool with a list of integers. Be sure to pass in a list of integers. You should never pass in a string.
|
||||
You should not check prime numbers before calling the tool.
|
||||
When you are asked to roll a die and check prime numbers, you should always make the following two function calls:
|
||||
1. You should first call the roll_die tool to get a roll. Wait for the function response before calling the check_prime tool.
|
||||
2. After you get the function response from roll_die tool, you should call the check_prime tool with the roll_die result.
|
||||
2.1 If user asks you to check primes based on previous rolls, make sure you include the previous rolls in the list.
|
||||
3. When you respond, you must include the roll_die result from step 1.
|
||||
You should always perform the previous 3 steps when asking for a roll and checking prime numbers.
|
||||
You should not rely on the previous history on prime results.
|
||||
""",
|
||||
tools=[
|
||||
roll_die,
|
||||
check_prime,
|
||||
],
|
||||
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,37 @@
|
||||
# Simplistic Live (Bidi-Streaming) Agent
|
||||
This project provides a basic example of a live, bidirectional streaming agent
|
||||
designed for testing and experimentation.
|
||||
|
||||
You can see full documentation [here](https://google.github.io/adk-docs/streaming/).
|
||||
|
||||
## Getting Started
|
||||
|
||||
Follow these steps to get the agent up and running:
|
||||
|
||||
1. **Start the ADK Web Server**
|
||||
Open your terminal, navigate to the root directory that contains the
|
||||
`live_bidi_streaming_agent` folder, and execute the following command:
|
||||
```bash
|
||||
adk web
|
||||
```
|
||||
|
||||
2. **Access the ADK Web UI**
|
||||
Once the server is running, open your web browser and navigate to the URL
|
||||
provided in the terminal (it will typically be `http://localhost:8000`).
|
||||
|
||||
3. **Select the Agent**
|
||||
In the top-left corner of the ADK Web UI, use the dropdown menu to select
|
||||
this agent.
|
||||
|
||||
4. **Start Streaming**
|
||||
Click on either the **Audio** or **Video** icon located near the chat input
|
||||
box to begin the streaming session.
|
||||
|
||||
5. **Interact with the Agent**
|
||||
You can now begin talking to the agent, and it will respond in real-time.
|
||||
|
||||
## Usage Notes
|
||||
|
||||
* You only need to click the **Audio** or **Video** button once to initiate the
|
||||
stream. The current version does not support stopping and restarting the stream
|
||||
by clicking the button again during a session.
|
||||
@@ -16,8 +16,8 @@
|
||||
import os
|
||||
|
||||
from google.adk.agents.llm_agent import LlmAgent
|
||||
from google.adk.tools.mcp_tool.mcp_session_manager import SseConnectionParams
|
||||
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset
|
||||
from google.adk.tools.mcp_tool.mcp_toolset import SseServerParams
|
||||
|
||||
_allowed_path = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
@@ -31,7 +31,7 @@ Allowed directory: {_allowed_path}
|
||||
""",
|
||||
tools=[
|
||||
MCPToolset(
|
||||
connection_params=SseServerParams(
|
||||
connection_params=SseConnectionParams(
|
||||
url='http://localhost:3000/sse',
|
||||
headers={'Accept': 'text/event-stream'},
|
||||
),
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
import os
|
||||
|
||||
from google.adk.agents.llm_agent import LlmAgent
|
||||
from google.adk.tools.mcp_tool import StdioConnectionParams
|
||||
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset
|
||||
from google.adk.tools.mcp_tool.mcp_toolset import StdioServerParameters
|
||||
from mcp import StdioServerParameters
|
||||
|
||||
_allowed_path = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
@@ -31,13 +32,16 @@ Allowed directory: {_allowed_path}
|
||||
""",
|
||||
tools=[
|
||||
MCPToolset(
|
||||
connection_params=StdioServerParameters(
|
||||
command='npx',
|
||||
args=[
|
||||
'-y', # Arguments for the command
|
||||
'@modelcontextprotocol/server-filesystem',
|
||||
_allowed_path,
|
||||
],
|
||||
connection_params=StdioConnectionParams(
|
||||
server_params=StdioServerParameters(
|
||||
command='npx',
|
||||
args=[
|
||||
'-y', # Arguments for the command
|
||||
'@modelcontextprotocol/server-filesystem',
|
||||
_allowed_path,
|
||||
],
|
||||
),
|
||||
timeout=5,
|
||||
),
|
||||
# don't want agent to do write operation
|
||||
# you can also do below
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user