Python: A2AAgent defaults name/description from AgentCard (#4661)

* Python: A2AAgent defaults name/description from AgentCard

When an AgentCard is provided but name/description are not explicitly
set, A2AAgent now falls back to agent_card.name and agent_card.description.
This avoids redundant duplication when constructing A2AAgent instances,
especially in GroupChat orchestrations where name and description are
essential for routing decisions.

Explicit values still take precedence over card values.

Fixes #4630

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Use 'is None' checks instead of truthiness for name/description fallback

Ensures explicitly provided empty strings are not overridden by
agent_card values. Adds test for the empty string edge case.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Giles Odigwe
2026-03-12 17:14:23 -07:00
committed by GitHub
Unverified
parent 5e33deff45
commit f696ac9b57
2 changed files with 60 additions and 2 deletions
@@ -114,9 +114,10 @@ class A2AAgent(AgentTelemetryLayer, BaseAgent):
"""Initialize the A2AAgent.
Keyword Args:
name: The name of the agent.
name: The name of the agent. Defaults to agent_card.name if agent_card is provided.
id: The unique identifier for the agent, will be created automatically if not provided.
description: A brief description of the agent's purpose.
description: A brief description of the agent's purpose. Defaults to agent_card.description
if agent_card is provided.
agent_card: The agent card for the agent.
url: The URL for the A2A server.
client: The A2A client for the agent.
@@ -127,6 +128,13 @@ class A2AAgent(AgentTelemetryLayer, BaseAgent):
10.0s write, 5.0s pool - optimized for A2A operations).
kwargs: any additional properties, passed to BaseAgent.
"""
# Default name/description from agent_card when not explicitly provided
if agent_card is not None:
if name is None:
name = agent_card.name
if description is None:
description = agent_card.description
super().__init__(id=id, name=name, description=description, **kwargs)
self._http_client: httpx.AsyncClient | None = http_client
self._timeout_config = self._create_timeout_config(timeout)
@@ -145,6 +145,54 @@ def test_a2a_agent_initialization_with_client(mock_a2a_client: MockA2AClient) ->
assert agent.client == mock_a2a_client
def test_a2a_agent_defaults_name_description_from_agent_card(mock_a2a_client: MockA2AClient) -> None:
"""Test A2AAgent defaults name and description from agent_card when not explicitly provided."""
mock_card = MagicMock(spec=AgentCard)
mock_card.name = "Card Agent Name"
mock_card.description = "Card agent description"
agent = A2AAgent(agent_card=mock_card, client=mock_a2a_client, http_client=None)
assert agent.name == "Card Agent Name"
assert agent.description == "Card agent description"
def test_a2a_agent_explicit_name_description_overrides_agent_card(mock_a2a_client: MockA2AClient) -> None:
"""Test that explicit name/description take precedence over agent_card values."""
mock_card = MagicMock(spec=AgentCard)
mock_card.name = "Card Agent Name"
mock_card.description = "Card agent description"
agent = A2AAgent(
name="Explicit Name",
description="Explicit description",
agent_card=mock_card,
client=mock_a2a_client,
http_client=None,
)
assert agent.name == "Explicit Name"
assert agent.description == "Explicit description"
def test_a2a_agent_empty_string_name_description_not_overridden(mock_a2a_client: MockA2AClient) -> None:
"""Test that explicitly provided empty strings are not overridden by agent_card values."""
mock_card = MagicMock(spec=AgentCard)
mock_card.name = "Card Agent Name"
mock_card.description = "Card agent description"
agent = A2AAgent(
name="",
description="",
agent_card=mock_card,
client=mock_a2a_client,
http_client=None,
)
assert agent.name == ""
assert agent.description == ""
def test_a2a_agent_initialization_without_client_raises_error() -> None:
"""Test A2AAgent initialization without client or URL raises ValueError."""
with raises(ValueError, match="Either agent_card or url must be provided"):
@@ -561,6 +609,8 @@ def test_transport_negotiation_both_fail() -> None:
# Create a mock agent card
mock_agent_card = MagicMock(spec=AgentCard)
mock_agent_card.url = "http://test-agent.example.com"
mock_agent_card.name = "Test Agent"
mock_agent_card.description = "A test agent"
# Mock the factory to simulate both primary and fallback failures
mock_factory = MagicMock()