Skip to content

Commit 49b8d7b

Browse files
mikeas1Gerrit Code Review
authored andcommitted
Merge "Create an example showing an ADK agent using an authenticated tool." into main
2 parents a284881 + 618d603 commit 49b8d7b

File tree

11 files changed

+1721
-363
lines changed

11 files changed

+1721
-363
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# ADK Agent with Authenticated Tools
2+
3+
This example shows how to create an A2A Server that uses an ADK-based Agent that uses Google-authenticated tools.
4+
5+
## Prerequisites
6+
7+
- Python 3.9 or higher
8+
- [UV](https://docs.astral.sh/uv/)
9+
- A Gemini API Key
10+
- A [Google OAuth Client](https://developers.google.com/identity/openid-connect/openid-connect#getcredentials)
11+
- Configure your OAuth client to handle redirect URLs at `localhost:10007/authenticate`
12+
13+
## Running the example
14+
15+
1. Create the .env file with your API Key and OAuth2.0 Client details
16+
17+
```bash
18+
echo "GOOGLE_API_KEY=your_api_key_here" > .env
19+
echo "GOOGLE_CLIENT_ID=your_client_id_here" >> .env
20+
echo "GOOGLE_CLIENT_SECRET=your_client_secret_here" >> .env
21+
```
22+
23+
2. Run the example
24+
25+
```
26+
uv run .
27+
```
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import logging
2+
import os
3+
import sys
4+
5+
import click
6+
import uvicorn
7+
8+
from adk_agent_executor import ADKAgentExecutor
9+
from adk_agent import create_agent
10+
from dotenv import load_dotenv
11+
12+
from a2a.server.tasks import InMemoryTaskStore
13+
from a2a.server.apps import A2AStarletteApplication
14+
from a2a.server.request_handlers import DefaultRequestHandler
15+
from a2a.types import (
16+
AgentAuthentication,
17+
AgentCapabilities,
18+
AgentCard,
19+
AgentSkill,
20+
)
21+
from google.adk.artifacts import InMemoryArtifactService
22+
from google.adk.memory.in_memory_memory_service import InMemoryMemoryService
23+
from google.adk.runners import Runner
24+
from google.adk.sessions import InMemorySessionService
25+
from starlette.applications import Starlette
26+
from starlette.routing import Route
27+
from starlette.requests import Request
28+
from starlette.responses import PlainTextResponse
29+
30+
load_dotenv()
31+
32+
logging.basicConfig()
33+
34+
35+
@click.command()
36+
@click.option('--host', 'host', default='localhost')
37+
@click.option('--port', 'port', default=10007)
38+
def main(host: str, port: int):
39+
# Verify an API key is set. Not required if using Vertex AI APIs, since those can use gcloud credentials.
40+
if not os.getenv('GOOGLE_GENAI_USE_VERTEXAI') == 'TRUE':
41+
if not os.getenv('GOOGLE_API_KEY'):
42+
raise Exception(
43+
'GOOGLE_API_KEY environment variable not set and GOOGLE_GENAI_USE_VERTEXAI is not TRUE.'
44+
)
45+
46+
skill = AgentSkill(
47+
id='check_availability',
48+
name='Check Availability',
49+
description="Checks a user's availability for a time using their Google Calendar",
50+
tags=['calendar'],
51+
examples=['Am I free from 10am to 11am tomorrow?'],
52+
)
53+
54+
agent_card = AgentCard(
55+
name='Calendar Agent',
56+
description="An agent that can manage a user's calendar",
57+
url=f'http://{host}:{port}/',
58+
version='1.0.0',
59+
defaultInputModes=['text'],
60+
defaultOutputModes=['text'],
61+
capabilities=AgentCapabilities(streaming=True),
62+
skills=[skill],
63+
authentication=AgentAuthentication(schemes=['public']),
64+
)
65+
66+
adk_agent = create_agent(
67+
client_id=os.getenv('GOOGLE_CLIENT_ID'),
68+
client_secret=os.getenv('GOOGLE_CLIENT_SECRET'),
69+
)
70+
runner = Runner(
71+
app_name=agent_card.name,
72+
agent=adk_agent,
73+
artifact_service=InMemoryArtifactService(),
74+
session_service=InMemorySessionService(),
75+
memory_service=InMemoryMemoryService(),
76+
)
77+
agent_executor = ADKAgentExecutor(runner, agent_card)
78+
79+
async def handle_auth(request: Request) -> PlainTextResponse:
80+
await agent_executor.on_auth_callback(
81+
request.query_params.get('state'), str(request.url)
82+
)
83+
return PlainTextResponse('Authentication successful.')
84+
85+
request_handler = DefaultRequestHandler(
86+
agent_executor=agent_executor, task_store=InMemoryTaskStore()
87+
)
88+
89+
a2a_app = A2AStarletteApplication(
90+
agent_card=agent_card, http_handler=request_handler
91+
)
92+
routes = a2a_app.routes()
93+
routes.append(
94+
Route(
95+
path='/authenticate',
96+
methods=['GET'],
97+
endpoint=handle_auth,
98+
)
99+
)
100+
app = Starlette(routes=routes)
101+
102+
uvicorn.run(app, host=host, port=port)
103+
104+
105+
if __name__ == '__main__':
106+
main()
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import datetime
2+
3+
from google.adk.agents import LlmAgent
4+
from google.adk.tools.google_api_tool import calendar_tool_set
5+
6+
7+
def create_agent(client_id, client_secret) -> LlmAgent:
8+
"""Constructs the ADK agent."""
9+
calendar_tool_set.configure_auth(client_id, client_secret)
10+
return LlmAgent(
11+
model='gemini-2.0-flash-001',
12+
name='calendar_agent',
13+
description="An agent that can help manage a user's calendar",
14+
instruction=f"""
15+
You are an agent that can help manage a user's calendar.
16+
17+
Users will request information about the state of their calendar or to make changes to
18+
their calendar. Use the provided tools for interacting with the calendar API.
19+
20+
If not specified, assume the calendar the user wants is the 'primary' calendar.
21+
22+
When using the Calendar API tools, use well-formed RFC3339 timestamps.
23+
24+
Today is {datetime.datetime.now()}.
25+
""",
26+
tools=calendar_tool_set.get_tools(),
27+
)

0 commit comments

Comments
 (0)