From 7a553b79541739c2911274b486827ae758163e57 Mon Sep 17 00:00:00 2001 From: RudrenduPaul Date: Wed, 1 Jul 2026 17:50:46 -0700 Subject: [PATCH] fix(cli): support flat-module agents in _determine_agent_language Merge https://github.com/google/adk-python/pull/5235 `_determine_agent_language()` handled only 3 of the 4 agent structures that `load_agent()` supports. When an agent is a flat module (`agents_dir/my_agent.py`, with no subdirectory), `load_agent()` loads it successfully but `_determine_agent_language()` raised `ValueError`. This adds the missing flat-module branch so language detection is consistent with all four supported load patterns: - `{name}/agent.py` - `{name}/__init__.py` - `{name}/root_agent.yaml` - `{name}.py` (flat module) It also adds `__init__.py` to the recognized agent marker files listed in the `adk web` / `adk api_server` AGENTS_DIR docstrings, and adds a `TestDetermineAgentLanguage` suite covering all four patterns plus the unrecognized-structure error case. Co-authored-by: Haran Rajkumar PiperOrigin-RevId: 941382778 --- src/google/adk/cli/cli_tools_click.py | 8 +-- src/google/adk/cli/utils/agent_loader.py | 2 + .../unittests/cli/utils/test_agent_loader.py | 53 +++++++++++++++++++ 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/google/adk/cli/cli_tools_click.py b/src/google/adk/cli/cli_tools_click.py index f884bce61a..29484a3bca 100644 --- a/src/google/adk/cli/cli_tools_click.py +++ b/src/google/adk/cli/cli_tools_click.py @@ -1806,8 +1806,8 @@ def cli_web( """Starts a FastAPI server with Web UI for agents. AGENTS_DIR: The directory of agents (where each subdirectory is a single - agent containing `agent.py` or `root_agent.yaml` files) or a path pointing - directly to a single agent folder. + agent containing `agent.py`, `__init__.py`, or `root_agent.yaml`) or a path + pointing directly to a single agent folder. Example: @@ -1947,8 +1947,8 @@ def cli_api_server( """Starts a FastAPI server for agents. AGENTS_DIR: The directory of agents (where each subdirectory is a single - agent containing `agent.py` or `root_agent.yaml` files) or a path pointing - directly to a single agent folder. + agent containing `agent.py`, `__init__.py`, or `root_agent.yaml`) or a path + pointing directly to a single agent folder. Example: diff --git a/src/google/adk/cli/utils/agent_loader.py b/src/google/adk/cli/utils/agent_loader.py index dd5e0ed81a..1a865fd104 100644 --- a/src/google/adk/cli/utils/agent_loader.py +++ b/src/google/adk/cli/utils/agent_loader.py @@ -471,6 +471,8 @@ def _determine_agent_language( return "python" elif (base_path / "__init__.py").exists(): return "python" + elif (base_path.parent / f"{agent_name}.py").exists(): + return "python" raise ValueError(f"Could not determine agent type for '{agent_name}'.") diff --git a/tests/unittests/cli/utils/test_agent_loader.py b/tests/unittests/cli/utils/test_agent_loader.py index 6e682ba7d8..52935a9a8d 100644 --- a/tests/unittests/cli/utils/test_agent_loader.py +++ b/tests/unittests/cli/utils/test_agent_loader.py @@ -1039,3 +1039,56 @@ def test_validate_agent_name_rejects_nonexistent_agent(self): # 'subprocess' is a valid identifier but shouldn't be importable as an agent with pytest.raises(ValueError, match="Agent not found"): loader.load_agent("subprocess") + + +class TestDetermineAgentLanguage: + """Tests for AgentLoader._determine_agent_language covering all 4 load patterns.""" + + def test_flat_module_returns_python(self): + """Flat-module agent (agents_dir/agent_name.py) is detected as python.""" + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + (temp_path / "my_agent.py").write_text("root_agent = None\n") + loader = AgentLoader(temp_dir) + assert loader._determine_agent_language("my_agent") == "python" + + def test_agent_py_subdirectory_returns_python(self): + """Subdirectory with agent.py is detected as python.""" + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + agent_dir = temp_path / "my_agent" + agent_dir.mkdir() + (agent_dir / "agent.py").write_text("root_agent = None\n") + loader = AgentLoader(temp_dir) + assert loader._determine_agent_language("my_agent") == "python" + + def test_init_py_subdirectory_returns_python(self): + """Subdirectory with __init__.py is detected as python.""" + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + agent_dir = temp_path / "my_agent" + agent_dir.mkdir() + (agent_dir / "__init__.py").write_text("root_agent = None\n") + loader = AgentLoader(temp_dir) + assert loader._determine_agent_language("my_agent") == "python" + + def test_root_agent_yaml_returns_yaml(self): + """Subdirectory with root_agent.yaml is detected as yaml.""" + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + agent_dir = temp_path / "my_agent" + agent_dir.mkdir() + (agent_dir / "root_agent.yaml").write_text("root_agent: {}\n") + loader = AgentLoader(temp_dir) + assert loader._determine_agent_language("my_agent") == "yaml" + + def test_unrecognized_structure_raises_value_error(self): + """A directory with no recognized structure raises ValueError.""" + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + agent_dir = temp_path / "my_agent" + agent_dir.mkdir() + (agent_dir / "main.py").write_text("root_agent = None\n") + loader = AgentLoader(temp_dir) + with pytest.raises(ValueError, match="Could not determine agent type"): + loader._determine_agent_language("my_agent")