Splitting this out of #6672 as suggested there, so it does not block #6673.
Two things, one root cause. In _compat.build_agent_card():
default_capabilities = {"streaming": streaming, "push_notifications": False}
...
"capabilities": _as_dict(capabilities) or default_capabilities,
The streaming= parameter only reaches the card through default_capabilities, and that is consulted only when capabilities is falsy. The two call sites take different branches, and #6673 changes which branch each one takes.
1. Cards built through agent_registry stay streaming: false
src/google/adk/integrations/agent_registry/agent_registry.py:640 passes neither capabilities nor streaming:
agent_card = _compat.build_agent_card(
name=name,
description=description,
version=version,
url=url,
protocol_binding=getattr(binding, "value", binding),
protocol_version=protocol_version,
skills=skills,
default_input_modes=["text"],
default_output_modes=["text"],
)
So it falls to default_capabilities, where streaming defaults to False. #6673 changes AgentCardBuilder's default, which this call site does not go through, so a card built here advertises streaming: false after that fix as well as before it.
2. streaming= becomes unreachable
src/google/adk/a2a/utils/agent_card_builder.py:85 passes capabilities=self._capabilities (line 94) and never passes streaming=. #6673 changes that field to:
self._capabilities = capabilities or AgentCapabilities(streaming=True)
which is always truthy, so _as_dict(capabilities) wins and default_capabilities is never evaluated from this caller. Together with (1), no call site in the repository can reach the streaming= parameter, and a caller who sets it gets no effect and no warning.
Reproduced
google-adk 2.6.1, a2a-sdk 1.1.2, Python 3.13.5, clean venv. build_agent_card is byte-identical between 2.6.1 and current main, so this applies to main.
from google.adk.a2a import _compat
from a2a.types import AgentCapabilities
binding = getattr(_compat.TP_HTTP_JSON, "value", _compat.TP_HTTP_JSON)
common = dict(name="x", description="d", version="1", url="http://h/a",
skills=[], default_input_modes=["text"],
default_output_modes=["text"])
# the agent_registry call shape
card = _compat.build_agent_card(protocol_binding=binding, **common)
print(card.capabilities.streaming) # False
# streaming= is honoured, but only when capabilities is absent
card = _compat.build_agent_card(protocol_binding=binding, streaming=True, **common)
print(card.capabilities.streaming) # True
# once capabilities is passed, which is what #6673 makes the default, it is ignored
card = _compat.build_agent_card(protocol_binding=binding,
capabilities=AgentCapabilities(streaming=True),
streaming=False, **common)
print(card.capabilities.streaming) # True, streaming= had no effect
Possible shapes
- Thread
capabilities through the agent_registry call site the way AgentCardBuilder does, and drop streaming= from build_agent_card once nothing reaches it.
- Keep
streaming= and make it compose rather than fall back, so it applies on top of a passed capabilities. This changes the meaning of an existing parameter for anyone already passing both.
- Keep
streaming= and document it as the no-capabilities convenience path only.
I have not opened a pull request because (2) changes the behaviour of an existing parameter and (1) removes public surface, so the choice looks like yours rather than mine. Happy to send whichever you prefer.
Splitting this out of #6672 as suggested there, so it does not block #6673.
Two things, one root cause. In
_compat.build_agent_card():The
streaming=parameter only reaches the card throughdefault_capabilities, and that is consulted only whencapabilitiesis falsy. The two call sites take different branches, and #6673 changes which branch each one takes.1. Cards built through
agent_registrystaystreaming: falsesrc/google/adk/integrations/agent_registry/agent_registry.py:640passes neithercapabilitiesnorstreaming:So it falls to
default_capabilities, wherestreamingdefaults toFalse. #6673 changesAgentCardBuilder's default, which this call site does not go through, so a card built here advertisesstreaming: falseafter that fix as well as before it.2.
streaming=becomes unreachablesrc/google/adk/a2a/utils/agent_card_builder.py:85passescapabilities=self._capabilities(line 94) and never passesstreaming=. #6673 changes that field to:which is always truthy, so
_as_dict(capabilities)wins anddefault_capabilitiesis never evaluated from this caller. Together with (1), no call site in the repository can reach thestreaming=parameter, and a caller who sets it gets no effect and no warning.Reproduced
google-adk 2.6.1,a2a-sdk 1.1.2, Python 3.13.5, clean venv.build_agent_cardis byte-identical between 2.6.1 and current main, so this applies to main.Possible shapes
capabilitiesthrough theagent_registrycall site the wayAgentCardBuilderdoes, and dropstreaming=frombuild_agent_cardonce nothing reaches it.streaming=and make it compose rather than fall back, so it applies on top of a passedcapabilities. This changes the meaning of an existing parameter for anyone already passing both.streaming=and document it as the no-capabilities convenience path only.I have not opened a pull request because (2) changes the behaviour of an existing parameter and (1) removes public surface, so the choice looks like yours rather than mine. Happy to send whichever you prefer.