mirror of
https://github.com/google/adk-docs.git
synced 2026-09-14 16:16:59 +08:00
150 lines
6.4 KiB
Python
150 lines
6.4 KiB
Python
import asyncio
|
|
from dotenv import load_dotenv
|
|
from google.adk.artifacts.in_memory_artifact_service import InMemoryArtifactService
|
|
from google.adk.runners import Runner
|
|
from google.adk.sessions import InMemorySessionService
|
|
from google.genai import types
|
|
|
|
from .helpers import is_pending_auth_event, get_function_call_id, get_function_call_auth_config, get_user_input
|
|
from .tools_and_agent import root_agent
|
|
|
|
load_dotenv()
|
|
|
|
agent = root_agent
|
|
|
|
async def async_main():
|
|
"""
|
|
Main asynchronous function orchestrating the agent interaction and authentication flow.
|
|
"""
|
|
# --- Step 1: Service Initialization ---
|
|
# Use in-memory services for session and artifact storage (suitable for demos/testing).
|
|
session_service = InMemorySessionService()
|
|
artifacts_service = InMemoryArtifactService()
|
|
|
|
# Create a new user session to maintain conversation state.
|
|
session = session_service.create_session(
|
|
state={}, # Optional state dictionary for session-specific data
|
|
app_name='my_app', # Application identifier
|
|
user_id='user' # User identifier
|
|
)
|
|
|
|
# --- Step 2: Initial User Query ---
|
|
# Define the user's initial request.
|
|
query = 'Show me my user info'
|
|
print(f"user: {query}")
|
|
|
|
# Format the query into the Content structure expected by the ADK Runner.
|
|
content = types.Content(role='user', parts=[types.Part(text=query)])
|
|
|
|
# Initialize the ADK Runner
|
|
runner = Runner(
|
|
app_name='my_app',
|
|
agent=agent,
|
|
artifact_service=artifacts_service,
|
|
session_service=session_service,
|
|
)
|
|
|
|
# --- Step 3: Send Query and Handle Potential Auth Request ---
|
|
print("\nRunning agent with initial query...")
|
|
events_async = runner.run_async(
|
|
session_id=session.id, user_id='user', new_message=content
|
|
)
|
|
|
|
# Variables to store details if an authentication request occurs.
|
|
auth_request_event_id, auth_config = None, None
|
|
|
|
# Iterate through the events generated by the first run.
|
|
async for event in events_async:
|
|
# Check if this event is the specific 'adk_request_credential' function call.
|
|
if is_pending_auth_event(event):
|
|
print("--> Authentication required by agent.")
|
|
auth_request_event_id = get_function_call_id(event)
|
|
auth_config = get_function_call_auth_config(event)
|
|
# Once the auth request is found and processed, exit this loop.
|
|
# We need to pause execution here to get user input for authentication.
|
|
break
|
|
|
|
|
|
# If no authentication request was detected after processing all events, exit.
|
|
if not auth_request_event_id or not auth_config:
|
|
print("\nAuthentication not required for this query or processing finished.")
|
|
return # Exit the main function
|
|
|
|
# --- Step 4: Manual Authentication Step (Simulated OAuth 2.0 Flow) ---
|
|
# This section simulates the user interaction part of an OAuth 2.0 flow.
|
|
# In a real web application, this would involve browser redirects.
|
|
|
|
# Define the Redirect URI. This *must* match one of the URIs registered
|
|
# with the OAuth provider for your application. The provider sends the user
|
|
# back here after they approve the request.
|
|
redirect_uri = 'http://localhost:8000/dev-ui' # Example for local development
|
|
|
|
# Construct the Authorization URL that the user must visit.
|
|
# This typically includes the provider's authorization endpoint URL,
|
|
# client ID, requested scopes, response type (e.g., 'code'), and the redirect URI.
|
|
# Here, we retrieve the base authorization URI from the AuthConfig provided by ADK
|
|
# and append the redirect_uri.
|
|
# NOTE: A robust implementation would use urlencode and potentially add state, scope, etc.
|
|
auth_request_uri = (
|
|
auth_config.exchanged_auth_credential.oauth2.auth_uri
|
|
+ f'&redirect_uri={redirect_uri}' # Simple concatenation; ensure correct query param format
|
|
)
|
|
|
|
print("\n--- User Action Required ---")
|
|
# Prompt the user to visit the authorization URL, log in, grant permissions,
|
|
# and then paste the *full* URL they are redirected back to (which contains the auth code).
|
|
auth_response_uri = await get_user_input(
|
|
f'1. Please open this URL in your browser to log in:\n {auth_request_uri}\n\n'
|
|
f'2. After successful login and authorization, your browser will be redirected.\n'
|
|
f' Copy the *entire* URL from the browser\'s address bar.\n\n'
|
|
f'3. Paste the copied URL here and press Enter:\n\n> '
|
|
)
|
|
|
|
# --- Step 5: Prepare Authentication Response for the Agent ---
|
|
# Update the AuthConfig object with the information gathered from the user.
|
|
# The ADK framework needs the full response URI (containing the code)
|
|
# and the original redirect URI to complete the OAuth token exchange process internally.
|
|
auth_config.exchanged_auth_credential.oauth2.auth_response_uri = auth_response_uri
|
|
auth_config.exchanged_auth_credential.oauth2.redirect_uri = redirect_uri
|
|
|
|
# Construct a FunctionResponse Content object to send back to the agent/runner.
|
|
# This response explicitly targets the 'adk_request_credential' function call
|
|
# identified earlier by its ID.
|
|
auth_content = types.Content(
|
|
role='user',
|
|
parts=[
|
|
types.Part(
|
|
function_response=types.FunctionResponse(
|
|
# Crucially, link this response to the original request using the saved ID.
|
|
id=auth_request_event_id,
|
|
# The special name of the function call we are responding to.
|
|
name='adk_request_credential',
|
|
# The payload containing all necessary authentication details.
|
|
response=auth_config.model_dump(),
|
|
)
|
|
)
|
|
],
|
|
)
|
|
|
|
# --- Step 6: Resume Execution with Authentication ---
|
|
print("\nSubmitting authentication details back to the agent...")
|
|
# Run the agent again, this time providing the `auth_content` (FunctionResponse).
|
|
# The ADK Runner intercepts this, processes the 'adk_request_credential' response
|
|
# (performs token exchange, stores credentials), and then allows the agent
|
|
# to retry the original tool call that required authentication, now succeeding with
|
|
# a valid access token embedded.
|
|
events_async = runner.run_async(
|
|
session_id=session.id,
|
|
user_id='user',
|
|
new_message=auth_content, # Provide the prepared auth response
|
|
)
|
|
|
|
# Process and print the final events from the agent after authentication is complete.
|
|
# This stream now contain the actual result from the tool (e.g., the user info).
|
|
print("\n--- Agent Response after Authentication ---")
|
|
async for event in events_async:
|
|
print(event)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
asyncio.run(async_main()) |