Create a developer folder and add samples.

PiperOrigin-RevId: 755885332
This commit is contained in:
Hangfei Lin
2025-05-07 09:25:42 -07:00
committed by Copybara-Service
parent 180c2a934b
commit a4adb739c0
49 changed files with 3194 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
# OAuth Sample
## Introduction
This sample tests and demos the OAuth support in ADK via two tools:
* 1. list_calendar_events
This is a customized tool that calls Google Calendar API to list calendar events.
It pass in the client id and client secrete to ADK and then get back the access token from ADK.
And then it uses the access token to call calendar api.
* 2. get_calendar_events
This is an google calendar tool that calls Google Calendar API to get the details of a specific calendar.
This tool is from the ADK built-in Google Calendar ToolSet.
Everything is wrapped and the tool user just needs to pass in the client id and client secret.
## 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.
Be sure to choose "web" as your client type.
* 2. Configure your .env file to add two variables:
* GOOGLE_CLIENT_ID={your client id}
* GOOGLE_CLIENT_SECRET={your client secret}
Note: done't create a separate .env , 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".
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.
## Sample prompt
* `List all my today's meeting from 7am to 7pm.`
* `Get the details of the first event.`

View File

@@ -0,0 +1,15 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from . import agent

View File

@@ -0,0 +1,215 @@
# 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 datetime import datetime
import json
import os
from dotenv import load_dotenv
from fastapi.openapi.models import OAuth2
from fastapi.openapi.models import OAuthFlowAuthorizationCode
from fastapi.openapi.models import OAuthFlows
from google.adk import Agent
from google.adk.agents.callback_context import CallbackContext
from google.adk.auth import AuthConfig
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.google_api_tool import calendar_tool_set
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
# 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")
SCOPES = ["https://www.googleapis.com/auth/calendar"]
calendar_tool_set.configure_auth(
client_id=oauth_client_id, client_secret=oauth_client_secret
)
get_calendar_events = calendar_tool_set.get_tool("calendar_events_get")
# list_calendar_events = calendar_tool_set.get_tool("calendar_events_list")
# you can replace below customized list_calendar_events tool with above ADK
# build-in google calendar tool which is commented for now to acheive same
# effect.
def list_calendar_events(
start_time: str,
end_time: str,
limit: int,
tool_context: ToolContext,
) -> list[dict]:
"""Search for calendar events.
Example:
flights = get_calendar_events(
calendar_id='joedoe@gmail.com',
start_time='2024-09-17T06:00:00',
end_time='2024-09-17T12:00:00',
limit=10
)
# Returns up to 10 calendar events between 6:00 AM and 12:00 PM on
September 17, 2024.
Args:
calendar_id (str): the calendar ID to search for events.
start_time (str): The start of the time range (format is
YYYY-MM-DDTHH:MM:SS).
end_time (str): The end of the time range (format is YYYY-MM-DDTHH:MM:SS).
limit (int): The maximum number of results to return.
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())
service = build("calendar", "v3", credentials=creds)
events_result = (
service.events()
.list(
calendarId="primary",
timeMin=start_time + "Z" if start_time else None,
timeMax=end_time + "Z" if end_time else None,
maxResults=limit,
singleEvents=True,
orderBy="startTime",
)
.execute()
)
events = events_result.get("items", [])
return events
def update_time(callback_context: CallbackContext):
# get current date time
now = datetime.now()
formatted_time = now.strftime("%Y-%m-%d %H:%M:%S")
callback_context.state["_time"] = formatted_time
root_agent = Agent(
model="gemini-2.0-flash",
name="calendar_agent",
instruction="""
You are a helpful personal calendar assistant.
Use the provided tools to search for calendar events (use 10 as limit if user does't specify), and update them.
Use "primary" as the calendarId if users don't specify.
Scenario1:
The user want to query the calendar events.
Use list_calendar_events to search for calendar events.
Scenario2:
User want to know the details of one of the listed calendar events.
Use get_calendar_event to get the details of a calendar event.
Current user:
<User>
{userInfo?}
</User>
Currnet time: {_time}
""",
tools=[list_calendar_events, get_calendar_events],
before_agent_callback=update_time,
)