Skip to content

Commit 38587d7

Browse files
google-genai-botcopybara-github
authored andcommitted
refactor: switch cli and agent_registry to the a2a-sdk compatibility shim
make cli/fast_api.py and agent_registry/agent_registry.py compatible with both a2a 0.3 and 1.0 by calling _compat helpers instead of a2a.* directly PiperOrigin-RevId: 944988024
1 parent 930e472 commit 38587d7

4 files changed

Lines changed: 105 additions & 60 deletions

File tree

src/google/adk/cli/fast_api.py

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -679,12 +679,9 @@ async def _a2a_lifespan(app_instance: FastAPI):
679679
_register_builder_endpoints(app, web, agents_dir)
680680

681681
if a2a and a2a_task_store is not None:
682-
from a2a.server.apps import A2AStarletteApplication
683-
from a2a.server.request_handlers import DefaultRequestHandler
684682
from a2a.server.tasks import InMemoryPushNotificationConfigStore
685-
from a2a.types import AgentCard
686-
from a2a.utils.constants import AGENT_CARD_WELL_KNOWN_PATH
687683

684+
from ..a2a import _compat
688685
from ..a2a.executor.a2a_agent_executor import A2aAgentExecutor
689686

690687
# locate all a2a agent apps in the agents directory
@@ -720,29 +717,19 @@ async def _get_a2a_runner_async() -> Runner:
720717

721718
push_config_store = InMemoryPushNotificationConfigStore()
722719

723-
request_handler = DefaultRequestHandler(
724-
agent_executor=agent_executor,
725-
task_store=a2a_task_store,
726-
push_config_store=push_config_store,
727-
)
728-
729720
with (p / "agent.json").open("r", encoding="utf-8") as f:
730721
data = json.load(f)
731-
agent_card = AgentCard(**data)
722+
agent_card = _compat.parse_agent_card(data)
732723

733-
a2a_app = A2AStarletteApplication(
724+
_compat.attach_a2a_routes_to_app(
725+
app,
734726
agent_card=agent_card,
735-
http_handler=request_handler,
736-
)
737-
738-
routes = a2a_app.routes(
739-
rpc_url=f"/a2a/{app_name}",
740-
agent_card_url=f"/a2a/{app_name}{AGENT_CARD_WELL_KNOWN_PATH}",
727+
agent_executor=agent_executor,
728+
task_store=a2a_task_store,
729+
push_config_store=push_config_store,
730+
prefix=f"/a2a/{app_name}",
741731
)
742732

743-
for new_route in routes:
744-
app.router.routes.append(new_route)
745-
746733
logger.info("Successfully configured A2A agent: %s", app_name)
747734

748735
except Exception as e:
@@ -756,7 +743,6 @@ async def _get_a2a_runner_async() -> Runner:
756743
)
757744

758745
import inspect
759-
import json
760746

761747
from google.adk.agents import Agent
762748
import google.auth

src/google/adk/integrations/agent_registry/agent_registry.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,8 @@
4848

4949
# pylint: disable=g-import-not-at-top
5050
try:
51-
from a2a.types import AgentCapabilities
52-
from a2a.types import AgentCard
5351
from a2a.types import AgentSkill
54-
from a2a.types import TransportProtocol as A2ATransport
52+
from google.adk.a2a import _compat
5553
from google.adk.agents.remote_a2a_agent import RemoteA2aAgent
5654
except ImportError as e:
5755
raise ImportError(
@@ -66,9 +64,9 @@
6664
AGENT_REGISTRY_MTLS_BASE_URL = "https://agentregistry.mtls.googleapis.com/v1"
6765

6866
_TRANSPORT_MAPPING = {
69-
"HTTP_JSON": A2ATransport.http_json,
70-
"JSONRPC": A2ATransport.jsonrpc,
71-
"GRPC": A2ATransport.grpc,
67+
"HTTP_JSON": _compat.TP_HTTP_JSON,
68+
"JSONRPC": _compat.TP_JSONRPC,
69+
"GRPC": _compat.TP_GRPC,
7270
}
7371

7472

@@ -273,8 +271,8 @@ def _get_connection_uri(
273271
self,
274272
resource_details: Mapping[str, Any],
275273
protocol_type: _ProtocolType | None = None,
276-
protocol_binding: A2ATransport | None = None,
277-
) -> str | None:
274+
protocol_binding: _compat.TransportProtocol | None = None,
275+
) -> tuple[Any, Any, Any]:
278276
"""Extracts the first matching URI based on type and binding filters."""
279277
protocols = list(resource_details.get("protocols", []))
280278
if "interfaces" in resource_details:
@@ -354,11 +352,11 @@ def get_mcp_toolset(
354352
mcp_server_id = None
355353

356354
endpoint_uri, _, _ = self._get_connection_uri(
357-
server_details, protocol_binding=A2ATransport.jsonrpc
355+
server_details, protocol_binding=_compat.TP_JSONRPC
358356
)
359357
if not endpoint_uri:
360358
endpoint_uri, _, _ = self._get_connection_uri(
361-
server_details, protocol_binding=A2ATransport.http_json
359+
server_details, protocol_binding=_compat.TP_HTTP_JSON
362360
)
363361
if not endpoint_uri:
364362
raise ValueError(
@@ -492,7 +490,7 @@ def get_remote_a2a_agent(
492490
card = agent_info.get("card", {})
493491
card_content = card.get("content")
494492
if card.get("type") == "A2A_AGENT_CARD" and card_content:
495-
agent_card = AgentCard(**card_content)
493+
agent_card = _compat.parse_agent_card(card_content)
496494
# Clean the name to be a valid identifier
497495
name = self._clean_name(agent_card.name)
498496

@@ -525,17 +523,17 @@ def get_remote_a2a_agent(
525523
)
526524
)
527525

528-
agent_card = AgentCard(
526+
binding = protocol_binding or _compat.TP_HTTP_JSON
527+
agent_card = _compat.build_agent_card(
529528
name=name,
530529
description=description,
531530
version=version,
532-
preferredTransport=protocol_binding or A2ATransport.http_json,
533-
protocolVersion=protocol_version or "0.3.0",
534531
url=url,
532+
protocol_binding=getattr(binding, "value", binding),
533+
protocol_version=protocol_version,
535534
skills=skills,
536-
capabilities=AgentCapabilities(streaming=False, polling=False),
537-
defaultInputModes=["text"],
538-
defaultOutputModes=["text"],
535+
default_input_modes=["text"],
536+
default_output_modes=["text"],
539537
)
540538

541539
return RemoteA2aAgent(

tests/unittests/cli/test_fast_api.py

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,15 @@
2222
from typing import Any
2323
from typing import Optional
2424
from unittest.mock import AsyncMock
25-
from unittest.mock import call
2625
from unittest.mock import MagicMock
2726
from unittest.mock import patch
2827
from urllib.parse import quote
2928

3029
from fastapi.testclient import TestClient
30+
from google.adk.a2a import _compat
3131
from google.adk.agents.base_agent import BaseAgent
3232
from google.adk.agents.llm_agent import LlmAgent
3333
from google.adk.agents.run_config import RunConfig
34-
from google.adk.apps.app import App
3534
from google.adk.artifacts.base_artifact_service import ArtifactVersion
3635
from google.adk.cli import fast_api as fast_api_module
3736
from google.adk.cli.fast_api import get_fast_api_app
@@ -46,7 +45,6 @@
4645
from google.adk.plugins.bigquery_agent_analytics_plugin import BigQueryAgentAnalyticsPlugin
4746
from google.adk.runners import Runner
4847
from google.adk.sessions.in_memory_session_service import InMemorySessionService
49-
from google.adk.sessions.session import Session
5048
from google.genai import types
5149
from pydantic import BaseModel
5250
import pytest
@@ -841,8 +839,11 @@ def temp_agents_dir_with_a2a():
841839
"name": "test_a2a_agent",
842840
"description": "Test A2A agent",
843841
"version": "1.0.0",
844-
"author": "test",
845-
"capabilities": ["text"],
842+
"url": "http://localhost:8000/a2a/test_a2a_agent",
843+
"capabilities": {},
844+
"defaultInputModes": ["text/plain"],
845+
"defaultOutputModes": ["text/plain"],
846+
"skills": [],
846847
}
847848

848849
with open(agent_dir / "agent.json", "w") as f:
@@ -2041,6 +2042,12 @@ def test_openapi_json_schema_accessible(test_app):
20412042
logger.info("OpenAPI /openapi.json endpoint is accessible")
20422043

20432044

2045+
@pytest.mark.skipif(
2046+
_compat.IS_A2A_V1,
2047+
reason=(
2048+
"0.3.x-only: mocks server.apps.A2AStarletteApplication (gone in 1.x)"
2049+
),
2050+
)
20442051
def test_a2a_agent_discovery(test_app_with_a2a):
20452052
"""Test that A2A agents are properly discovered and configured."""
20462053
# This test mainly verifies that the A2A setup doesn't break the app
@@ -2049,6 +2056,12 @@ def test_a2a_agent_discovery(test_app_with_a2a):
20492056
logger.info("A2A agent discovery test passed")
20502057

20512058

2059+
@pytest.mark.skipif(
2060+
_compat.IS_A2A_V1,
2061+
reason=(
2062+
"0.3.x-only: mocks server.apps.A2AStarletteApplication (gone in 1.x)"
2063+
),
2064+
)
20522065
def test_a2a_request_handler_uses_push_config_store(
20532066
mock_session_service,
20542067
mock_artifact_service,
@@ -2131,6 +2144,12 @@ def test_a2a_request_handler_uses_push_config_store(
21312144
)
21322145

21332146

2147+
@pytest.mark.skipif(
2148+
_compat.IS_A2A_V1,
2149+
reason=(
2150+
"0.3.x-only: mocks server.apps.A2AStarletteApplication (gone in 1.x)"
2151+
),
2152+
)
21342153
def test_a2a_request_handler_uses_task_store_uri(
21352154
mock_session_service,
21362155
mock_artifact_service,
@@ -2211,6 +2230,12 @@ def test_a2a_request_handler_uses_task_store_uri(
22112230
assert call_kwargs["task_store"] is custom_task_store
22122231

22132232

2233+
@pytest.mark.skipif(
2234+
_compat.IS_A2A_V1,
2235+
reason=(
2236+
"0.3.x-only: mocks server.apps.A2AStarletteApplication (gone in 1.x)"
2237+
),
2238+
)
22142239
def test_a2a_task_store_engine_disposed_on_shutdown(
22152240
mock_session_service,
22162241
mock_artifact_service,
@@ -2292,6 +2317,12 @@ def test_a2a_task_store_engine_disposed_on_shutdown(
22922317
mock_engine.dispose.assert_awaited_once()
22932318

22942319

2320+
@pytest.mark.skipif(
2321+
_compat.IS_A2A_V1,
2322+
reason=(
2323+
"0.3.x-only: mocks server.apps.A2AStarletteApplication (gone in 1.x)"
2324+
),
2325+
)
22952326
def test_a2a_in_memory_task_store_no_engine_dispose(
22962327
mock_session_service,
22972328
mock_artifact_service,

0 commit comments

Comments
 (0)