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
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| da34692231 | |||
| d263afd91b | |||
| c85330eae8 | |||
| adef7b297a | |||
| 18cd3844db | |||
| e545e5a570 | |||
| e33161b4f8 | |||
| b5d9f8f3e3 | |||
| 75699fbeca | |||
| 45d60a1906 | |||
| bf39c00610 | |||
| 28d0ea876f | |||
| 4ca77bc056 | |||
| dc414cb507 | |||
| a021222077 | |||
| 4442167dd5 | |||
| e437c7aac6 | |||
| 3fa2ea7cb9 | |||
| 6a39c854e0 | |||
| b04a5cebb8 | |||
| 362fb3f2b7 | |||
| 0bd05df471 | |||
| 62c4a85917 | |||
| 08869ccc07 | |||
| df141db60c | |||
| 9bd539eca9 | |||
| 3d2f13cecd | |||
| d0fdfb8c8e | |||
| 0959b06dbd | |||
| 9af2394e0a | |||
| 873beca088 | |||
| a903c54bac | |||
| b0d88bf172 | |||
| 17d6042995 | |||
| 43083baddc | |||
| 9b75e24d8c | |||
| a58cc3d882 | |||
| 37108be855 | |||
| b6c7b5b64f | |||
| ffa9b361db | |||
| dc43d518c9 | |||
| e2748b3ed5 | |||
| 379810dd6a | |||
| 1cf5cf0d0a | |||
| f0183a9b98 | |||
| 3f621ae6f2 | |||
| 4e765ae2f3 | |||
| c13c9875cf | |||
| 045aea9b15 | |||
| 20279d9a50 | |||
| 09e487df3c | |||
| 51a559eb2a | |||
| 1fe9c47cc6 | |||
| 31e41bdd06 | |||
| 9029b8a66e | |||
| e153d07593 | |||
| e79651cd86 | |||
| 22629a17bd | |||
| 5356f20ead | |||
| ed09cd840f | |||
| 630f1674cb | |||
| 2f55de6ded | |||
| 77b869f5e3 | |||
| 04de3e197d | |||
| 3901fade71 | |||
| a71dbdf9e2 |
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"contextFileName": "AGENTS.md"
|
||||
}
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
|
||||
# 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)
|
||||
FILES_MISSING_IMPORT=$(grep -L 'from __future__ import annotations' $CHANGED_FILES || true)
|
||||
|
||||
# Check if the list of non-compliant files is empty
|
||||
if [ -z "$FILES_MISSING_IMPORT" ]; then
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# .github/workflows/pr-commit-check.yml
|
||||
# This GitHub Action workflow checks if a pull request has more than one commit.
|
||||
# If it does, it fails the check and instructs the user to squash their commits.
|
||||
|
||||
name: 'PR Commit Check'
|
||||
|
||||
# This workflow runs on pull request events.
|
||||
# It's configured to run on any pull request that is opened or synchronized (new commits pushed).
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
|
||||
# Defines the jobs that will run as part of the workflow.
|
||||
jobs:
|
||||
check-commit-count:
|
||||
# The type of runner that the job will run on. 'ubuntu-latest' is a good default.
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# The steps that will be executed as part of the job.
|
||||
steps:
|
||||
# Step 1: Check out the code
|
||||
# This action checks out your repository under $GITHUB_WORKSPACE, so your workflow can access it.
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# We need to fetch all commits to accurately count them.
|
||||
# '0' means fetch all history for all branches and tags.
|
||||
fetch-depth: 0
|
||||
|
||||
# Step 2: Count the commits in the pull request
|
||||
# This step runs a script to get the number of commits in the PR.
|
||||
- name: Count Commits
|
||||
id: count_commits
|
||||
# We use `git rev-list --count` to count the commits.
|
||||
# ${{ github.event.pull_request.base.sha }} is the commit SHA of the base branch.
|
||||
# ${{ github.event.pull_request.head.sha }} is the commit SHA of the head branch (the PR branch).
|
||||
# The '..' syntax gives us the list of commits in the head branch that are not in the base branch.
|
||||
# The output of the command (the count) is stored in a step output variable named 'count'.
|
||||
run: |
|
||||
count=$(git rev-list --count ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }})
|
||||
echo "commit_count=$count" >> $GITHUB_OUTPUT
|
||||
|
||||
# Step 3: Check if the commit count is greater than 1
|
||||
# This step uses the output from the previous step to decide whether to pass or fail.
|
||||
- name: Check Commit Count
|
||||
# This step only runs if the 'commit_count' output from the 'count_commits' step is greater than 1.
|
||||
if: steps.count_commits.outputs.commit_count > 1
|
||||
# If the condition is met, the workflow will exit with a failure status.
|
||||
run: |
|
||||
echo "This pull request has ${{ steps.count_commits.outputs.commit_count }} commits."
|
||||
echo "Please squash them into a single commit before merging."
|
||||
echo "You can use git rebase -i HEAD~N"
|
||||
echo "...where N is the number of commits you want to squash together. The PR check conveniently tells you this number! For example, if the check says you have 3 commits, you would run: git rebase -i HEAD~3."
|
||||
echo "Because you have rewritten the commit history, you must use the --force flag to update the pull request: git push --force"
|
||||
exit 1
|
||||
|
||||
# Step 4: Success message
|
||||
# This step runs if the commit count is not greater than 1 (i.e., it's 1).
|
||||
- name: Success
|
||||
if: steps.count_commits.outputs.commit_count <= 1
|
||||
run: |
|
||||
echo "This pull request has a single commit. Great job!"
|
||||
@@ -40,4 +40,5 @@ jobs:
|
||||
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
|
||||
PYTHONPATH: contributing/samples
|
||||
run: python -m adk_triaging_agent.main
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
# Gemini CLI / Gemini Code Assist Context
|
||||
|
||||
This document provides context for the Gemini CLI and Gemini Code Assist to understand the project and assist with development.
|
||||
|
||||
## Project Overview
|
||||
|
||||
The Agent Development Kit (ADK) is an open-source, code-first Python toolkit for building, evaluating, and deploying sophisticated AI agents with flexibility and control. While optimized for Gemini and the Google ecosystem, ADK is model-agnostic, deployment-agnostic, and is built for compatibility with other frameworks. ADK was designed to make agent development feel more like software development, to make it easier for developers to create, deploy, and orchestrate agentic architectures that range from simple tasks to complex workflows.
|
||||
|
||||
## Project Architecture
|
||||
|
||||
Please refer to [ADK Project Overview and Architecture](https://github.com/google/adk-python/blob/main/contributing/adk_project_overview_and_architecture.md) for details.
|
||||
|
||||
## ADK: Style Guides
|
||||
|
||||
### Python Style Guide
|
||||
|
||||
The project follows the Google Python Style Guide. Key conventions are enforced using `pylint` with the provided `pylintrc` configuration file. Here are some of the key style points:
|
||||
|
||||
* **Indentation**: 2 spaces.
|
||||
* **Line Length**: Maximum 80 characters.
|
||||
* **Naming Conventions**:
|
||||
* `function_and_variable_names`: `snake_case`
|
||||
* `ClassNames`: `CamelCase`
|
||||
* `CONSTANTS`: `UPPERCASE_SNAKE_CASE`
|
||||
* **Docstrings**: Required for all public modules, functions, classes, and methods.
|
||||
* **Imports**: Organized and sorted.
|
||||
* **Error Handling**: Specific exceptions should be caught, not general ones like `Exception`.
|
||||
|
||||
### Autoformat
|
||||
|
||||
We have autoformat.sh to help solve import organize and formatting issues.
|
||||
|
||||
```bash
|
||||
# Run in open_source_workspace/
|
||||
$ ./autoformat.sh
|
||||
```
|
||||
|
||||
### In ADK source
|
||||
|
||||
Below styles applies to the ADK source code (under `src/` folder of the Github.
|
||||
repo).
|
||||
|
||||
#### Use relative imports
|
||||
|
||||
```python
|
||||
# DO
|
||||
from ..agents.llm_agent import LlmAgent
|
||||
|
||||
# DON'T
|
||||
from google.adk.agents.llm_agent import LlmAgent
|
||||
```
|
||||
|
||||
#### Import from module, not from `__init__.py`
|
||||
|
||||
```python
|
||||
# DO
|
||||
from ..agents.llm_agent import LlmAgent
|
||||
|
||||
# DON'T
|
||||
from ..agents import LlmAgent # import from agents/__init__.py
|
||||
```
|
||||
|
||||
#### Always do `from __future__ import annotations`
|
||||
|
||||
```python
|
||||
# DO THIS, right after the open-source header.
|
||||
from __future__ import annotations
|
||||
```
|
||||
|
||||
Like below:
|
||||
|
||||
```python
|
||||
# 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 __future__ import annotations
|
||||
|
||||
# ... the rest of the file.
|
||||
```
|
||||
|
||||
This allows us to forward-reference a class without quotes.
|
||||
|
||||
Check out go/pep563 for details.
|
||||
|
||||
### In ADK tests
|
||||
|
||||
#### Use absolute imports
|
||||
|
||||
In tests, we use `google.adk` same as how our users uses.
|
||||
|
||||
```python
|
||||
# DO
|
||||
from google.adk.agents.llm_agent import LlmAgent
|
||||
|
||||
# DON'T
|
||||
from ..agents.llm_agent import LlmAgent
|
||||
```
|
||||
|
||||
## ADK: Local testing
|
||||
|
||||
### Unit tests
|
||||
|
||||
Run below command:
|
||||
|
||||
```bash
|
||||
$ pytest tests/unittests
|
||||
```
|
||||
|
||||
## Docstring and comments
|
||||
|
||||
### Comments - Explaining the Why, Not the What
|
||||
Philosophy: Well-written code should be largely self-documenting. Comments
|
||||
serve a different purpose: they should explain the complex algorithms,
|
||||
non-obvious business logic, or the rationale behind a particular implementation
|
||||
choice—the things the code cannot express on its own. Avoid comments that
|
||||
merely restate what the code does (e.g., # increment i above i += 1).
|
||||
|
||||
Style: Comments should be written as complete sentences. Block comments must
|
||||
begin with a # followed by a single space.
|
||||
|
||||
## Versioning
|
||||
ADK adherence to Semantic Versioning 2.0.0
|
||||
|
||||
Core Principle: The adk-python project strictly adheres to the Semantic
|
||||
Versioning 2.0.0 specification. All release versions will follow the
|
||||
MAJOR.MINOR.PATCH format.
|
||||
|
||||
### Breaking Change
|
||||
|
||||
A breaking change is any modification that introduces backward-incompatible
|
||||
changes to the public API. In the context of the ADK, this means a change that
|
||||
could force a developer using the framework to alter their existing code to
|
||||
upgrade to the new version. The public API is not limited to just the Python
|
||||
function and class signatures; it also encompasses data schemas for stored
|
||||
information (like evaluation datasets), the command-line interface (CLI),
|
||||
and the data format used for server communications.
|
||||
|
||||
### Public API Surface Definition
|
||||
|
||||
The "public API" of ADK is a broad contract that extends beyond its Python
|
||||
function signatures. A breaking change in any of the following areas can
|
||||
disrupt user workflows and the wider ecosystem of agents and tools built with
|
||||
ADK. The analysis of the breaking changes introduced in v1.0.0 demonstrates the
|
||||
expansive nature of this contract. For the purposes of versioning, the ADK
|
||||
Public API Surface is defined as:
|
||||
|
||||
- All public classes, methods, and functions in the google.adk namespace.
|
||||
|
||||
- The names, required parameters, and expected behavior of all built-in Tools
|
||||
(e.g., google_search, BuiltInCodeExecutor).
|
||||
|
||||
- The structure and schema of persisted data, including Session data, Memory,
|
||||
and Evaluation datasets.
|
||||
|
||||
- The JSON request/response format of the ADK API server(FastAPI server)
|
||||
used by adk web, including field casing conventions.
|
||||
|
||||
- The command-line interface (CLI) commands, arguments, and flags (e.g., adk deploy).
|
||||
|
||||
- The expected file structure for agent definitions that are loaded by the
|
||||
framework (e.g., the agent.py convention).
|
||||
|
||||
#### Checklist for Breaking Changes:
|
||||
|
||||
The following changes are considered breaking and necessitate a MAJOR version
|
||||
bump.
|
||||
|
||||
- API Signature Change: Renaming, removing, or altering the required parameters
|
||||
of any public class, method, or function (e.g., the removal of the list_events
|
||||
method from BaseSessionService).
|
||||
|
||||
- Architectural Shift: A fundamental change to a core component's behavior
|
||||
(e.g., making all service methods async, which requires consumers to use await).
|
||||
|
||||
- Data Schema Change: A non-additive change to a persisted data schema that
|
||||
renders old data unreadable or invalid (e.g., the redesign of the
|
||||
MemoryService and evaluation dataset schemas).
|
||||
|
||||
- Tool Interface Change: Renaming a built-in tool, changing its required
|
||||
parameters, or altering its fundamental purpose (e.g., replacing
|
||||
BuiltInCodeExecutionTool with BuiltInCodeExecutor and moving it from the tools
|
||||
parameter to the code_executor parameter of an Agent).
|
||||
|
||||
- Configuration Change: Altering the required structure of configuration files
|
||||
or agent definition files that the framework loads (e.g., the simplification
|
||||
of the agent.py structure for MCPToolset).
|
||||
|
||||
- Wire Format Change: Modifying the data format for API server interactions
|
||||
(e.g., the switch from snake_case to camelCase for all JSON payloads).
|
||||
|
||||
- Dependency Removal: Removing support for a previously integrated third-party
|
||||
library or tool type.
|
||||
@@ -1,5 +1,74 @@
|
||||
# Changelog
|
||||
|
||||
## [1.6.1](https://github.com/google/adk-python/compare/v1.5.0...v1.6.1) (2025-07-09)
|
||||
|
||||
### Features
|
||||
|
||||
* Add A2A support as experimental features [f0183a9](https://github.com/google/adk-python/commit/f0183a9b98b0bcf8aab4f948f467cef204ddc9d6)
|
||||
* Install google-adk with a2a extra: pip install google-adk[a2a]
|
||||
* Users can serve agents as A2A agent with `--a2a` option for `adk web` and
|
||||
`adk api_server`
|
||||
* Users can run a remote A2A agent with `RemoteA2AAgent` class
|
||||
* Three A2A agent samples are added:
|
||||
* contributing/samples/a2a_basic
|
||||
* contributing/samples/a2a_auth
|
||||
* contributing/samples/a2a_human_in_loop
|
||||
|
||||
* Support agent hot reload.[e545e5a](https://github.com/google/adk-python/commit/e545e5a570c1331d2ed8fda31c7244b5e0f71584)
|
||||
Users can add `--reload_agents` flag to `adk web` and `adk api_server` command
|
||||
to reload agents automatically when new changes are detected.
|
||||
|
||||
* Eval features
|
||||
* Implement auto rater-based evaluator for responses [75699fb](https://github.com/google/adk-python/commit/75699fbeca06f99c6f2415938da73bb423ec9b9b)
|
||||
* Add Safety evaluator metric [0bd05df](https://github.com/google/adk-python/commit/0bd05df471a440159a44b5864be4740b0f1565f9)
|
||||
* Add BaseEvalService declaration and surrounding data models [b0d88bf](https://github.com/google/adk-python/commit/b0d88bf17242e738bcd409b3d106deed8ce4d407)
|
||||
|
||||
* Minor features
|
||||
* Add `custom_metadata` to VertexAiSessionService when adding events [a021222](https://github.com/google/adk-python/commit/a02122207734cabb26f7c23e84d2336c4b8b0375)
|
||||
* Support protected write in BigQuery `execute_sql` tool [dc43d51](https://github.com/google/adk-python/commit/dc43d518c90b44932b3fdedd33fca9e6c87704e2)
|
||||
* Added clone() method to BaseAgent to allow users to create copies of an agent [d263afd] (https://github.com/google/adk-python/commit/d263afd91ba4a3444e5321c0e1801c499dec4c68)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Support project-based gemini model path to use enterprise_web_search_tool [e33161b](https://github.com/google/adk-python/commit/e33161b4f8650e8bcb36c650c4e2d1fe79ae2526)
|
||||
* Use inspect.signature() instead of typing.get_type_hints for examining function signatures[4ca77bc](https://github.com/google/adk-python/commit/4ca77bc056daa575621a80d3c8d5014b78209233)
|
||||
* Replace Event ID generation with UUID4 to prevent SQLite integrity constraint failures [e437c7a](https://github.com/google/adk-python/commit/e437c7aac650ac6a53fcfa71bd740e3e5ec0f230)
|
||||
* Remove duplicate options from `adk deploy` [3fa2ea7](https://github.com/google/adk-python/commit/3fa2ea7cb923c9f8606d98b45a23bd58a7027436)
|
||||
* Fix scenario where a user can access another users events given the same session id [362fb3f](https://github.com/google/adk-python/commit/362fb3f2b7ac4ad15852d00ce4f3935249d097f6)
|
||||
* Handle unexpected 'parameters' argument in FunctionTool.run_async [0959b06](https://github.com/google/adk-python/commit/0959b06dbdf3037fe4121f12b6d25edca8fb9afc)
|
||||
* Make sure each partial event has different timestamp [17d6042](https://github.com/google/adk-python/commit/17d604299505c448fcb55268f0cbaeb6c4fa314a)
|
||||
* Avoid pydantic.ValidationError when the model stream returns empty final chunk [9b75e24](https://github.com/google/adk-python/commit/9b75e24d8c01878c153fec26ccfea4490417d23b)
|
||||
* Fix google_search_tool.py to support updated Gemini LIVE model naming [77b869f](https://github.com/google/adk-python/commit/77b869f5e35a66682cba35563824fd23a9028d7c)
|
||||
* Adding detailed information on each metric evaluation [04de3e1](https://github.com/google/adk-python/commit/04de3e197d7a57935488eb7bfa647c7ab62cd9d9)
|
||||
* Converts litellm generate config err [3901fad](https://github.com/google/adk-python/commit/3901fade71486a1e9677fe74a120c3f08efe9d9e)
|
||||
* Save output in state via output_key only when the event is authored by current agent [20279d9](https://github.com/google/adk-python/commit/20279d9a50ac051359d791dea77865c17c0bbf9e)
|
||||
* Treat SQLite database update time as UTC for session's last update time [3f621ae](https://github.com/google/adk-python/commit/3f621ae6f2a5fac7f992d3d833a5311b4d4e7091)
|
||||
* Raise ValueError when sessionId and userId are incorrect combination(#1653) [4e765ae](https://github.com/google/adk-python/commit/4e765ae2f3821318e581c26a52e11d392aaf72a4)
|
||||
* Support API-Key for MCP Tool authentication [045aea9](https://github.com/google/adk-python/commit/045aea9b15ad0190a960f064d6e1e1fc7f964c69)
|
||||
* Lock LangGraph version to <= 0.4.10 [9029b8a](https://github.com/google/adk-python/commit/9029b8a66e9d5e0d29d9a6df0e5590cc7c0e9038)
|
||||
* Update the retry logic of create session polling [3d2f13c](https://github.com/google/adk-python/commit/3d2f13cecd3fef5adfa1c98bf23d7b68ff355f4d)
|
||||
|
||||
### Chores
|
||||
|
||||
* Extract mcp client creation logic to a separate method [45d60a1](https://github.com/google/adk-python/commit/45d60a1906bfe7c43df376a829377e2112ea3d17)
|
||||
* Add tests for live streaming configs [bf39c00](https://github.com/google/adk-python/commit/bf39c006102ef3f01e762e7bb744596a4589f171)
|
||||
* Update ResponseEvaluator to use newer version of Eval SDK [62c4a85](https://github.com/google/adk-python/commit/62c4a8591780a9a3fdb03a0de11092d84118a1b9)
|
||||
* Add util to build our llms.txt and llms-full.txt files [a903c54](https://github.com/google/adk-python/commit/a903c54bacfcb150dc315bec9c67bf7ce9551c07)
|
||||
* Create an example for multi agent live streaming [a58cc3d](https://github.com/google/adk-python/commit/a58cc3d882e59358553e8ea16d166b1ab6d3aa71)
|
||||
* Refactor the ADK Triaging Agent to make the code easier to read [b6c7b5b](https://github.com/google/adk-python/commit/b6c7b5b64fcd2e83ed43f7b96ea43791733955d8)
|
||||
|
||||
|
||||
### Documentation
|
||||
|
||||
* Update the a2a exmaple link in README.md [d0fdfb8](https://github.com/google/adk-python/commit/d0fdfb8c8e2e32801999c81de8d8ed0be3f88e76)
|
||||
* Adds AGENTS.md to provide relevant project context for the Gemini CLI [37108be](https://github.com/google/adk-python/commit/37108be8557e011f321de76683835448213f8515)
|
||||
* Update CONTRIBUTING.md [ffa9b36](https://github.com/google/adk-python/commit/ffa9b361db615ae365ba62c09a8f4226fb761551)
|
||||
* Add adk project overview and architecture [28d0ea8](https://github.com/google/adk-python/commit/28d0ea876f2f8de952f1eccbc788e98e39f50cf5)
|
||||
* Add docstring to clarify that inmemory service are not suitable for production [dc414cb](https://github.com/google/adk-python/commit/dc414cb5078326b8c582b3b9072cbda748766286)
|
||||
* Update agents.md to include versioning strategy [6a39c85](https://github.com/google/adk-python/commit/6a39c854e032bda3bc15f0e4fe159b41cf2f474b)
|
||||
* Add tenacity into project.toml [df141db](https://github.com/google/adk-python/commit/df141db60c1137a6bcddd6d46aad3dc506868543)
|
||||
* Updating CONTRIBUTING.md with missing extra [e153d07](https://github.com/google/adk-python/commit/e153d075939fb628a7dc42b12e1b3461842db541)
|
||||
|
||||
## [1.5.0](https://github.com/google/adk-python/compare/v1.4.2...v1.5.0) (2025-06-25)
|
||||
|
||||
|
||||
|
||||
+4
-3
@@ -49,6 +49,7 @@ This project follows
|
||||
|
||||
## Requirement for PRs
|
||||
|
||||
- Each PR should only have one commit. Please squash it if there are multiple PRs.
|
||||
- All PRs, other than small documentation or typo fixes, should have a Issue assoicated. If not, please create one.
|
||||
- Small, focused PRs. Keep changes minimal—one concern per PR.
|
||||
- For bug fixes or features, please provide logs or screenshot after the fix is applied to help reviewers better understand the fix.
|
||||
@@ -147,11 +148,11 @@ For any changes that impact user-facing documentation (guides, API reference, tu
|
||||
pytest ./tests/unittests
|
||||
```
|
||||
|
||||
NOTE: for accurately repro test failure, only include `test` and `eval` as
|
||||
extra dependencies.
|
||||
NOTE: for accurate repro of test failure, only include `test`, `eval` and
|
||||
`a2a` as extra dependencies.
|
||||
|
||||
```shell
|
||||
uv sync --extra test --extra eval
|
||||
uv sync --extra test --extra eval --extra a2a
|
||||
pytest ./tests/unittests
|
||||
```
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ Agent Development Kit (ADK) is a flexible and modular framework for developing a
|
||||
|
||||
For remote agent-to-agent communication, ADK integrates with the
|
||||
[A2A protocol](https://github.com/google-a2a/A2A/).
|
||||
See this [example](https://github.com/google-a2a/a2a-samples/tree/main/samples/python/agents/google_adk)
|
||||
See this [example](https://github.com/a2aproject/a2a-samples/tree/main/samples/python/agents)
|
||||
for how they can work together.
|
||||
|
||||
## 🚀 Installation
|
||||
|
||||
@@ -7,3 +7,10 @@ This folder host resources for ADK contributors, for example, testing samples et
|
||||
Samples folder host samples to test different features. The samples are usually minimal and simplistic to test one or a few scenarios.
|
||||
|
||||
**Note**: This is different from the [google/adk-samples](https://github.com/google/adk-samples) repo, which hosts more complex e2e samples for customers to use or modify directly.
|
||||
|
||||
## ADK project and architecture overview
|
||||
|
||||
The [adk_project_overview_and_architecture.md](adk_project_overview_and_architecture.md) describes the ADK project overview and its technical architecture from high-level.
|
||||
|
||||
This is helpful for contributors to understand the project and design philosophy.
|
||||
It can also be feed into LLMs for vibe-coding.
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
# ADK Project Overview and Architecture
|
||||
|
||||
Google Agent Development Kit (ADK) for Python
|
||||
|
||||
## Core Philosophy & Architecture
|
||||
|
||||
- Code-First: Everything is defined in Python code for versioning, testing, and IDE support. Avoid GUI-based logic.
|
||||
|
||||
- Modularity & Composition: We build complex multi-agent systems by composing multiple, smaller, specialized agents.
|
||||
|
||||
- Deployment-Agnostic: The agent's core logic is separate from its deployment environment. The same agent.py can be run locally for testing, served via an API, or deployed to the cloud.
|
||||
|
||||
## Foundational Abstractions (Our Vocabulary)
|
||||
|
||||
- Agent: The blueprint. It defines an agent's identity, instructions, and tools. It's a declarative configuration object.
|
||||
|
||||
- Tool: A capability. A Python function an agent can call to interact with the world (e.g., search, API call).
|
||||
|
||||
- Runner: The engine. It orchestrates the "Reason-Act" loop, manages LLM calls, and executes tools.
|
||||
|
||||
- Session: The conversation state. It holds the history for a single, continuous dialogue.
|
||||
|
||||
- Memory: Long-term recall across different sessions.
|
||||
|
||||
- Artifact Service: Manages non-textual data like files.
|
||||
|
||||
## Canonical Project Structure
|
||||
|
||||
Adhere to this structure for compatibility with ADK tooling.
|
||||
|
||||
my_adk_project/ \
|
||||
└── src/ \
|
||||
└── my_app/ \
|
||||
├── agents/ \
|
||||
│ ├── my_agent/ \
|
||||
│ │ ├── __init__.py # Must contain: from. import agent \
|
||||
│ │ └── agent.py # Must contain: root_agent = Agent(...) \
|
||||
│ └── another_agent/ \
|
||||
│ ├── __init__.py \
|
||||
│ └── agent.py\
|
||||
|
||||
agent.py: Must define the agent and assign it to a variable named root_agent. This is how ADK's tools find it.
|
||||
|
||||
__init__.py: In each agent directory, it must contain from. import agent to make the agent discoverable.
|
||||
|
||||
## Local Development & Debugging
|
||||
|
||||
Interactive UI (adk web): This is our primary debugging tool. It's a decoupled system:
|
||||
|
||||
Backend: A FastAPI server started with adk api_server.
|
||||
|
||||
Frontend: An Angular app that connects to the backend.
|
||||
|
||||
Use the "Events" tab to inspect the full execution trace (prompts, tool calls, responses).
|
||||
|
||||
CLI (adk run): For quick, stateless functional checks in the terminal.
|
||||
|
||||
Programmatic (pytest): For writing automated unit and integration tests.
|
||||
|
||||
## The API Layer (FastAPI)
|
||||
|
||||
We expose agents as production APIs using FastAPI.
|
||||
|
||||
- get_fast_api_app: This is the key helper function from google.adk.cli.fast_api that creates a FastAPI app from our agent directory.
|
||||
|
||||
- Standard Endpoints: The generated app includes standard routes like /list-apps and /run_sse for streaming responses. The wire format is camelCase.
|
||||
|
||||
- Custom Endpoints: We can add our own routes (e.g., /health) to the app object returned by the helper.
|
||||
|
||||
Python
|
||||
|
||||
from google.adk.cli.fast_api import get_fast_api_app
|
||||
app = get_fast_api_app(agent_dir="./agents")
|
||||
|
||||
@app.get("/health")
|
||||
async def health_check():
|
||||
return {"status": "ok"}
|
||||
|
||||
|
||||
## Deployment to Production
|
||||
|
||||
The adk cli provides the "adk deploy" command to deploy to Google Vertex Agent Engine, Google CloudRun, Google GKE.
|
||||
|
||||
## Testing & Evaluation Strategy
|
||||
|
||||
Testing is layered, like a pyramid.
|
||||
|
||||
### Layer 1: Unit Tests (Base)
|
||||
|
||||
What: Test individual Tool functions in isolation.
|
||||
|
||||
How: Use pytest in tests/test_tools.py. Verify deterministic logic.
|
||||
|
||||
### Layer 2: Integration Tests (Middle)
|
||||
|
||||
What: Test the agent's internal logic and interaction with tools.
|
||||
|
||||
How: Use pytest in tests/test_agent.py, often with mocked LLMs or services.
|
||||
|
||||
### Layer 3: Evaluation Tests (Top)
|
||||
|
||||
What: Assess end-to-end performance with a live LLM. This is about quality, not just pass/fail.
|
||||
|
||||
How: Use the ADK Evaluation Framework.
|
||||
|
||||
Test Cases: Create JSON files with input and a reference (expected tool calls and final response).
|
||||
|
||||
Metrics: tool_trajectory_avg_score (does it use tools correctly?) and response_match_score (is the final answer good?).
|
||||
|
||||
Run via: adk web (UI), pytest (for CI/CD), or adk eval (CLI).
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
build_llms_txt.py – produce llms.txt and llms-full.txt
|
||||
– skips ```java``` blocks
|
||||
– README can be next to docs/ or inside docs/
|
||||
– includes Python API reference from HTML files
|
||||
– includes adk-python repository README
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sys
|
||||
import textwrap
|
||||
from typing import List
|
||||
from typing import Tuple
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
RE_JAVA = re.compile(r"```java[ \t\r\n][\s\S]*?```", re.I | re.M)
|
||||
RE_SNIPPET = re.compile(r"^(\s*)--8<--\s+\"([^\"]+?)(?::([^\"]+))?\"$", re.M)
|
||||
|
||||
|
||||
def fetch_adk_python_readme() -> str:
|
||||
"""Fetch README content from adk-python repository"""
|
||||
try:
|
||||
url = "https://raw.githubusercontent.com/google/adk-python/main/README.md"
|
||||
with urllib.request.urlopen(url) as response:
|
||||
return response.read().decode("utf-8")
|
||||
except (urllib.error.URLError, urllib.error.HTTPError) as e:
|
||||
print(f"Warning: Could not fetch adk-python README: {e}")
|
||||
return ""
|
||||
|
||||
|
||||
def strip_java(md: str) -> str:
|
||||
return RE_JAVA.sub("", md)
|
||||
|
||||
|
||||
def first_heading(md: str) -> str | None:
|
||||
for line in md.splitlines():
|
||||
if line.startswith("#"):
|
||||
return line.lstrip("#").strip()
|
||||
return None
|
||||
|
||||
|
||||
def md_to_text(md: str) -> str:
|
||||
import bs4
|
||||
import markdown
|
||||
|
||||
html = markdown.markdown(
|
||||
md, extensions=["fenced_code", "tables", "attr_list"]
|
||||
)
|
||||
return bs4.BeautifulSoup(html, "html.parser").get_text("\n")
|
||||
|
||||
|
||||
def html_to_text(html_file: Path) -> str:
|
||||
"""Extract text content from HTML files (for Python API reference)"""
|
||||
import bs4
|
||||
|
||||
try:
|
||||
html_content = html_file.read_text(encoding="utf-8")
|
||||
soup = bs4.BeautifulSoup(html_content, "html.parser")
|
||||
|
||||
# Remove script and style elements
|
||||
for script in soup(["script", "style"]):
|
||||
script.decompose()
|
||||
|
||||
# Get text and clean it up
|
||||
text = soup.get_text()
|
||||
lines = (line.strip() for line in text.splitlines())
|
||||
chunks = (phrase.strip() for line in lines for phrase in line.split(" "))
|
||||
text = "\n".join(chunk for chunk in chunks if chunk)
|
||||
|
||||
return text
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not process {html_file}: {e}")
|
||||
return ""
|
||||
|
||||
|
||||
def count_tokens(text: str, model: str = "cl100k_base") -> int:
|
||||
try:
|
||||
import tiktoken
|
||||
|
||||
return len(tiktoken.get_encoding(model).encode(text))
|
||||
except Exception:
|
||||
return len(text.split())
|
||||
|
||||
|
||||
def expand_code_snippets(content: str, project_root: Path) -> str:
|
||||
"""
|
||||
Expands code snippets marked with --8<-- "path/to/file.py" or
|
||||
--8<-- "path/to/file.py:section_name" into the content.
|
||||
"""
|
||||
|
||||
def replace_snippet(match):
|
||||
indent = match.group(1) # Capture leading spaces
|
||||
snippet_path_str = match.group(
|
||||
2
|
||||
) # Capture the file path (e.g., "examples/python/snippets/file.py")
|
||||
section_name = match.group(
|
||||
3
|
||||
) # Capture the section name if present (e.g., "init")
|
||||
snippet_full_path = (
|
||||
project_root / snippet_path_str
|
||||
) # Changed from base_path to project_root
|
||||
|
||||
# If not found in project root, try adk-docs directory
|
||||
if not snippet_full_path.exists():
|
||||
script_dir = Path(__file__).resolve().parent
|
||||
adk_docs_path = script_dir / "adk-docs" / snippet_path_str
|
||||
if adk_docs_path.exists():
|
||||
snippet_full_path = adk_docs_path
|
||||
|
||||
if snippet_full_path.exists():
|
||||
try:
|
||||
file_content = snippet_full_path.read_text(encoding="utf-8")
|
||||
if section_name:
|
||||
# Extract content based on section markers
|
||||
# Handle both single and double hash markers with optional spacing
|
||||
start_marker_patterns = [
|
||||
f"# --8<-- [start:{section_name.strip()}]",
|
||||
f"## --8<-- [start:{section_name.strip()}]",
|
||||
]
|
||||
end_marker_patterns = [
|
||||
f"# --8<-- [end:{section_name.strip()}]",
|
||||
f"## --8<-- [end:{section_name.strip()}]",
|
||||
f"## --8<-- [end:{section_name.strip()}]", # Handle extra space
|
||||
]
|
||||
|
||||
start_index = -1
|
||||
end_index = -1
|
||||
|
||||
# Find start marker
|
||||
for pattern in start_marker_patterns:
|
||||
start_index = file_content.find(pattern)
|
||||
if start_index != -1:
|
||||
start_marker = pattern
|
||||
break
|
||||
|
||||
# Find end marker
|
||||
for pattern in end_marker_patterns:
|
||||
end_index = file_content.find(pattern)
|
||||
if end_index != -1:
|
||||
break
|
||||
|
||||
if start_index != -1 and end_index != -1 and start_index < end_index:
|
||||
# Adjust start_index to begin immediately after the start_marker
|
||||
start_of_code = start_index + len(start_marker)
|
||||
temp_content = file_content[start_of_code:end_index]
|
||||
lines = temp_content.splitlines(keepends=True)
|
||||
extracted_lines = []
|
||||
for line in lines:
|
||||
if (
|
||||
not line.strip().startswith("# --8<--")
|
||||
and not line.strip().startswith("## --8<--")
|
||||
and line.strip() != ""
|
||||
):
|
||||
extracted_lines.append(line)
|
||||
extracted_content = "".join(extracted_lines).strip("\n")
|
||||
|
||||
return textwrap.indent(extracted_content, indent)
|
||||
else:
|
||||
print(
|
||||
f"Warning: Section '{section_name}' not found or markers"
|
||||
f" malformed in {snippet_full_path}"
|
||||
)
|
||||
return match.group(0)
|
||||
else:
|
||||
# Read entire file if no section name
|
||||
return textwrap.indent(file_content, indent)
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not read snippet file {snippet_full_path}: {e}")
|
||||
return match.group(0)
|
||||
else:
|
||||
print(f"Warning: Snippet file not found: {snippet_full_path}")
|
||||
return match.group(0)
|
||||
|
||||
expanded_content = RE_SNIPPET.sub(replace_snippet, content)
|
||||
return expanded_content
|
||||
|
||||
|
||||
# ---------- index (llms.txt) ----------
|
||||
def build_index(docs: Path) -> str:
|
||||
# Locate README
|
||||
for cand in (docs / "README.md", docs.parent / "README.md"):
|
||||
if cand.exists():
|
||||
readme = cand.read_text(encoding="utf-8")
|
||||
break
|
||||
else:
|
||||
sys.exit("README.md not found in docs/ or its parent")
|
||||
|
||||
title = first_heading(readme) or "Documentation"
|
||||
summary = md_to_text(readme).split("\n\n")[0]
|
||||
lines = [f"# {title}", "", f"> {summary}", ""]
|
||||
|
||||
# Add adk-python repository README content
|
||||
adk_readme = fetch_adk_python_readme()
|
||||
if adk_readme:
|
||||
lines.append("## ADK Python Repository")
|
||||
lines.append("")
|
||||
# Include the full README content, properly formatted
|
||||
adk_text = md_to_text(strip_java(adk_readme))
|
||||
lines.append(adk_text)
|
||||
lines.append("")
|
||||
lines.append(
|
||||
f"**Source:** [adk-python"
|
||||
f" repository](https://github.com/google/adk-python)"
|
||||
)
|
||||
lines.append("")
|
||||
|
||||
primary: List[Tuple[str, str]] = []
|
||||
secondary: List[Tuple[str, str]] = []
|
||||
|
||||
# Process Markdown files
|
||||
for md in sorted(docs.rglob("*.md")):
|
||||
# Skip Java API reference files
|
||||
if "api-reference" in md.parts and "java" in md.parts:
|
||||
continue
|
||||
|
||||
rel = md.relative_to(docs)
|
||||
# Construct the correct GitHub URL for the Markdown file
|
||||
url = f"https://github.com/google/adk-docs/blob/main/docs/{rel}".replace(
|
||||
" ", "%20"
|
||||
)
|
||||
h = first_heading(strip_java(md.read_text(encoding="utf-8"))) or rel.stem
|
||||
(
|
||||
secondary
|
||||
if "sample" in rel.parts or "tutorial" in rel.parts
|
||||
else primary
|
||||
).append((h, url))
|
||||
|
||||
# Add Python API reference
|
||||
python_api_dir = docs / "api-reference" / "python"
|
||||
if python_api_dir.exists():
|
||||
primary.append((
|
||||
"Python API Reference",
|
||||
"https://github.com/google/adk-docs/blob/main/docs/api-reference/python/",
|
||||
))
|
||||
|
||||
def emit(name: str, items: List[Tuple[str, str]]):
|
||||
nonlocal lines
|
||||
if items:
|
||||
lines.append(f"## {name}")
|
||||
lines += [f"- [{h}]({u})" for h, u in items]
|
||||
lines.append("")
|
||||
|
||||
emit("Documentation", primary)
|
||||
emit("Optional", secondary)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
# ---------- full corpus ----------
|
||||
def build_full(docs: Path) -> str:
|
||||
out = []
|
||||
|
||||
script_dir = Path(__file__).resolve().parent
|
||||
project_root = script_dir.parents[2] # Correct project root
|
||||
print(f"DEBUG: Project Root: {project_root}")
|
||||
print(f"DEBUG: Docs Dir: {docs}")
|
||||
|
||||
# Add adk-python repository README content at the beginning
|
||||
adk_readme = fetch_adk_python_readme()
|
||||
if adk_readme:
|
||||
# Expand snippets in README if any
|
||||
expanded_adk_readme = expand_code_snippets(
|
||||
strip_java(adk_readme), project_root
|
||||
) # Pass project_root
|
||||
out.append("# ADK Python Repository")
|
||||
out.append("")
|
||||
out.append(expanded_adk_readme) # Use expanded content
|
||||
out.append("")
|
||||
out.append("---")
|
||||
out.append("")
|
||||
|
||||
# Process Markdown files
|
||||
for md in sorted(docs.rglob("*.md")):
|
||||
# Skip Java API reference files
|
||||
if "api-reference" in md.parts and "java" in md.parts:
|
||||
continue
|
||||
|
||||
md_content = md.read_text(encoding="utf-8")
|
||||
print(f"DEBUG: Processing markdown file: {md.relative_to(docs)}")
|
||||
expanded_md_content = expand_code_snippets(
|
||||
strip_java(md_content), project_root
|
||||
) # Changed back to project_root
|
||||
out.append(expanded_md_content) # Use expanded content
|
||||
|
||||
# Process Python API reference HTML files
|
||||
python_api_dir = docs / "api-reference" / "python"
|
||||
if python_api_dir.exists():
|
||||
# Add a separator and header for Python API reference
|
||||
out.append("\n\n# Python API Reference\n")
|
||||
|
||||
# Process main HTML files (skip static assets and generated files)
|
||||
html_files = [
|
||||
python_api_dir / "index.html",
|
||||
python_api_dir / "google-adk.html",
|
||||
python_api_dir / "genindex.html",
|
||||
python_api_dir / "py-modindex.html",
|
||||
]
|
||||
|
||||
for html_file in html_files:
|
||||
if html_file.exists():
|
||||
text = html_to_text(html_file)
|
||||
if text.strip():
|
||||
out.append(f"\n## {html_file.stem}\n")
|
||||
out.append(text)
|
||||
|
||||
return "\n\n".join(out)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
ap = argparse.ArgumentParser(
|
||||
description="Generate llms.txt / llms-full.txt",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
)
|
||||
ap.add_argument("--docs-dir", required=True, type=Path)
|
||||
ap.add_argument("--out-root", default=Path("."), type=Path)
|
||||
ap.add_argument("--index-limit", type=int, default=50_000)
|
||||
ap.add_argument("--full-limit", type=int, default=500_000)
|
||||
args = ap.parse_args()
|
||||
|
||||
idx, full = build_index(args.docs_dir), build_full(args.docs_dir)
|
||||
if (tok := count_tokens(idx)) > args.index_limit:
|
||||
sys.exit(f"Index too big: {tok:,}")
|
||||
if (tok := count_tokens(full)) > args.full_limit:
|
||||
sys.exit(f"Full text too big: {tok:,}")
|
||||
|
||||
(args.out_root / "llms.txt").write_text(idx, encoding="utf-8")
|
||||
(args.out_root / "llms-full.txt").write_text(full, encoding="utf-8")
|
||||
print("✅ Generated llms.txt and llms-full.txt successfully")
|
||||
print(f"llms.txt tokens: {count_tokens(idx)}")
|
||||
print(f"llms-full.txt tokens: {count_tokens(full)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,183 @@
|
||||
# A2A OAuth Authentication Sample Agent
|
||||
|
||||
This sample demonstrates the **Agent-to-Agent (A2A)** architecture with **OAuth Authentication** workflows in the Agent Development Kit (ADK). The sample implements a multi-agent system where a remote agent can surface OAuth authentication requests to the local agent, which then guides the end user through the OAuth flow before returning the authentication credentials to the remote agent for API access.
|
||||
|
||||
## Overview
|
||||
|
||||
The A2A OAuth Authentication sample consists of:
|
||||
|
||||
- **Root Agent** (`root_agent`): The main orchestrator that handles user requests and delegates tasks to specialized agents
|
||||
- **YouTube Search Agent** (`youtube_search_agent`): A local agent that handles YouTube video searches using LangChain tools
|
||||
- **BigQuery Agent** (`bigquery_agent`): A remote A2A agent that manages BigQuery operations and requires OAuth authentication for Google Cloud access
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌────────────────────┐ ┌──────────────────┐
|
||||
│ End User │───▶│ Root Agent │───▶│ BigQuery Agent │
|
||||
│ (OAuth Flow) │ │ (Local) │ │ (Remote A2A) │
|
||||
│ │ │ │ │ (localhost:8001) │
|
||||
│ OAuth UI │◀───│ │◀───│ OAuth Request │
|
||||
└─────────────────┘ └────────────────────┘ └──────────────────┘
|
||||
```
|
||||
|
||||
## Key Features
|
||||
|
||||
### 1. **Multi-Agent Architecture**
|
||||
- Root agent coordinates between local YouTube search and remote BigQuery operations
|
||||
- Demonstrates hybrid local/remote agent workflows
|
||||
- Seamless task delegation based on user request types
|
||||
|
||||
### 2. **OAuth Authentication Workflow**
|
||||
- Remote BigQuery agent surfaces OAuth authentication requests to the root agent
|
||||
- Root agent guides end users through Google OAuth flow for BigQuery access
|
||||
- Secure token exchange between agents for authenticated API calls
|
||||
|
||||
### 3. **Google Cloud Integration**
|
||||
- BigQuery toolset with comprehensive dataset and table management capabilities
|
||||
- OAuth-protected access to user's Google Cloud BigQuery resources
|
||||
- Support for listing, creating, and managing datasets and tables
|
||||
|
||||
### 4. **LangChain Tool Integration**
|
||||
- YouTube search functionality using LangChain community tools
|
||||
- Demonstrates integration of third-party tools in agent workflows
|
||||
|
||||
## Setup and Usage
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. **Set up OAuth Credentials**:
|
||||
```bash
|
||||
export OAUTH_CLIENT_ID=your_google_oauth_client_id
|
||||
export OAUTH_CLIENT_SECRET=your_google_oauth_client_secret
|
||||
```
|
||||
|
||||
2. **Start the Remote BigQuery Agent server**:
|
||||
```bash
|
||||
# Start the remote a2a server that serves the BigQuery agent on port 8001
|
||||
adk api_server --a2a --port 8001 contributing/samples/a2a_auth/remote_a2a
|
||||
```
|
||||
|
||||
3. **Run the Main Agent**:
|
||||
```bash
|
||||
# In a separate terminal, run the adk web server
|
||||
adk web contributing/samples/
|
||||
```
|
||||
|
||||
### Example Interactions
|
||||
|
||||
Once both services are running, you can interact with the root agent:
|
||||
|
||||
**YouTube Search (No Authentication Required):**
|
||||
```
|
||||
User: Search for 3 Taylor Swift music videos
|
||||
Agent: I'll help you search for Taylor Swift music videos on YouTube.
|
||||
[Agent delegates to YouTube Search Agent]
|
||||
Agent: I found 3 Taylor Swift music videos:
|
||||
1. "Anti-Hero" - Official Music Video
|
||||
2. "Shake It Off" - Official Music Video
|
||||
3. "Blank Space" - Official Music Video
|
||||
```
|
||||
|
||||
**BigQuery Operations (OAuth Required):**
|
||||
```
|
||||
User: List my BigQuery datasets
|
||||
Agent: I'll help you access your BigQuery datasets. This requires authentication with your Google account.
|
||||
[Agent delegates to BigQuery Agent]
|
||||
Agent: To access your BigQuery data, please complete the OAuth authentication.
|
||||
[OAuth flow initiated - user redirected to Google authentication]
|
||||
User: [Completes OAuth flow in browser]
|
||||
Agent: Authentication successful! Here are your BigQuery datasets:
|
||||
- dataset_1: Customer Analytics
|
||||
- dataset_2: Sales Data
|
||||
- dataset_3: Marketing Metrics
|
||||
```
|
||||
|
||||
**Dataset Management:**
|
||||
```
|
||||
User: Show me details for my Customer Analytics dataset
|
||||
Agent: I'll get the details for your Customer Analytics dataset.
|
||||
[Using existing OAuth token]
|
||||
Agent: Customer Analytics Dataset Details:
|
||||
- Created: 2024-01-15
|
||||
- Location: US
|
||||
- Tables: 5
|
||||
- Description: Customer behavior and analytics data
|
||||
```
|
||||
|
||||
## Code Structure
|
||||
|
||||
### Main Agent (`agent.py`)
|
||||
|
||||
- **`youtube_search_agent`**: Local agent with LangChain YouTube search tool
|
||||
- **`bigquery_agent`**: Remote A2A agent configuration for BigQuery operations
|
||||
- **`root_agent`**: Main orchestrator with task delegation logic
|
||||
|
||||
### Remote BigQuery Agent (`remote_a2a/bigquery_agent/`)
|
||||
|
||||
- **`agent.py`**: Implementation of the BigQuery agent with OAuth toolset
|
||||
- **`agent.json`**: Agent card of the A2A agent
|
||||
- **`BigQueryToolset`**: OAuth-enabled tools for BigQuery dataset and table management
|
||||
|
||||
## OAuth Authentication Workflow
|
||||
|
||||
The OAuth authentication process follows this pattern:
|
||||
|
||||
1. **Initial Request**: User requests BigQuery operation through root agent
|
||||
2. **Delegation**: Root agent delegates to remote BigQuery agent
|
||||
3. **Auth Check**: BigQuery agent checks for valid OAuth token
|
||||
4. **Auth Request**: If no token, agent surfaces OAuth request to root agent
|
||||
5. **User OAuth**: Root agent guides user through Google OAuth flow
|
||||
6. **Token Exchange**: Root agent sends OAuth token to BigQuery agent
|
||||
7. **API Call**: BigQuery agent uses token to make authenticated API calls
|
||||
8. **Result Return**: BigQuery agent returns results through root agent to user
|
||||
|
||||
## Supported BigQuery Operations
|
||||
|
||||
The BigQuery agent supports the following operations:
|
||||
|
||||
### Dataset Operations:
|
||||
- **List Datasets**: `bigquery_datasets_list` - Get all user's datasets
|
||||
- **Get Dataset**: `bigquery_datasets_get` - Get specific dataset details
|
||||
- **Create Dataset**: `bigquery_datasets_insert` - Create new dataset
|
||||
|
||||
### Table Operations:
|
||||
- **List Tables**: `bigquery_tables_list` - Get tables in a dataset
|
||||
- **Get Table**: `bigquery_tables_get` - Get specific table details
|
||||
- **Create Table**: `bigquery_tables_insert` - Create new table in dataset
|
||||
|
||||
## Extending the Sample
|
||||
|
||||
You can extend this sample by:
|
||||
|
||||
- Adding more Google Cloud services (Cloud Storage, Compute Engine, etc.)
|
||||
- Implementing token refresh and expiration handling
|
||||
- Adding role-based access control for different BigQuery operations
|
||||
- Creating OAuth flows for other providers (Microsoft, Facebook, etc.)
|
||||
- Adding audit logging for authentication events
|
||||
- Implementing multi-tenant OAuth token management
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Connection Issues:**
|
||||
- Ensure the local ADK web server is running on port 8000
|
||||
- Ensure the remote A2A server is running on port 8001
|
||||
- Check that no firewall is blocking localhost connections
|
||||
- Verify the agent.json URL matches the running A2A server
|
||||
|
||||
**OAuth Issues:**
|
||||
- Verify OAuth client ID and secret are correctly set in .env file
|
||||
- Ensure OAuth redirect URIs are properly configured in Google Cloud Console
|
||||
- Check that the OAuth scopes include BigQuery access permissions
|
||||
- Verify the user has access to the BigQuery projects/datasets
|
||||
|
||||
**BigQuery Access Issues:**
|
||||
- Ensure the authenticated user has BigQuery permissions
|
||||
- Check that the Google Cloud project has BigQuery API enabled
|
||||
- Verify dataset and table names are correct and accessible
|
||||
- Check for quota limits on BigQuery API calls
|
||||
|
||||
**Agent Communication Issues:**
|
||||
- Check the logs for both the local ADK web server and remote A2A server
|
||||
- Verify OAuth tokens are properly passed between agents
|
||||
- Ensure agent instructions are clear about authentication requirements
|
||||
Executable → Regular
@@ -0,0 +1,62 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
from google.adk.agents import Agent
|
||||
from google.adk.agents.remote_a2a_agent import RemoteA2aAgent
|
||||
from google.adk.tools.langchain_tool import LangchainTool
|
||||
from langchain_community.tools import YouTubeSearchTool
|
||||
|
||||
# Instantiate the tool
|
||||
langchain_yt_tool = YouTubeSearchTool()
|
||||
|
||||
# Wrap the tool in the LangchainTool class from ADK
|
||||
adk_yt_tool = LangchainTool(
|
||||
tool=langchain_yt_tool,
|
||||
)
|
||||
|
||||
youtube_search_agent = Agent(
|
||||
name="youtube_search_agent",
|
||||
model="gemini-2.0-flash", # Replace with the actual model name
|
||||
instruction="""
|
||||
Ask customer to provide singer name, and the number of videos to search.
|
||||
""",
|
||||
description="Help customer to search for a video on Youtube.",
|
||||
tools=[adk_yt_tool],
|
||||
output_key="youtube_search_output",
|
||||
)
|
||||
|
||||
bigquery_agent = RemoteA2aAgent(
|
||||
name="bigquery_agent",
|
||||
description="Help customer to manage notion workspace.",
|
||||
agent_card=(
|
||||
"http://localhost:8001/a2a/bigquery_agent/.well-known/agent.json"
|
||||
),
|
||||
)
|
||||
|
||||
root_agent = Agent(
|
||||
model="gemini-2.0-flash",
|
||||
name="root_agent",
|
||||
instruction="""
|
||||
You are a helpful assistant that can help search youtube videos, look up BigQuery datasets and tables.
|
||||
You delegate youtube search tasks to the youtube_search_agent.
|
||||
You delegate BigQuery tasks to the bigquery_agent.
|
||||
Always clarify the results before proceeding.
|
||||
""",
|
||||
global_instruction=(
|
||||
"You are a helpful assistant that can help search youtube videos, look"
|
||||
" up BigQuery datasets and tables."
|
||||
),
|
||||
sub_agents=[youtube_search_agent, bigquery_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
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"capabilities": {},
|
||||
"defaultInputModes": ["text/plain"],
|
||||
"defaultOutputModes": ["application/json"],
|
||||
"description": "A Google BigQuery agent that helps manage users' data on Google BigQuery. Can list, get, and create datasets, as well as manage tables within datasets. Supports OAuth authentication for secure access to BigQuery resources.",
|
||||
"name": "bigquery_agent",
|
||||
"skills": [
|
||||
{
|
||||
"id": "dataset_management",
|
||||
"name": "Dataset Management",
|
||||
"description": "List, get details, and create BigQuery datasets",
|
||||
"tags": ["bigquery", "datasets", "google-cloud"]
|
||||
},
|
||||
{
|
||||
"id": "table_management",
|
||||
"name": "Table Management",
|
||||
"description": "List, get details, and create BigQuery tables within datasets",
|
||||
"tags": ["bigquery", "tables", "google-cloud"]
|
||||
},
|
||||
{
|
||||
"id": "oauth_authentication",
|
||||
"name": "OAuth Authentication",
|
||||
"description": "Secure authentication with Google BigQuery using OAuth",
|
||||
"tags": ["authentication", "oauth", "security"]
|
||||
}
|
||||
],
|
||||
"url": "http://localhost:8000/a2a/bigquery_agent",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from google.adk import Agent
|
||||
from google.adk.tools.google_api_tool import BigQueryToolset
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
# Access the variable
|
||||
oauth_client_id = os.getenv("OAUTH_CLIENT_ID")
|
||||
oauth_client_secret = os.getenv("OAUTH_CLIENT_SECRET")
|
||||
tools_to_expose = [
|
||||
"bigquery_datasets_list",
|
||||
"bigquery_datasets_get",
|
||||
"bigquery_datasets_insert",
|
||||
"bigquery_tables_list",
|
||||
"bigquery_tables_get",
|
||||
"bigquery_tables_insert",
|
||||
]
|
||||
bigquery_toolset = BigQueryToolset(
|
||||
client_id=oauth_client_id,
|
||||
client_secret=oauth_client_secret,
|
||||
tool_filter=tools_to_expose,
|
||||
)
|
||||
|
||||
root_agent = Agent(
|
||||
model="gemini-2.0-flash",
|
||||
name="bigquery_agent",
|
||||
instruction="""
|
||||
You are a helpful Google BigQuery agent that help to manage users' data on Google BigQuery.
|
||||
Use the provided tools to conduct various operations on users' data in Google BigQuery.
|
||||
|
||||
Scenario 1:
|
||||
The user wants to query their biguqery datasets
|
||||
Use bigquery_datasets_list to query user's datasets
|
||||
|
||||
Scenario 2:
|
||||
The user wants to query the details of a specific dataset
|
||||
Use bigquery_datasets_get to get a dataset's details
|
||||
|
||||
Scenario 3:
|
||||
The user wants to create a new dataset
|
||||
Use bigquery_datasets_insert to create a new dataset
|
||||
|
||||
Scenario 4:
|
||||
The user wants to query their tables in a specific dataset
|
||||
Use bigquery_tables_list to list all tables in a dataset
|
||||
|
||||
Scenario 5:
|
||||
The user wants to query the details of a specific table
|
||||
Use bigquery_tables_get to get a table's details
|
||||
|
||||
Scenario 6:
|
||||
The user wants to insert a new table into a dataset
|
||||
Use bigquery_tables_insert to insert a new table into a dataset
|
||||
|
||||
Current user:
|
||||
<User>
|
||||
{userInfo?}
|
||||
</User>
|
||||
""",
|
||||
tools=[bigquery_toolset],
|
||||
)
|
||||
@@ -0,0 +1,120 @@
|
||||
# A2A Basic Sample Agent
|
||||
|
||||
This sample demonstrates the **Agent-to-Agent (A2A)** architecture in the Agent Development Kit (ADK), showcasing how multiple agents can work together to handle complex tasks. The sample implements an agent that can roll dice and check if numbers are prime.
|
||||
|
||||
## Overview
|
||||
|
||||
The A2A Basic sample consists of:
|
||||
|
||||
- **Root Agent** (`root_agent`): The main orchestrator that delegates tasks to specialized sub-agents
|
||||
- **Roll Agent** (`roll_agent`): A local sub-agent that handles dice rolling operations
|
||||
- **Prime Agent** (`prime_agent`): A remote A2A agent that checks if numbers are prime, this agent is running on a separate A2A server
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌──────────────────┐ ┌────────────────────┐
|
||||
│ Root Agent │───▶│ Roll Agent │ │ Remote Prime │
|
||||
│ (Local) │ │ (Local) │ │ Agent │
|
||||
│ │ │ │ │ (localhost:8001) │
|
||||
│ │───▶│ │◀───│ │
|
||||
└─────────────────┘ └──────────────────┘ └────────────────────┘
|
||||
```
|
||||
|
||||
## Key Features
|
||||
|
||||
### 1. **Local Sub-Agent Integration**
|
||||
- The `roll_agent` demonstrates how to create and integrate local sub-agents
|
||||
- Handles dice rolling with configurable number of sides
|
||||
- Uses a simple function tool (`roll_die`) for random number generation
|
||||
|
||||
### 2. **Remote A2A Agent Integration**
|
||||
- The `prime_agent` shows how to connect to remote agent services
|
||||
- Communicates with a separate service via HTTP at `http://localhost:8001/a2a/check_prime_agent`
|
||||
- Demonstrates cross-service agent communication
|
||||
|
||||
### 3. **Agent Orchestration**
|
||||
- The root agent intelligently delegates tasks based on user requests
|
||||
- Can chain operations (e.g., "roll a die and check if it's prime")
|
||||
- Provides clear workflow coordination between multiple agents
|
||||
|
||||
### 4. **Example Tool Integration**
|
||||
- Includes an `ExampleTool` with sample interactions for context
|
||||
- Helps the agent understand expected behavior patterns
|
||||
|
||||
## Setup and Usage
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. **Start the Remote Prime Agent server**:
|
||||
```bash
|
||||
# Start the remote a2a server that serves the check prime agent on port 8001
|
||||
adk api_server --a2a --port 8001 contributing/samples/a2a_basic/remote_a2a
|
||||
```
|
||||
|
||||
2. **Run the Main Agent**:
|
||||
```bash
|
||||
# In a separate terminal, run the adk web server
|
||||
adk web contributing/samples/
|
||||
```
|
||||
|
||||
### Example Interactions
|
||||
|
||||
Once both services are running, you can interact with the root agent:
|
||||
|
||||
**Simple Dice Rolling:**
|
||||
```
|
||||
User: Roll a 6-sided die
|
||||
Bot: I rolled a 4 for you.
|
||||
```
|
||||
|
||||
**Prime Number Checking:**
|
||||
```
|
||||
User: Is 7 a prime number?
|
||||
Bot: Yes, 7 is a prime number.
|
||||
```
|
||||
|
||||
**Combined Operations:**
|
||||
```
|
||||
User: Roll a 10-sided die and check if it's prime
|
||||
Bot: I rolled an 8 for you.
|
||||
Bot: 8 is not a prime number.
|
||||
```
|
||||
|
||||
## Code Structure
|
||||
|
||||
### Main Agent (`agent.py`)
|
||||
|
||||
- **`roll_die(sides: int)`**: Function tool for rolling dice
|
||||
- **`roll_agent`**: Local agent specialized in dice rolling
|
||||
- **`prime_agent`**: Remote A2A agent configuration
|
||||
- **`root_agent`**: Main orchestrator with delegation logic
|
||||
|
||||
### Remote Prime Agent (`remote_a2a/check_prime_agent/`)
|
||||
|
||||
- **`agent.py`**: Implementation of the prime checking service
|
||||
- **`agent.json`**: Agent card of the A2A agent
|
||||
- **`check_prime(nums: list[int])`**: Prime number checking algorithm
|
||||
|
||||
|
||||
## Extending the Sample
|
||||
|
||||
You can extend this sample by:
|
||||
|
||||
- Adding more mathematical operations (factorization, square roots, etc.)
|
||||
- Creating additional remote agent
|
||||
- Implementing more complex delegation logic
|
||||
- Adding persistent state management
|
||||
- Integrating with external APIs or databases
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Connection Issues:**
|
||||
- Ensure the local ADK web server is running on port 8000
|
||||
- Ensure the remote A2A server is running on port 8001
|
||||
- Check that no firewall is blocking localhost connections
|
||||
- Verify the agent.json URL matches the running A2A server
|
||||
|
||||
**Agent Not Responding:**
|
||||
- Check the logs for both the local ADK web server on port 8000 and remote A2A server on port 8001
|
||||
- Verify the agent instructions are clear and unambiguous
|
||||
Executable
+15
@@ -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
|
||||
Executable
+120
@@ -0,0 +1,120 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import random
|
||||
|
||||
from google.adk.agents import Agent
|
||||
from google.adk.agents.remote_a2a_agent import RemoteA2aAgent
|
||||
from google.adk.tools.example_tool import ExampleTool
|
||||
from google.genai import types
|
||||
|
||||
|
||||
# --- Roll Die Sub-Agent ---
|
||||
def roll_die(sides: int) -> int:
|
||||
"""Roll a die and return the rolled result."""
|
||||
return random.randint(1, sides)
|
||||
|
||||
|
||||
roll_agent = Agent(
|
||||
name="roll_agent",
|
||||
description="Handles rolling dice of different sizes.",
|
||||
instruction="""
|
||||
You are responsible for rolling dice based on the user's request.
|
||||
When asked to roll a die, you must call the roll_die tool with the number of sides as an integer.
|
||||
""",
|
||||
tools=[roll_die],
|
||||
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,
|
||||
),
|
||||
]
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
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."}],
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
prime_agent = RemoteA2aAgent(
|
||||
name="prime_agent",
|
||||
description="Agent that handles checking if numbers are prime.",
|
||||
agent_card=(
|
||||
"http://localhost:8001/a2a/check_prime_agent/.well-known/agent.json"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
root_agent = Agent(
|
||||
model="gemini-2.0-flash",
|
||||
name="root_agent",
|
||||
instruction="""
|
||||
You are a helpful assistant that can roll dice and check if numbers are prime.
|
||||
You delegate rolling dice tasks to the roll_agent and prime checking tasks to the prime_agent.
|
||||
Follow these steps:
|
||||
1. If the user asks to roll a die, delegate to the roll_agent.
|
||||
2. If the user asks to check primes, delegate to the prime_agent.
|
||||
3. If the user asks to roll a die and then check if the result is prime, call roll_agent first, then pass the result to prime_agent.
|
||||
Always clarify the results before proceeding.
|
||||
""",
|
||||
global_instruction=(
|
||||
"You are DicePrimeBot, ready to roll dice and check prime numbers."
|
||||
),
|
||||
sub_agents=[roll_agent, prime_agent],
|
||||
tools=[example_tool],
|
||||
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,
|
||||
),
|
||||
]
|
||||
),
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user