From b7584455ce040b7e20e0e54d8c2f7972d9668443 Mon Sep 17 00:00:00 2001 From: Himansh Raj Date: Wed, 28 Oct 2026 07:04:17 +0530 Subject: [PATCH] fix(a2a): stamp A2A-Version on the rebound client with a2a-sdk 1.3 a2a-sdk 1.3.0 no longer stores ClientFactory._httpx_client, so rebind_client_factory_httpx never set A2A-Version on the httpx client a RemoteA2aAgent sends with when a caller passed its own client factory. Stamp the header whenever the SDK is 1.x, independent of that attribute. --- src/google/adk/a2a/_compat.py | 19 ++++++++++--------- .../a2a/agent/test_remote_a2a_agent.py | 2 +- tests/unittests/a2a/test_compat.py | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/google/adk/a2a/_compat.py b/src/google/adk/a2a/_compat.py index 01dded6c146..3dd9f898b6f 100644 --- a/src/google/adk/a2a/_compat.py +++ b/src/google/adk/a2a/_compat.py @@ -923,18 +923,19 @@ def rebind_client_factory_httpx(factory: Any, httpx_client: Any) -> Any: new_factory = copy.copy(factory) new_factory._config = config + # a2a-sdk 1.3 dropped ``ClientFactory._httpx_client``; it now reads the + # client from ``_config`` when it builds a transport, so the version header + # has to be stamped on the rebound client whether that attribute exists. if hasattr(new_factory, "_httpx_client"): new_factory._httpx_client = httpx_client - if hasattr(httpx_client, "headers"): - try: - from a2a.utils.constants import PROTOCOL_VERSION_CURRENT - from a2a.utils.constants import VERSION_HEADER + if IS_A2A_V1 and hasattr(httpx_client, "headers"): + try: + from a2a.utils.constants import PROTOCOL_VERSION_CURRENT + from a2a.utils.constants import VERSION_HEADER - httpx_client.headers.setdefault( - VERSION_HEADER, PROTOCOL_VERSION_CURRENT - ) - except ImportError: - pass + httpx_client.headers.setdefault(VERSION_HEADER, PROTOCOL_VERSION_CURRENT) + except ImportError: + pass if hasattr(new_factory, "_registry") and new_factory._registry is not None: new_factory._registry = copy.copy(factory._registry) if hasattr(new_factory, "_register_defaults"): diff --git a/tests/unittests/a2a/agent/test_remote_a2a_agent.py b/tests/unittests/a2a/agent/test_remote_a2a_agent.py index dde33506a96..0c06dfa0b26 100644 --- a/tests/unittests/a2a/agent/test_remote_a2a_agent.py +++ b/tests/unittests/a2a/agent/test_remote_a2a_agent.py @@ -640,7 +640,7 @@ def create(self, *args, **kwargs): ) if _compat.IS_A2A_V1: assert ( - agent._a2a_client_factory._httpx_client.headers[VERSION_HEADER] + agent._httpx_client.headers[VERSION_HEADER] == PROTOCOL_VERSION_CURRENT ) assert factory._config.httpx_client is None diff --git a/tests/unittests/a2a/test_compat.py b/tests/unittests/a2a/test_compat.py index 9e8268f249d..062f164b939 100644 --- a/tests/unittests/a2a/test_compat.py +++ b/tests/unittests/a2a/test_compat.py @@ -359,6 +359,24 @@ async def test_rebind_client_factory_httpx_defaults_protocol_bindings(): await new_client.aclose() +@pytest.mark.asyncio +async def test_rebind_client_factory_httpx_sets_version_header_on_new_client(): + if not _compat.IS_A2A_V1: + pytest.skip('the A2A-Version header is 1.x only') + from a2a.utils.constants import PROTOCOL_VERSION_CURRENT + from a2a.utils.constants import VERSION_HEADER + + new_client = httpx.AsyncClient() + factory = ClientFactory(ClientConfig(httpx_client=None)) + rebound = _compat.rebind_client_factory_httpx(factory, new_client) + + try: + assert rebound._config.httpx_client is new_client + assert new_client.headers[VERSION_HEADER] == PROTOCOL_VERSION_CURRENT + finally: + await new_client.aclose() + + @pytest.mark.asyncio async def test_rebind_client_factory_httpx_preserves_protocol_bindings(): old_client, new_client = httpx.AsyncClient(), httpx.AsyncClient()