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
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 832a633351 | |||
| 738d1a8b84 | |||
| 5306ddad4d | |||
| a623467299 | |||
| f54b9b6ad1 | |||
| 6729edd08e | |||
| 917a8a19f7 | |||
| ef3c745d65 | |||
| 88a4402d14 | |||
| 09f1269bf7 | |||
| 9e473e0abd | |||
| acbdca0d84 | |||
| ed7a21e189 | |||
| a1e1441115 | |||
| f33e0903b2 | |||
| abc89d2c81 | |||
| 00cc8cd643 | |||
| 9597a446fd | |||
| fa025d7559 | |||
| 120cbabeb2 | |||
| 29cd183aa1 | |||
| bd67e8480f | |||
| ea69c9093a | |||
| f033e405c1 | |||
| 3b1d9a8a3e | |||
| 7c670f638b | |||
| fb13963ded | |||
| 2fd8feb65d |
@@ -31,5 +31,8 @@ If applicable, add screenshots to help explain your problem.
|
||||
- Python version(python -V):
|
||||
- ADK version(pip show google-adk):
|
||||
|
||||
**Model Information:**
|
||||
For example, which model is being used.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
@@ -36,8 +36,8 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install uv
|
||||
run: curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
|
||||
@@ -82,6 +82,7 @@ log/
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
uv.lock
|
||||
|
||||
# Google Cloud specific
|
||||
.gcloudignore
|
||||
|
||||
@@ -1,5 +1,40 @@
|
||||
# Changelog
|
||||
|
||||
## [1.5.0](https://github.com/google/adk-python/compare/v1.4.2...v1.5.0) (2025-06-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add a new option `eval_storage_uri` in adk web & adk eval to specify GCS bucket to store eval data ([fa025d7](https://github.com/google/adk-python/commit/fa025d755978e1506fa0da1fecc49775bebc1045))
|
||||
* Add ADK examples for litellm with add_function_to_prompt ([f33e090](https://github.com/google/adk-python/commit/f33e0903b21b752168db3006dd034d7d43f7e84d))
|
||||
* Add implementation of VertexAiMemoryBankService and support in FastAPI endpoint ([abc89d2](https://github.com/google/adk-python/commit/abc89d2c811ba00805f81b27a3a07d56bdf55a0b))
|
||||
* Add rouge_score library to ADK eval dependencies, and implement RougeEvaluator that is computes ROUGE-1 for "response_match_score" metric ([9597a44](https://github.com/google/adk-python/commit/9597a446fdec63ad9e4c2692d6966b14f80ff8e2))
|
||||
* Add usage span attributes to telemetry ([#356](https://github.com/google/adk-python/issues/356)) ([ea69c90](https://github.com/google/adk-python/commit/ea69c9093a16489afdf72657136c96f61c69cafd))
|
||||
* Add Vertex Express mode compatibility for VertexAiSessionService ([00cc8cd](https://github.com/google/adk-python/commit/00cc8cd6433fc45ecfc2dbaa04dbbc1a81213b4d))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Include current turn context when include_contents='none' ([9e473e0](https://github.com/google/adk-python/commit/9e473e0abdded24e710fd857782356c15d04b515))
|
||||
* Make LiteLLM streaming truly asynchronous ([bd67e84](https://github.com/google/adk-python/commit/bd67e8480f6e8b4b0f8c22b94f15a8cda1336339))
|
||||
* Make raw_auth_credential and exchanged_auth_credential optional given their default value is None ([acbdca0](https://github.com/google/adk-python/commit/acbdca0d8400e292ba5525931175e0d6feab15f1))
|
||||
* Minor typo fix in the agent instruction ([ef3c745](https://github.com/google/adk-python/commit/ef3c745d655538ebd1ed735671be615f842341a8))
|
||||
* Typo fix in sample agent instruction ([ef3c745](https://github.com/google/adk-python/commit/ef3c745d655538ebd1ed735671be615f842341a8))
|
||||
* Update contributing links ([a1e1441](https://github.com/google/adk-python/commit/a1e14411159fd9f3e114e15b39b4949d0fd6ecb1))
|
||||
* Use starred tuple unpacking on GCS artifact blob names ([3b1d9a8](https://github.com/google/adk-python/commit/3b1d9a8a3e631ca2d86d30f09640497f1728986c))
|
||||
|
||||
|
||||
### Chore
|
||||
|
||||
* Do not send api request when session does not have events ([88a4402](https://github.com/google/adk-python/commit/88a4402d142672171d0a8ceae74671f47fa14289))
|
||||
* Leverage official uv action for install([09f1269](https://github.com/google/adk-python/commit/09f1269bf7fa46ab4b9324e7f92b4f70ffc923e5))
|
||||
* Update google-genai package and related deps to latest([ed7a21e](https://github.com/google/adk-python/commit/ed7a21e1890466fcdf04f7025775305dc71f603d))
|
||||
* Add credential service backed by session state([29cd183](https://github.com/google/adk-python/commit/29cd183aa1b47dc4f5d8afe22f410f8546634abc))
|
||||
* Clarify the behavior of Event.invocation_id([f033e40](https://github.com/google/adk-python/commit/f033e405c10ff8d86550d1419a9d63c0099182f9))
|
||||
* Send user message to the agent that returned a corresponding function call if user message is a function response([7c670f6](https://github.com/google/adk-python/commit/7c670f638bc17374ceb08740bdd057e55c9c2e12))
|
||||
* Add request converter to convert a2a request to ADK request([fb13963](https://github.com/google/adk-python/commit/fb13963deda0ff0650ac27771711ea0411474bf5))
|
||||
* Support allow_origins in cloud_run deployment ([2fd8feb](https://github.com/google/adk-python/commit/2fd8feb65d6ae59732fb3ec0652d5650f47132cc))
|
||||
|
||||
## [1.4.2](https://github.com/google/adk-python/compare/v1.4.1...v1.4.2) (2025-06-20)
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -200,7 +200,7 @@ For any changes that impact user-facing documentation (guides, API reference, tu
|
||||
|
||||
## Contributing Resources
|
||||
|
||||
[Contributing folder](https://github.com/google/adk-python/tree/main/contributing/samples) has resources that is helpful for contributors.
|
||||
[Contributing folder](https://github.com/google/adk-python/tree/main/contributing) has resources that is helpful for contributors.
|
||||
|
||||
|
||||
## Code reviews
|
||||
|
||||
@@ -135,7 +135,7 @@ adk eval \
|
||||
## 🤝 Contributing
|
||||
|
||||
We welcome contributions from the community! Whether it's bug reports, feature requests, documentation improvements, or code contributions, please see our
|
||||
- [General contribution guideline and flow](https://google.github.io/adk-docs/contributing-guide/#questions).
|
||||
- [General contribution guideline and flow](https://google.github.io/adk-docs/contributing-guide/).
|
||||
- Then if you want to contribute code, please read [Code Contributing Guidelines](./CONTRIBUTING.md) to get started.
|
||||
|
||||
## đź“„ License
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from adk_issue_formatting_agent.settings import GITHUB_BASE_URL
|
||||
from adk_issue_formatting_agent.settings import IS_INTERACTIVE
|
||||
from adk_issue_formatting_agent.settings import OWNER
|
||||
from adk_issue_formatting_agent.settings import REPO
|
||||
from adk_issue_formatting_agent.utils import error_response
|
||||
from adk_issue_formatting_agent.utils import get_request
|
||||
from adk_issue_formatting_agent.utils import post_request
|
||||
from adk_issue_formatting_agent.utils import read_file
|
||||
from google.adk import Agent
|
||||
import requests
|
||||
|
||||
BUG_REPORT_TEMPLATE = read_file(
|
||||
Path(__file__).parent / "../../../../.github/ISSUE_TEMPLATE/bug_report.md"
|
||||
)
|
||||
FREATURE_REQUEST_TEMPLATE = read_file(
|
||||
Path(__file__).parent
|
||||
/ "../../../../.github/ISSUE_TEMPLATE/feature_request.md"
|
||||
)
|
||||
|
||||
APPROVAL_INSTRUCTION = (
|
||||
"**Do not** wait or ask for user approval or confirmation for adding the"
|
||||
" comment."
|
||||
)
|
||||
if IS_INTERACTIVE:
|
||||
APPROVAL_INSTRUCTION = (
|
||||
"Ask for user approval or confirmation for adding the comment."
|
||||
)
|
||||
|
||||
|
||||
def list_open_issues(issue_count: int) -> dict[str, Any]:
|
||||
"""List most recent `issue_count` numer of open issues in the repo.
|
||||
|
||||
Args:
|
||||
issue_count: number of issues to return
|
||||
|
||||
Returns:
|
||||
The status of this request, with a list of issues when successful.
|
||||
"""
|
||||
url = f"{GITHUB_BASE_URL}/search/issues"
|
||||
query = f"repo:{OWNER}/{REPO} is:open is:issue"
|
||||
params = {
|
||||
"q": query,
|
||||
"sort": "created",
|
||||
"order": "desc",
|
||||
"per_page": issue_count,
|
||||
"page": 1,
|
||||
}
|
||||
|
||||
try:
|
||||
response = get_request(url, params)
|
||||
except requests.exceptions.RequestException as e:
|
||||
return error_response(f"Error: {e}")
|
||||
issues = response.get("items", None)
|
||||
return {"status": "success", "issues": issues}
|
||||
|
||||
|
||||
def get_issue(issue_number: int) -> dict[str, Any]:
|
||||
"""Get the details of the specified issue number.
|
||||
|
||||
Args:
|
||||
issue_number: issue number of the Github issue.
|
||||
|
||||
Returns:
|
||||
The status of this request, with the issue details when successful.
|
||||
"""
|
||||
url = f"{GITHUB_BASE_URL}/repos/{OWNER}/{REPO}/issues/{issue_number}"
|
||||
try:
|
||||
response = get_request(url)
|
||||
except requests.exceptions.RequestException as e:
|
||||
return error_response(f"Error: {e}")
|
||||
return {"status": "success", "issue": response}
|
||||
|
||||
|
||||
def add_comment_to_issue(issue_number: int, comment: str) -> dict[str, any]:
|
||||
"""Add the specified comment to the given issue number.
|
||||
|
||||
Args:
|
||||
issue_number: issue number of the Github issue
|
||||
comment: comment to add
|
||||
|
||||
Returns:
|
||||
The the status of this request, with the applied comment when successful.
|
||||
"""
|
||||
print(f"Attempting to add comment '{comment}' to issue #{issue_number}")
|
||||
url = f"{GITHUB_BASE_URL}/repos/{OWNER}/{REPO}/issues/{issue_number}/comments"
|
||||
payload = {"body": comment}
|
||||
|
||||
try:
|
||||
response = post_request(url, payload)
|
||||
except requests.exceptions.RequestException as e:
|
||||
return error_response(f"Error: {e}")
|
||||
return {
|
||||
"status": "success",
|
||||
"added_comment": response,
|
||||
}
|
||||
|
||||
|
||||
def list_comments_on_issue(issue_number: int) -> dict[str, any]:
|
||||
"""List all comments on the given issue number.
|
||||
|
||||
Args:
|
||||
issue_number: issue number of the Github issue
|
||||
|
||||
Returns:
|
||||
The the status of this request, with the list of comments when successful.
|
||||
"""
|
||||
print(f"Attempting to list comments on issue #{issue_number}")
|
||||
url = f"{GITHUB_BASE_URL}/repos/{OWNER}/{REPO}/issues/{issue_number}/comments"
|
||||
|
||||
try:
|
||||
response = get_request(url)
|
||||
except requests.exceptions.RequestException as e:
|
||||
return error_response(f"Error: {e}")
|
||||
return {"status": "success", "comments": response}
|
||||
|
||||
|
||||
root_agent = Agent(
|
||||
model="gemini-2.5-pro",
|
||||
name="adk_issue_formatting_assistant",
|
||||
description="Check ADK issue format and content.",
|
||||
instruction=f"""
|
||||
# 1. IDENTITY
|
||||
You are an AI assistant designed to help maintain the quality and consistency of issues in our GitHub repository.
|
||||
Your primary role is to act as a "GitHub Issue Format Validator." You will analyze new and existing **open** issues
|
||||
to ensure they contain all the necessary information as required by our templates. You are helpful, polite,
|
||||
and precise in your feedback.
|
||||
|
||||
# 2. CONTEXT & RESOURCES
|
||||
* **Repository:** You are operating on the GitHub repository `{OWNER}/{REPO}`.
|
||||
* **Bug Report Template:** (`{BUG_REPORT_TEMPLATE}`)
|
||||
* **Feature Request Template:** (`{FREATURE_REQUEST_TEMPLATE}`)
|
||||
|
||||
# 3. CORE MISSION
|
||||
Your goal is to check if a GitHub issue, identified as either a "bug" or a "feature request,"
|
||||
contains all the information required by the corresponding template. If it does not, your job is
|
||||
to post a single, helpful comment asking the original author to provide the missing information.
|
||||
{APPROVAL_INSTRUCTION}
|
||||
|
||||
**IMPORTANT NOTE:**
|
||||
* You add one comment at most each time you are invoked.
|
||||
* Don't proceed to other issues which are not the target issues.
|
||||
* Don't take any action on closed issues.
|
||||
|
||||
# 4. BEHAVIORAL RULES & LOGIC
|
||||
|
||||
## Step 1: Identify Issue Type & Applicability
|
||||
|
||||
Your first task is to determine if the issue is a valid target for validation.
|
||||
|
||||
1. **Assess Content Intent:** You must perform a quick semantic check of the issue's title, body, and comments.
|
||||
If you determine the issue's content is fundamentally *not* a bug report or a feature request
|
||||
(for example, it is a general question, a request for help, or a discussion prompt), then you must ignore it.
|
||||
2. **Exit Condition:** If the issue does not clearly fall into the categories of "bug" or "feature request"
|
||||
based on both its labels and its content, **take no action**.
|
||||
|
||||
## Step 2: Analyze the Issue Content
|
||||
|
||||
If you have determined the issue is a valid bug or feature request, your analysis depends on whether it has comments.
|
||||
|
||||
**Scenario A: Issue has NO comments**
|
||||
1. Read the main body of the issue.
|
||||
2. Compare the content of the issue body against the required headings/sections in the relevant template (Bug or Feature).
|
||||
3. Check for the presence of content under each heading. A heading with no content below it is considered incomplete.
|
||||
4. If one or more sections are missing or empty, proceed to Step 3.
|
||||
5. If all sections are filled out, your task is complete. Do nothing.
|
||||
|
||||
**Scenario B: Issue HAS one or more comments**
|
||||
1. First, analyze the main issue body to see which sections of the template are filled out.
|
||||
2. Next, read through **all** the comments in chronological order.
|
||||
3. As you read the comments, check if the information provided in them satisfies any of the template sections that were missing from the original issue body.
|
||||
4. After analyzing the body and all comments, determine if any required sections from the template *still* remain unaddressed.
|
||||
5. If one or more sections are still missing information, proceed to Step 3.
|
||||
6. If the issue body and comments *collectively* provide all the required information, your task is complete. Do nothing.
|
||||
|
||||
## Step 3: Formulate and Post a Comment (If Necessary)
|
||||
|
||||
If you determined in Step 2 that information is missing, you must post a **single comment** on the issue.
|
||||
|
||||
Please include a bolded note in your comment that this comment was added by an ADK agent.
|
||||
|
||||
**Comment Guidelines:**
|
||||
* **Be Polite and Helpful:** Start with a friendly tone.
|
||||
* **Be Specific:** Clearly list only the sections from the template that are still missing. Do not list sections that have already been filled out.
|
||||
* **Address the Author:** Mention the issue author by their username (e.g., `@username`).
|
||||
* **Provide Context:** Explain *why* the information is needed (e.g., "to help us reproduce the bug" or "to better understand your request").
|
||||
* **Do not be repetitive:** If you have already commented on an issue asking for information, do not comment again unless new information has been added and it's still incomplete.
|
||||
|
||||
**Example Comment for a Bug Report:**
|
||||
> **Response from ADK Agent**
|
||||
>
|
||||
> Hello @[issue-author-username], thank you for submitting this issue!
|
||||
>
|
||||
> To help us investigate and resolve this bug effectively, could you please provide the missing details for the following sections of our bug report template:
|
||||
>
|
||||
> * **To Reproduce:** (Please provide the specific steps required to reproduce the behavior)
|
||||
> * **Desktop (please complete the following information):** (Please provide OS, Python version, and ADK version)
|
||||
>
|
||||
> This information will give us the context we need to move forward. Thanks!
|
||||
|
||||
**Example Comment for a Feature Request:**
|
||||
> **Response from ADK Agent**
|
||||
>
|
||||
> Hi @[issue-author-username], thanks for this great suggestion!
|
||||
>
|
||||
> To help our team better understand and evaluate your feature request, could you please provide a bit more information on the following section:
|
||||
>
|
||||
> * **Is your feature request related to a problem? Please describe.**
|
||||
>
|
||||
> We look forward to hearing more about your idea!
|
||||
|
||||
# 5. FINAL INSTRUCTION
|
||||
|
||||
Execute this process for the given GitHub issue. Your final output should either be **[NO ACTION]**
|
||||
if the issue is complete or invalid, or **[POST COMMENT]** followed by the exact text of the comment you will post.
|
||||
|
||||
Please include your justification for your decision in your output.
|
||||
""",
|
||||
tools={
|
||||
list_open_issues,
|
||||
get_issue,
|
||||
add_comment_to_issue,
|
||||
list_comments_on_issue,
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,33 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv(override=True)
|
||||
|
||||
GITHUB_BASE_URL = "https://api.github.com"
|
||||
|
||||
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
|
||||
if not GITHUB_TOKEN:
|
||||
raise ValueError("GITHUB_TOKEN environment variable not set")
|
||||
|
||||
OWNER = os.getenv("OWNER", "google")
|
||||
REPO = os.getenv("REPO", "adk-python")
|
||||
EVENT_NAME = os.getenv("EVENT_NAME")
|
||||
ISSUE_NUMBER = os.getenv("ISSUE_NUMBER")
|
||||
ISSUE_COUNT_TO_PROCESS = os.getenv("ISSUE_COUNT_TO_PROCESS")
|
||||
|
||||
IS_INTERACTIVE = os.environ.get("INTERACTIVE", "1").lower() in ["true", "1"]
|
||||
@@ -0,0 +1,53 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from typing import Any
|
||||
|
||||
from adk_issue_formatting_agent.settings import GITHUB_TOKEN
|
||||
import requests
|
||||
|
||||
headers = {
|
||||
"Authorization": f"token {GITHUB_TOKEN}",
|
||||
"Accept": "application/vnd.github.v3+json",
|
||||
}
|
||||
|
||||
|
||||
def get_request(
|
||||
url: str, params: dict[str, Any] | None = None
|
||||
) -> dict[str, Any]:
|
||||
if params is None:
|
||||
params = {}
|
||||
response = requests.get(url, headers=headers, params=params, timeout=60)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
|
||||
def post_request(url: str, payload: Any) -> dict[str, Any]:
|
||||
response = requests.post(url, headers=headers, json=payload, timeout=60)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
|
||||
def error_response(error_message: str) -> dict[str, Any]:
|
||||
return {"status": "error", "message": error_message}
|
||||
|
||||
|
||||
def read_file(file_path: str) -> str:
|
||||
"""Read the content of the given file."""
|
||||
try:
|
||||
with open(file_path, "r") as f:
|
||||
return f.read()
|
||||
except FileNotFoundError:
|
||||
print(f"Error: File not found: {file_path}.")
|
||||
return ""
|
||||
@@ -31,7 +31,7 @@ root_agent = Agent(
|
||||
model='gemini-2.0-flash',
|
||||
name='log_agent',
|
||||
description='Log user query.',
|
||||
instruction="""Always log the user query and reploy "kk, I've logged."
|
||||
instruction="""Always log the user query and reply "kk, I've logged."
|
||||
""",
|
||||
tools=[log_query],
|
||||
generate_content_config=types.GenerateContentConfig(
|
||||
|
||||
@@ -60,7 +60,7 @@ bigquery_toolset = BigQueryToolset(
|
||||
# debug CLI
|
||||
root_agent = llm_agent.Agent(
|
||||
model="gemini-2.0-flash",
|
||||
name="hello_agent",
|
||||
name="bigquery_agent",
|
||||
description=(
|
||||
"Agent to answer questions about BigQuery data and models and execute"
|
||||
" SQL queries."
|
||||
|
||||
+14
-19
@@ -1,45 +1,40 @@
|
||||
# BigQuery Sample
|
||||
# Google API Tools Sample
|
||||
|
||||
## Introduction
|
||||
|
||||
This sample tests and demos the BigQuery support in ADK via two tools:
|
||||
This sample tests and demos Google API tools available in the
|
||||
`google.adk.tools.google_api_tool` module. We pick the following BigQuery API
|
||||
tools for this sample agent:
|
||||
|
||||
* 1. bigquery_datasets_list:
|
||||
1. `bigquery_datasets_list`: List user's datasets.
|
||||
|
||||
List user's datasets.
|
||||
2. `bigquery_datasets_get`: Get a dataset's details.
|
||||
|
||||
* 2. bigquery_datasets_get:
|
||||
Get a dataset's details.
|
||||
3. `bigquery_datasets_insert`: Create a new dataset.
|
||||
|
||||
* 3. bigquery_datasets_insert:
|
||||
Create a new dataset.
|
||||
4. `bigquery_tables_list`: List all tables in a dataset.
|
||||
|
||||
* 4. bigquery_tables_list:
|
||||
List all tables in a dataset.
|
||||
5. `bigquery_tables_get`: Get a table's details.
|
||||
|
||||
* 5. bigquery_tables_get:
|
||||
Get a table's details.
|
||||
|
||||
* 6. bigquery_tables_insert:
|
||||
Insert a new table into a dataset.
|
||||
6. `bigquery_tables_insert`: Insert a new table into a dataset.
|
||||
|
||||
## How to use
|
||||
|
||||
* 1. Follow https://developers.google.com/identity/protocols/oauth2#1.-obtain-oauth-2.0-credentials-from-the-dynamic_data.setvar.console_name. to get your client id and client secret.
|
||||
1. Follow https://developers.google.com/identity/protocols/oauth2#1.-obtain-oauth-2.0-credentials-from-the-dynamic_data.setvar.console_name. to get your client id and client secret.
|
||||
Be sure to choose "web" as your client type.
|
||||
|
||||
* 2. Configure your `.env` file to add two variables:
|
||||
2. Configure your `.env` file to add two variables:
|
||||
|
||||
* OAUTH_CLIENT_ID={your client id}
|
||||
* OAUTH_CLIENT_SECRET={your client secret}
|
||||
|
||||
Note: don't create a separate `.env` file , instead put it to the same `.env` file that stores your Vertex AI or Dev ML credentials
|
||||
|
||||
* 3. Follow https://developers.google.com/identity/protocols/oauth2/web-server#creatingcred to add http://localhost/dev-ui/ to "Authorized redirect URIs".
|
||||
3. Follow https://developers.google.com/identity/protocols/oauth2/web-server#creatingcred to add http://localhost/dev-ui/ to "Authorized redirect URIs".
|
||||
|
||||
Note: localhost here is just a hostname that you use to access the dev ui, replace it with the actual hostname you use to access the dev ui.
|
||||
|
||||
* 4. For 1st run, allow popup for localhost in Chrome.
|
||||
4. For 1st run, allow popup for localhost in Chrome.
|
||||
|
||||
## Sample prompt
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from . import agent
|
||||
+1
-1
@@ -40,7 +40,7 @@ bigquery_toolset = BigQueryToolset(
|
||||
|
||||
root_agent = Agent(
|
||||
model="gemini-2.0-flash",
|
||||
name="bigquery_agent",
|
||||
name="google_api_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.
|
||||
@@ -0,0 +1,16 @@
|
||||
# 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,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 random
|
||||
|
||||
from google.adk import Agent
|
||||
from google.adk.models.lite_llm import LiteLlm
|
||||
from langchain_core.utils.function_calling import convert_to_openai_function
|
||||
|
||||
|
||||
def roll_die(sides: int) -> 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.
|
||||
"""
|
||||
return random.randint(1, sides)
|
||||
|
||||
|
||||
def check_prime(number: int) -> str:
|
||||
"""Check if a given number is prime.
|
||||
|
||||
Args:
|
||||
number: The input number to check.
|
||||
|
||||
Returns:
|
||||
A str indicating the number is prime or not.
|
||||
"""
|
||||
if number <= 1:
|
||||
return f"{number} is not prime."
|
||||
is_prime = True
|
||||
for i in range(2, int(number**0.5) + 1):
|
||||
if number % i == 0:
|
||||
is_prime = False
|
||||
break
|
||||
if is_prime:
|
||||
return f"{number} is prime."
|
||||
else:
|
||||
return f"{number} is not prime."
|
||||
|
||||
|
||||
root_agent = Agent(
|
||||
model=LiteLlm(
|
||||
model="vertex_ai/meta/llama-4-maverick-17b-128e-instruct-maas",
|
||||
# If the model is not trained with functions and you would like to
|
||||
# enable function calling, you can add functions to the models, and the
|
||||
# functions will be added to the prompts during inferences.
|
||||
functions=[
|
||||
convert_to_openai_function(roll_die),
|
||||
convert_to_openai_function(check_prime),
|
||||
],
|
||||
),
|
||||
name="data_processing_agent",
|
||||
description="""You are a helpful assistant.""",
|
||||
instruction="""
|
||||
You are a helpful assistant, and call tools optionally.
|
||||
If call tools, the tool format should be in json, and the tool arguments should be parsed from users inputs.
|
||||
""",
|
||||
tools=[
|
||||
roll_die,
|
||||
check_prime,
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,81 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import asyncio
|
||||
import time
|
||||
|
||||
import agent
|
||||
from dotenv import load_dotenv
|
||||
from google.adk import Runner
|
||||
from google.adk.artifacts import InMemoryArtifactService
|
||||
from google.adk.cli.utils import logs
|
||||
from google.adk.sessions import InMemorySessionService
|
||||
from google.adk.sessions import Session
|
||||
from google.genai import types
|
||||
|
||||
load_dotenv(override=True)
|
||||
logs.log_to_tmp_folder()
|
||||
|
||||
|
||||
async def main():
|
||||
app_name = 'my_app'
|
||||
user_id_1 = 'user1'
|
||||
session_service = InMemorySessionService()
|
||||
artifact_service = InMemoryArtifactService()
|
||||
runner = Runner(
|
||||
app_name=app_name,
|
||||
agent=agent.root_agent,
|
||||
artifact_service=artifact_service,
|
||||
session_service=session_service,
|
||||
)
|
||||
session_11 = await session_service.create_session(
|
||||
app_name=app_name, user_id=user_id_1
|
||||
)
|
||||
|
||||
async def run_prompt(session: Session, new_message: str):
|
||||
content = types.Content(
|
||||
role='user', parts=[types.Part.from_text(text=new_message)]
|
||||
)
|
||||
print('** User says:', content.model_dump(exclude_none=True))
|
||||
async for event in runner.run_async(
|
||||
user_id=user_id_1,
|
||||
session_id=session.id,
|
||||
new_message=content,
|
||||
):
|
||||
if event.content.parts:
|
||||
part = event.content.parts[0]
|
||||
if part.text:
|
||||
print(f'** {event.author}: {part.text}')
|
||||
if part.function_call:
|
||||
print(f'** {event.author} calls tool: {part.function_call}')
|
||||
if part.function_response:
|
||||
print(
|
||||
f'** {event.author} gets tool response: {part.function_response}'
|
||||
)
|
||||
|
||||
start_time = time.time()
|
||||
print('Start time:', start_time)
|
||||
print('------------------------------------')
|
||||
await run_prompt(session_11, 'Hi, introduce yourself.')
|
||||
await run_prompt(session_11, 'Roll a die with 100 sides.')
|
||||
await run_prompt(session_11, 'Check if it is prime.')
|
||||
end_time = time.time()
|
||||
print('------------------------------------')
|
||||
print('End time:', end_time)
|
||||
print('Total time:', end_time - start_time)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
@@ -13,7 +13,6 @@
|
||||
# limitations under the License.
|
||||
|
||||
from datetime import datetime
|
||||
import json
|
||||
import os
|
||||
|
||||
from dotenv import load_dotenv
|
||||
@@ -27,8 +26,8 @@ from google.adk.auth import AuthCredential
|
||||
from google.adk.auth import AuthCredentialTypes
|
||||
from google.adk.auth import OAuth2Auth
|
||||
from google.adk.tools import ToolContext
|
||||
from google.adk.tools.authenticated_function_tool import AuthenticatedFunctionTool
|
||||
from google.adk.tools.google_api_tool import CalendarToolset
|
||||
from google.auth.transport.requests import Request
|
||||
from google.oauth2.credentials import Credentials
|
||||
from googleapiclient.discovery import build
|
||||
|
||||
@@ -56,6 +55,7 @@ def list_calendar_events(
|
||||
end_time: str,
|
||||
limit: int,
|
||||
tool_context: ToolContext,
|
||||
credential: AuthCredential,
|
||||
) -> list[dict]:
|
||||
"""Search for calendar events.
|
||||
|
||||
@@ -80,84 +80,11 @@ def list_calendar_events(
|
||||
Returns:
|
||||
list[dict]: A list of events that match the search criteria.
|
||||
"""
|
||||
creds = None
|
||||
|
||||
# Check if the tokes were already in the session state, which means the user
|
||||
# has already gone through the OAuth flow and successfully authenticated and
|
||||
# authorized the tool to access their calendar.
|
||||
if "calendar_tool_tokens" in tool_context.state:
|
||||
creds = Credentials.from_authorized_user_info(
|
||||
tool_context.state["calendar_tool_tokens"], SCOPES
|
||||
)
|
||||
if not creds or not creds.valid:
|
||||
# If the access token is expired, refresh it with the refresh token.
|
||||
if creds and creds.expired and creds.refresh_token:
|
||||
creds.refresh(Request())
|
||||
else:
|
||||
auth_scheme = OAuth2(
|
||||
flows=OAuthFlows(
|
||||
authorizationCode=OAuthFlowAuthorizationCode(
|
||||
authorizationUrl="https://accounts.google.com/o/oauth2/auth",
|
||||
tokenUrl="https://oauth2.googleapis.com/token",
|
||||
scopes={
|
||||
"https://www.googleapis.com/auth/calendar": (
|
||||
"See, edit, share, and permanently delete all the"
|
||||
" calendars you can access using Google Calendar"
|
||||
)
|
||||
},
|
||||
)
|
||||
)
|
||||
)
|
||||
auth_credential = AuthCredential(
|
||||
auth_type=AuthCredentialTypes.OAUTH2,
|
||||
oauth2=OAuth2Auth(
|
||||
client_id=oauth_client_id, client_secret=oauth_client_secret
|
||||
),
|
||||
)
|
||||
# If the user has not gone through the OAuth flow before, or the refresh
|
||||
# token also expired, we need to ask users to go through the OAuth flow.
|
||||
# First we check whether the user has just gone through the OAuth flow and
|
||||
# Oauth response is just passed back.
|
||||
auth_response = tool_context.get_auth_response(
|
||||
AuthConfig(
|
||||
auth_scheme=auth_scheme, raw_auth_credential=auth_credential
|
||||
)
|
||||
)
|
||||
if auth_response:
|
||||
# ADK exchanged the access token already for us
|
||||
access_token = auth_response.oauth2.access_token
|
||||
refresh_token = auth_response.oauth2.refresh_token
|
||||
|
||||
creds = Credentials(
|
||||
token=access_token,
|
||||
refresh_token=refresh_token,
|
||||
token_uri=auth_scheme.flows.authorizationCode.tokenUrl,
|
||||
client_id=oauth_client_id,
|
||||
client_secret=oauth_client_secret,
|
||||
scopes=list(auth_scheme.flows.authorizationCode.scopes.keys()),
|
||||
)
|
||||
else:
|
||||
# If there are no auth response which means the user has not gone
|
||||
# through the OAuth flow yet, we need to ask users to go through the
|
||||
# OAuth flow.
|
||||
tool_context.request_credential(
|
||||
AuthConfig(
|
||||
auth_scheme=auth_scheme,
|
||||
raw_auth_credential=auth_credential,
|
||||
)
|
||||
)
|
||||
# The return value is optional and could be any dict object. It will be
|
||||
# wrapped in a dict with key as 'result' and value as the return value
|
||||
# if the object returned is not a dict. This response will be passed
|
||||
# to LLM to generate a user friendly message. e.g. LLM will tell user:
|
||||
# "I need your authorization to access your calendar. Please authorize
|
||||
# me so I can check your meetings for today."
|
||||
return "Need User Authorization to access their calendar."
|
||||
# We store the access token and refresh token in the session state for the
|
||||
# next runs. This is just an example. On production, a tool should store
|
||||
# those credentials in some secure store or properly encrypt it before store
|
||||
# it in the session state.
|
||||
tool_context.state["calendar_tool_tokens"] = json.loads(creds.to_json())
|
||||
creds = Credentials(
|
||||
token=credential.oauth2.access_token,
|
||||
refresh_token=credential.oauth2.refresh_token,
|
||||
)
|
||||
|
||||
service = build("calendar", "v3", credentials=creds)
|
||||
events_result = (
|
||||
@@ -208,6 +135,33 @@ root_agent = Agent(
|
||||
|
||||
Currnet time: {_time}
|
||||
""",
|
||||
tools=[list_calendar_events, calendar_toolset],
|
||||
tools=[
|
||||
AuthenticatedFunctionTool(
|
||||
func=list_calendar_events,
|
||||
auth_config=AuthConfig(
|
||||
auth_scheme=OAuth2(
|
||||
flows=OAuthFlows(
|
||||
authorizationCode=OAuthFlowAuthorizationCode(
|
||||
authorizationUrl=(
|
||||
"https://accounts.google.com/o/oauth2/auth"
|
||||
),
|
||||
tokenUrl="https://oauth2.googleapis.com/token",
|
||||
scopes={
|
||||
"https://www.googleapis.com/auth/calendar": "",
|
||||
},
|
||||
)
|
||||
)
|
||||
),
|
||||
raw_auth_credential=AuthCredential(
|
||||
auth_type=AuthCredentialTypes.OAUTH2,
|
||||
oauth2=OAuth2Auth(
|
||||
client_id=oauth_client_id,
|
||||
client_secret=oauth_client_secret,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
calendar_toolset,
|
||||
],
|
||||
before_agent_callback=update_time,
|
||||
)
|
||||
|
||||
+4
-4
@@ -25,6 +25,7 @@ classifiers = [ # List of https://pypi.org/classifiers/
|
||||
]
|
||||
dependencies = [
|
||||
# go/keep-sorted start
|
||||
"PyYAML>=6.0.2", # For APIHubToolset.
|
||||
"anyio>=4.9.0;python_version>='3.10'", # For MCP Session Manager
|
||||
"authlib>=1.5.1", # For RestAPI Tool
|
||||
"click>=8.1.8", # For CLI tools
|
||||
@@ -34,7 +35,7 @@ dependencies = [
|
||||
"google-cloud-secret-manager>=2.22.0", # Fetching secrets in RestAPI Tool
|
||||
"google-cloud-speech>=2.30.0", # For Audio Transcription
|
||||
"google-cloud-storage>=2.18.0, <3.0.0", # For GCS Artifact service
|
||||
"google-genai>=1.17.0", # Google GenAI SDK
|
||||
"google-genai>=1.21.1", # Google GenAI SDK
|
||||
"graphviz>=0.20.2", # Graphviz for graph rendering
|
||||
"mcp>=1.8.0;python_version>='3.10'", # For MCP Toolset
|
||||
"opentelemetry-api>=1.31.0", # OpenTelemetry
|
||||
@@ -43,7 +44,6 @@ dependencies = [
|
||||
"pydantic>=2.0, <3.0.0", # For data validation/models
|
||||
"python-dateutil>=2.9.0.post0", # For Vertext AI Session Service
|
||||
"python-dotenv>=1.0.0", # To manage environment variables
|
||||
"PyYAML>=6.0.2", # For APIHubToolset.
|
||||
"requests>=2.32.4",
|
||||
"sqlalchemy>=2.0", # SQL database ORM
|
||||
"starlette>=0.46.2", # For FastAPI CLI
|
||||
@@ -70,9 +70,9 @@ dev = [
|
||||
# go/keep-sorted start
|
||||
"flit>=3.10.0",
|
||||
"isort>=6.0.0",
|
||||
"mypy>=1.15.0",
|
||||
"pyink>=24.10.0",
|
||||
"pylint>=2.6.0",
|
||||
"mypy>=1.15.0",
|
||||
# go/keep-sorted end
|
||||
]
|
||||
|
||||
@@ -87,6 +87,7 @@ eval = [
|
||||
"google-cloud-aiplatform[evaluation]>=1.87.0",
|
||||
"pandas>=2.2.3",
|
||||
"tabulate>=0.9.0",
|
||||
"rouge-score>=0.1.2",
|
||||
# go/keep-sorted end
|
||||
]
|
||||
|
||||
@@ -97,7 +98,6 @@ test = [
|
||||
"langgraph>=0.2.60", # For LangGraphAgent
|
||||
"litellm>=1.71.2", # For LiteLLM tests
|
||||
"llama-index-readers-file>=0.4.0", # For retrieval tests
|
||||
|
||||
"pytest-asyncio>=0.25.0",
|
||||
"pytest-mock>=3.14.0",
|
||||
"pytest-xdist>=3.6.1",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user