Skip to content

Commit 969db70

Browse files
committed
fix(a2a): advertise streaming on agent_registry agent cards
agent_registry called build_agent_card without capabilities or streaming, so constructed cards advertised streaming:false. Pass streaming=True on that convenience path and document that streaming= is ignored when capabilities is provided. Fixes #6778
1 parent c9323d5 commit 969db70

4 files changed

Lines changed: 73 additions & 0 deletions

File tree

src/google/adk/a2a/_compat.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,10 @@ def build_agent_card(
485485
transport is ``preferredTransport``.
486486
1.x: ``AgentCard`` is a proto message — RPC URL lives in
487487
``supported_interfaces[i].url`` (with ``protocol_binding``).
488+
489+
``streaming`` is the no-capabilities convenience path: it is applied only
490+
when ``capabilities`` is omitted. A passed ``capabilities`` object is used
491+
as-is, so ``streaming`` has no effect on that call.
488492
"""
489493

490494
def _as_dict(obj: Any) -> Any:

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,8 @@ def get_remote_a2a_agent(
637637
)
638638

639639
binding = protocol_binding or _compat.TP_HTTP_JSON
640+
# Registry metadata has no capabilities object. Pass streaming=True so
641+
# constructed cards do not advertise streaming:false.
640642
agent_card = _compat.build_agent_card(
641643
name=name,
642644
description=description,
@@ -647,6 +649,7 @@ def get_remote_a2a_agent(
647649
skills=skills,
648650
default_input_modes=["text"],
649651
default_output_modes=["text"],
652+
streaming=True,
650653
)
651654

652655
return RemoteA2aAgent(

tests/unittests/a2a/test_compat.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,43 @@ def _build_card(**overrides):
9494
return _compat.build_agent_card(**kwargs)
9595

9696

97+
def _registry_shaped_card(**overrides):
98+
"""Call ``build_agent_card`` without a capabilities object."""
99+
kwargs = dict(
100+
name='x',
101+
description='d',
102+
version='1',
103+
url='http://h/a',
104+
protocol_binding=getattr(
105+
_compat.TP_HTTP_JSON, 'value', _compat.TP_HTTP_JSON
106+
),
107+
skills=[],
108+
default_input_modes=['text'],
109+
default_output_modes=['text'],
110+
)
111+
kwargs.update(overrides)
112+
return _compat.build_agent_card(**kwargs)
113+
114+
115+
def test_build_agent_card_registry_shape_defaults_streaming_false():
116+
"""Omitting capabilities still defaults streaming to false on the helper."""
117+
assert _registry_shaped_card().capabilities.streaming is False
118+
119+
120+
def test_build_agent_card_streaming_true_without_capabilities():
121+
"""streaming= is honoured when capabilities is omitted."""
122+
assert _registry_shaped_card(streaming=True).capabilities.streaming is True
123+
124+
125+
def test_build_agent_card_capabilities_object_ignores_streaming_flag():
126+
"""A passed capabilities object is used as-is; streaming= does not compose."""
127+
card = _registry_shaped_card(
128+
streaming=False,
129+
capabilities=AgentCapabilities(streaming=True),
130+
)
131+
assert card.capabilities.streaming is True
132+
133+
97134
@v03_only
98135
def test_build_agent_card_strips_trailing_slash_from_url():
99136
# The RPC URL is concatenated with paths by callers, so the card must not
@@ -113,6 +150,12 @@ def test_build_agent_card_with_protocol_version_keeps_caller_value():
113150
assert _build_card(protocol_version='0.2.9').protocol_version == '0.2.9'
114151

115152

153+
@v03_only
154+
def test_build_agent_card_omitted_streaming_defaults_to_false():
155+
"""Callers that omit capabilities must pass streaming=True to advertise it."""
156+
assert _build_card().capabilities.streaming is False
157+
158+
116159
@pytest.mark.parametrize('streaming', [True, False])
117160
@v03_only
118161
def test_build_agent_card_default_capabilities_follow_streaming_flag(streaming):

tests/unittests/integrations/agent_registry/test_agent_registry.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,29 @@ def test_get_remote_a2a_agent_defaults(self, registry):
583583
agent._agent_card, "1.0" if _compat.IS_A2A_V1 else "0.3.0"
584584
)
585585

586+
def test_get_remote_a2a_agent_advertises_streaming(self, registry):
587+
"""Constructed registry cards advertise streaming when no card is supplied."""
588+
mock_response = MagicMock()
589+
mock_response.json.return_value = {
590+
"displayName": "TestAgent",
591+
"description": "Test Desc",
592+
"version": "1.0",
593+
"protocols": [{
594+
"type": _ProtocolType.A2A_AGENT,
595+
"interfaces": [{
596+
"url": "https://my-agent.com",
597+
}],
598+
}],
599+
}
600+
mock_response.raise_for_status = MagicMock()
601+
registry._session.get.return_value = mock_response
602+
603+
registry._credentials.token = "token"
604+
registry._credentials.refresh = MagicMock()
605+
606+
agent = registry.get_remote_a2a_agent("test-agent")
607+
assert agent._agent_card.capabilities.streaming is True
608+
586609
def test_get_remote_a2a_agent_with_card(self, registry):
587610
mock_response = MagicMock()
588611
mock_response.json.return_value = {

0 commit comments

Comments
 (0)