Skip to content

build_agent_card(): agent_registry cards stay streaming:false after #6673, and the streaming= parameter becomes unreachable #6778

Description

@arunpshankar

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

  1. Thread capabilities through the agent_registry call site the way AgentCardBuilder does, and drop streaming= from build_agent_card once nothing reaches it.
  2. 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.
  3. 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.

Metadata

Metadata

Assignees

Labels

agent config[Component] This issue is related to the Agent Config interface and implementation

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions