|
| 1 | +# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates. |
| 2 | +# |
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +# you may not use this file except in compliance with the License. |
| 5 | +# You may obtain a copy of the License at |
| 6 | +# |
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +# |
| 9 | +# Unless required by applicable law or agreed to in writing, software |
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +# See the License for the specific language governing permissions and |
| 13 | +# limitations under the License. |
| 14 | + |
| 15 | +from __future__ import annotations |
| 16 | +from typing import Literal, Optional, Union |
| 17 | + |
| 18 | +from a2a.types import AgentCard |
| 19 | +from google.adk.agents import BaseAgent |
| 20 | +from starlette.applications import Starlette |
| 21 | +from google.adk.artifacts.in_memory_artifact_service import InMemoryArtifactService |
| 22 | +from google.adk.sessions.in_memory_session_service import InMemorySessionService |
| 23 | +from google.adk.memory.in_memory_memory_service import InMemoryMemoryService |
| 24 | +from google.adk.a2a.utils.agent_to_a2a import to_a2a as google_adk_to_a2a |
| 25 | +from veadk import Runner |
| 26 | +from veadk.a2a.ve_middlewares import build_a2a_auth_middleware |
| 27 | +from veadk.auth.ve_credential_service import VeCredentialService |
| 28 | +from veadk.consts import DEFAULT_AGENT_NAME |
| 29 | + |
| 30 | + |
| 31 | +def to_a2a( |
| 32 | + agent: BaseAgent, |
| 33 | + *, |
| 34 | + host: str = "localhost", |
| 35 | + port: int = 8000, |
| 36 | + protocol: str = "http", |
| 37 | + agent_card: Optional[Union[AgentCard, str]] = None, |
| 38 | + runner: Optional[Runner] = None, |
| 39 | + enable_auth: bool = False, |
| 40 | + auth_method: Literal["header", "querystring"] = "header", |
| 41 | +) -> Starlette: |
| 42 | + """Convert an ADK agent to a A2A Starlette application with optional VeADK enhancements. |
| 43 | +
|
| 44 | + This function wraps Google ADK's to_a2a utility and optionally adds: |
| 45 | + - VeCredentialService for authentication management |
| 46 | + - A2A authentication middleware for token validation |
| 47 | +
|
| 48 | + Args: |
| 49 | + agent: The ADK agent to convert to A2A server |
| 50 | + host: The host for the A2A RPC URL (default: "localhost") |
| 51 | + port: The port for the A2A RPC URL (default: 8000) |
| 52 | + protocol: The protocol for the A2A RPC URL (default: "http") |
| 53 | + agent_card: Optional pre-built AgentCard object or path to agent card |
| 54 | + JSON. If not provided, will be built automatically from the |
| 55 | + agent. |
| 56 | + runner: Optional pre-built Runner object. If not provided, a default |
| 57 | + runner will be created using in-memory services. |
| 58 | + When enable_auth=True: |
| 59 | + - If runner is provided and has a credential_service, it must be |
| 60 | + a VeCredentialService instance (raises TypeError otherwise) |
| 61 | + - If runner is provided without credential_service, a new |
| 62 | + VeCredentialService will be created and set |
| 63 | + - If runner is not provided, a new runner with VeCredentialService |
| 64 | + will be created |
| 65 | + auth_method: Authentication method for A2A requests (only used when |
| 66 | + enable_auth=True). Options: |
| 67 | + - "header": Extract token from Authorization header (default) |
| 68 | + - "querystring": Extract token from query parameter |
| 69 | + enable_auth: Whether to enable VeADK authentication features. |
| 70 | + When True, enables credential service and auth middleware. |
| 71 | + When False, uses standard Google ADK behavior. |
| 72 | + Default: False |
| 73 | +
|
| 74 | + Returns: |
| 75 | + A Starlette application that can be run with uvicorn |
| 76 | +
|
| 77 | + Raises: |
| 78 | + TypeError: If enable_auth=True and runner has a credential_service |
| 79 | + that is not a VeCredentialService instance |
| 80 | +
|
| 81 | + Example: |
| 82 | + Basic usage (without VeADK auth): |
| 83 | + ```python |
| 84 | + from veadk import Agent |
| 85 | + from veadk.a2a.utils.agent_to_a2a import to_a2a |
| 86 | +
|
| 87 | + agent = Agent(name="my_agent", tools=[...]) |
| 88 | + app = to_a2a(agent, host="localhost", port=8000) |
| 89 | + # Run with: uvicorn module:app --host localhost --port 8000 |
| 90 | + ``` |
| 91 | +
|
| 92 | + With VeADK authentication enabled: |
| 93 | + ```python |
| 94 | + app = to_a2a(agent, enable_auth=True) |
| 95 | + ``` |
| 96 | +
|
| 97 | + With custom runner and VeADK auth: |
| 98 | + ```python |
| 99 | + from veadk import Agent, Runner |
| 100 | + from veadk.memory.short_term_memory import ShortTermMemory |
| 101 | + from veadk.auth.ve_credential_service import VeCredentialService |
| 102 | +
|
| 103 | + agent = Agent(name="my_agent") |
| 104 | + runner = Runner( |
| 105 | + agent=agent, |
| 106 | + short_term_memory=ShortTermMemory(), |
| 107 | + app_name="my_app", |
| 108 | + credential_service=VeCredentialService() # Optional |
| 109 | + ) |
| 110 | + app = to_a2a(agent, runner=runner, enable_auth=True) |
| 111 | + ``` |
| 112 | +
|
| 113 | + With querystring authentication: |
| 114 | + ```python |
| 115 | + app = to_a2a(agent, enable_auth=True, auth_method="querystring") |
| 116 | + ``` |
| 117 | + """ |
| 118 | + app_name = agent.name or DEFAULT_AGENT_NAME |
| 119 | + middleware = None # May need support multiple middlewares in the future |
| 120 | + |
| 121 | + # Handle VeADK authentication setup |
| 122 | + if enable_auth: |
| 123 | + # Create credential service if not provided |
| 124 | + credential_service = VeCredentialService() |
| 125 | + if runner is not None: |
| 126 | + # Check if runner has credential_service |
| 127 | + if runner.credential_service is not None: |
| 128 | + # Validate that it's a VeCredentialService |
| 129 | + if not isinstance(runner.credential_service, VeCredentialService): |
| 130 | + raise TypeError( |
| 131 | + f"When enable_auth=True, runner.credential_service must be " |
| 132 | + f"a VeCredentialService instance, got {type(runner.credential_service).__name__}" |
| 133 | + ) |
| 134 | + # Use existing credential service |
| 135 | + credential_service = runner.credential_service |
| 136 | + else: |
| 137 | + # Add credential_service to runner |
| 138 | + runner.credential_service = credential_service |
| 139 | + else: |
| 140 | + # Create runner with credential_service |
| 141 | + runner = Runner( |
| 142 | + app_name=app_name, |
| 143 | + agent=agent, |
| 144 | + artifact_service=InMemoryArtifactService(), |
| 145 | + session_service=InMemorySessionService(), |
| 146 | + memory_service=InMemoryMemoryService(), |
| 147 | + credential_service=credential_service, |
| 148 | + ) |
| 149 | + |
| 150 | + middleware = build_a2a_auth_middleware( |
| 151 | + app_name=app_name, |
| 152 | + credential_service=credential_service, |
| 153 | + auth_method=auth_method, |
| 154 | + ) |
| 155 | + |
| 156 | + # Convert agent to A2A Starlette app using Google ADK utility |
| 157 | + app: Starlette = google_adk_to_a2a( |
| 158 | + agent=agent, |
| 159 | + host=host, |
| 160 | + port=port, |
| 161 | + protocol=protocol, |
| 162 | + agent_card=agent_card, |
| 163 | + runner=runner, |
| 164 | + ) |
| 165 | + |
| 166 | + # Add VeADK authentication middleware only if enabled |
| 167 | + if middleware: |
| 168 | + app.add_middleware(middleware) |
| 169 | + |
| 170 | + return app |
0 commit comments