From 5bd3ed1faf758dbabd9591285bcdce199ef86943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Ra=C4=8Dinsk=C3=BD?= Date: Wed, 20 Aug 2025 09:49:11 +0200 Subject: [PATCH 1/3] fix: correct agent card --- src/google/adk/cli/fast_api.py | 14 +++++++---- .../unittests/agents/test_remote_a2a_agent.py | 24 +++++++++---------- tests/unittests/cli/test_fast_api.py | 4 ++-- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/google/adk/cli/fast_api.py b/src/google/adk/cli/fast_api.py index 7d93b54360..5a93caa5f6 100644 --- a/src/google/adk/cli/fast_api.py +++ b/src/google/adk/cli/fast_api.py @@ -339,12 +339,12 @@ async def _get_a2a_runner_async() -> Runner: return _get_a2a_runner_async for p in base_path.iterdir(): - # only folders with an agent.json file representing agent card are valid + # only folders with an agent-card.json or agent.json file representing agent card are valid # a2a agents if ( p.is_file() or p.name.startswith((".", "__pycache__")) - or not (p / "agent.json").is_file() + or not ((p / "agent-card.json").is_file() or (p / "agent.json").is_file()) ): continue @@ -359,8 +359,14 @@ async def _get_a2a_runner_async() -> Runner: request_handler = DefaultRequestHandler( agent_executor=agent_executor, task_store=a2a_task_store ) - - with (p / "agent.json").open("r", encoding="utf-8") as f: + agent_card_path = p / "agent-card.json" + deprecated_agent_card_path = p / "agent.json" + if agent_card_path.is_file(): + json_file = agent_card_path + else: + json_file = deprecated_agent_card_path + + with json_file.open("r", encoding="utf-8") as f: data = json.load(f) agent_card = AgentCard(**data) diff --git a/tests/unittests/agents/test_remote_a2a_agent.py b/tests/unittests/agents/test_remote_a2a_agent.py index 7ef32de66d..741b133228 100644 --- a/tests/unittests/agents/test_remote_a2a_agent.py +++ b/tests/unittests/agents/test_remote_a2a_agent.py @@ -112,27 +112,27 @@ def test_init_with_agent_card_object(self): def test_init_with_url_string(self): """Test initialization with URL string.""" agent = RemoteA2aAgent( - name="test_agent", agent_card="https://example.com/agent.json" + name="test_agent", agent_card="https://example.com/agent-card.json" ) assert agent.name == "test_agent" assert agent._agent_card is None - assert agent._agent_card_source == "https://example.com/agent.json" + assert agent._agent_card_source == "https://example.com/agent-card.json" def test_init_with_file_path(self): """Test initialization with file path.""" - agent = RemoteA2aAgent(name="test_agent", agent_card="/path/to/agent.json") + agent = RemoteA2aAgent(name="test_agent", agent_card="/path/to/agent-card.json") assert agent.name == "test_agent" assert agent._agent_card is None - assert agent._agent_card_source == "/path/to/agent.json" + assert agent._agent_card_source == "/path/to/agent-card.json" def test_init_with_shared_httpx_client(self): """Test initialization with shared httpx client.""" httpx_client = httpx.AsyncClient() agent = RemoteA2aAgent( name="test_agent", - agent_card="https://example.com/agent.json", + agent_card="https://example.com/agent-card.json", httpx_client=httpx_client, ) @@ -158,7 +158,7 @@ def test_init_with_custom_timeout(self): """Test initialization with custom timeout.""" agent = RemoteA2aAgent( name="test_agent", - agent_card="https://example.com/agent.json", + agent_card="https://example.com/agent-card.json", timeout=300.0, ) @@ -219,7 +219,7 @@ async def test_ensure_httpx_client_reuses_existing_client(self): async def test_resolve_agent_card_from_url_success(self): """Test successful agent card resolution from URL.""" agent = RemoteA2aAgent( - name="test_agent", agent_card="https://example.com/agent.json" + name="test_agent", agent_card="https://example.com/agent-card.json" ) with patch.object(agent, "_ensure_httpx_client") as mock_ensure_client: @@ -234,7 +234,7 @@ async def test_resolve_agent_card_from_url_success(self): mock_resolver_class.return_value = mock_resolver result = await agent._resolve_agent_card_from_url( - "https://example.com/agent.json" + "https://example.com/agent-card.json" ) assert result == self.agent_card @@ -242,7 +242,7 @@ async def test_resolve_agent_card_from_url_success(self): httpx_client=mock_client, base_url="https://example.com" ) mock_resolver.get_agent_card.assert_called_once_with( - relative_card_path="/agent.json" + relative_card_path="/agent-card.json" ) @pytest.mark.asyncio @@ -256,7 +256,7 @@ async def test_resolve_agent_card_from_url_invalid_url(self): @pytest.mark.asyncio async def test_resolve_agent_card_from_file_success(self): """Test successful agent card resolution from file.""" - agent = RemoteA2aAgent(name="test_agent", agent_card="/path/to/agent.json") + agent = RemoteA2aAgent(name="test_agent", agent_card="/path/to/agent-card.json") with tempfile.NamedTemporaryFile( mode="w", suffix=".json", delete=False @@ -286,7 +286,7 @@ async def test_resolve_agent_card_from_file_not_found(self): @pytest.mark.asyncio async def test_resolve_agent_card_from_file_invalid_json(self): """Test agent card resolution from file with invalid JSON raises error.""" - agent = RemoteA2aAgent(name="test_agent", agent_card="/path/to/agent.json") + agent = RemoteA2aAgent(name="test_agent", agent_card="/path/to/agent-card.json") with tempfile.NamedTemporaryFile( mode="w", suffix=".json", delete=False @@ -393,7 +393,7 @@ async def test_ensure_resolved_with_direct_agent_card(self): async def test_ensure_resolved_with_url_source(self): """Test _ensure_resolved with URL source.""" agent = RemoteA2aAgent( - name="test_agent", agent_card="https://example.com/agent.json" + name="test_agent", agent_card="https://example.com/agent-card.json" ) agent_card = create_test_agent_card() diff --git a/tests/unittests/cli/test_fast_api.py b/tests/unittests/cli/test_fast_api.py index f1c9e9d6ef..e1ccc16b8c 100755 --- a/tests/unittests/cli/test_fast_api.py +++ b/tests/unittests/cli/test_fast_api.py @@ -500,7 +500,7 @@ def temp_agents_dir_with_a2a(): agent_dir = Path(temp_dir) / "test_a2a_agent" agent_dir.mkdir() - # Create agent.json file + # Create agent-card.json file agent_card = { "name": "test_a2a_agent", "description": "Test A2A agent", @@ -509,7 +509,7 @@ def temp_agents_dir_with_a2a(): "capabilities": ["text"], } - with open(agent_dir / "agent.json", "w") as f: + with open(agent_dir / "agent-card.json", "w") as f: json.dump(agent_card, f) # Create a simple agent.py file From b051efbd6625fddd43479c9c7929b7a212777ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Ra=C4=8Dinsk=C3=BD?= Date: Wed, 20 Aug 2025 09:52:45 +0200 Subject: [PATCH 2/3] fix: correct agent card name --- .../remote_a2a/bigquery_agent/{agent.json => agent-card.json} | 0 .../remote_a2a/check_prime_agent/{agent.json => agent-card.json} | 0 .../remote_a2a/human_in_loop/{agent.json => agent-card.json} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename contributing/samples/a2a_auth/remote_a2a/bigquery_agent/{agent.json => agent-card.json} (100%) rename contributing/samples/a2a_basic/remote_a2a/check_prime_agent/{agent.json => agent-card.json} (100%) rename contributing/samples/a2a_human_in_loop/remote_a2a/human_in_loop/{agent.json => agent-card.json} (100%) diff --git a/contributing/samples/a2a_auth/remote_a2a/bigquery_agent/agent.json b/contributing/samples/a2a_auth/remote_a2a/bigquery_agent/agent-card.json similarity index 100% rename from contributing/samples/a2a_auth/remote_a2a/bigquery_agent/agent.json rename to contributing/samples/a2a_auth/remote_a2a/bigquery_agent/agent-card.json diff --git a/contributing/samples/a2a_basic/remote_a2a/check_prime_agent/agent.json b/contributing/samples/a2a_basic/remote_a2a/check_prime_agent/agent-card.json similarity index 100% rename from contributing/samples/a2a_basic/remote_a2a/check_prime_agent/agent.json rename to contributing/samples/a2a_basic/remote_a2a/check_prime_agent/agent-card.json diff --git a/contributing/samples/a2a_human_in_loop/remote_a2a/human_in_loop/agent.json b/contributing/samples/a2a_human_in_loop/remote_a2a/human_in_loop/agent-card.json similarity index 100% rename from contributing/samples/a2a_human_in_loop/remote_a2a/human_in_loop/agent.json rename to contributing/samples/a2a_human_in_loop/remote_a2a/human_in_loop/agent-card.json From 53263d59e36a96380eaddf72baa965ec1466887f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Ra=C4=8Dinsk=C3=BD?= Date: Wed, 20 Aug 2025 10:42:56 +0200 Subject: [PATCH 3/3] fix: deduplicate os access --- src/google/adk/cli/fast_api.py | 19 +++++++------------ .../unittests/agents/test_remote_a2a_agent.py | 12 +++++++++--- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/google/adk/cli/fast_api.py b/src/google/adk/cli/fast_api.py index 5a93caa5f6..5c451d5cea 100644 --- a/src/google/adk/cli/fast_api.py +++ b/src/google/adk/cli/fast_api.py @@ -341,13 +341,15 @@ async def _get_a2a_runner_async() -> Runner: for p in base_path.iterdir(): # only folders with an agent-card.json or agent.json file representing agent card are valid # a2a agents - if ( - p.is_file() - or p.name.startswith((".", "__pycache__")) - or not ((p / "agent-card.json").is_file() or (p / "agent.json").is_file()) - ): + if p.is_file() or p.name.startswith(('.', '__pycache__')): continue + json_file = p / 'agent-card.json' + if not json_file.is_file(): + json_file = p / 'agent.json' + if not json_file.is_file(): + continue + app_name = p.name logger.info("Setting up A2A agent: %s", app_name) @@ -359,13 +361,6 @@ async def _get_a2a_runner_async() -> Runner: request_handler = DefaultRequestHandler( agent_executor=agent_executor, task_store=a2a_task_store ) - agent_card_path = p / "agent-card.json" - deprecated_agent_card_path = p / "agent.json" - if agent_card_path.is_file(): - json_file = agent_card_path - else: - json_file = deprecated_agent_card_path - with json_file.open("r", encoding="utf-8") as f: data = json.load(f) agent_card = AgentCard(**data) diff --git a/tests/unittests/agents/test_remote_a2a_agent.py b/tests/unittests/agents/test_remote_a2a_agent.py index 741b133228..07b02a2398 100644 --- a/tests/unittests/agents/test_remote_a2a_agent.py +++ b/tests/unittests/agents/test_remote_a2a_agent.py @@ -121,7 +121,9 @@ def test_init_with_url_string(self): def test_init_with_file_path(self): """Test initialization with file path.""" - agent = RemoteA2aAgent(name="test_agent", agent_card="/path/to/agent-card.json") + agent = RemoteA2aAgent( + name="test_agent", agent_card="/path/to/agent-card.json" + ) assert agent.name == "test_agent" assert agent._agent_card is None @@ -256,7 +258,9 @@ async def test_resolve_agent_card_from_url_invalid_url(self): @pytest.mark.asyncio async def test_resolve_agent_card_from_file_success(self): """Test successful agent card resolution from file.""" - agent = RemoteA2aAgent(name="test_agent", agent_card="/path/to/agent-card.json") + agent = RemoteA2aAgent( + name="test_agent", agent_card="/path/to/agent-card.json" + ) with tempfile.NamedTemporaryFile( mode="w", suffix=".json", delete=False @@ -286,7 +290,9 @@ async def test_resolve_agent_card_from_file_not_found(self): @pytest.mark.asyncio async def test_resolve_agent_card_from_file_invalid_json(self): """Test agent card resolution from file with invalid JSON raises error.""" - agent = RemoteA2aAgent(name="test_agent", agent_card="/path/to/agent-card.json") + agent = RemoteA2aAgent( + name="test_agent", agent_card="/path/to/agent-card.json" + ) with tempfile.NamedTemporaryFile( mode="w", suffix=".json", delete=False