Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion src/google/adk/cli/adk_web_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,17 @@ class ListMetricsInfoResponse(common.BaseModel):
metrics_info: list[MetricInfo]


class AppInfo(common.BaseModel):
name: str
root_agent_name: str
description: str
language: Literal["yaml", "python"]


class ListAppsResponse(common.BaseModel):
apps: list[AppInfo]


def _setup_telemetry(
otel_to_cloud: bool = False,
internal_exporters: Optional[list[SpanProcessor]] = None,
Expand Down Expand Up @@ -699,7 +710,14 @@ async def internal_lifespan(app: FastAPI):
)

@app.get("/list-apps")
async def list_apps() -> list[str]:
async def list_apps(
detailed: bool = Query(
default=False, description="Return detailed app information"
)
) -> list[str] | ListAppsResponse:
if detailed:
apps_info = self.agent_loader.list_agents_detailed()
return ListAppsResponse(apps=[AppInfo(**app) for app in apps_info])
return self.agent_loader.list_agents()

@app.get("/debug/trace/{event_id}", tags=[TAG_DEBUG])
Expand Down
46 changes: 46 additions & 0 deletions src/google/adk/cli/utils/agent_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import os
from pathlib import Path
import sys
from typing import Any
from typing import Literal
from typing import Optional
from typing import Union

Expand Down Expand Up @@ -321,6 +323,50 @@ def list_agents(self) -> list[str]:
agent_names.sort()
return agent_names

def list_agents_detailed(self) -> list[dict[str, Any]]:
"""Lists all agents with detailed metadata (name, description, type)."""
agent_names = self.list_agents()
apps_info = []

for agent_name in agent_names:
try:
loaded = self.load_agent(agent_name)
if isinstance(loaded, App):
agent = loaded.root_agent
else:
agent = loaded

language = self._determine_agent_language(agent_name)

app_info = {
"name": agent_name,
"root_agent_name": agent.name,
"description": agent.description,
"language": language,
}
apps_info.append(app_info)

except Exception as e:
logger.error("Failed to load agent '%s': %s", agent_name, e)
continue

return apps_info

def _determine_agent_language(
self, agent_name: str
) -> Literal["yaml", "python"]:
"""Determine the type of agent based on file structure."""
base_path = Path.cwd() / self.agents_dir / agent_name

if (base_path / "root_agent.yaml").exists():
return "yaml"
elif (base_path / "agent.py").exists():
return "python"
elif (base_path / "__init__.py").exists():
return "python"

raise ValueError(f"Could not determine agent type for '{agent_name}'.")

def remove_agent_from_cache(self, agent_name: str):
# Clear module cache for the agent and its submodules
keys_to_delete = [
Expand Down
13 changes: 13 additions & 0 deletions src/google/adk/cli/utils/base_agent_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from abc import ABC
from abc import abstractmethod
from typing import Any
from typing import Union

from ...agents.base_agent import BaseAgent
Expand All @@ -34,3 +35,15 @@ def load_agent(self, agent_name: str) -> Union[BaseAgent, App]:
@abstractmethod
def list_agents(self) -> list[str]:
"""Lists all agents available in the agent loader in alphabetical order."""

def list_agents_detailed(self) -> list[dict[str, Any]]:
agent_names = self.list_agents()
return [
{
'name': name,
'display_name': None,
'description': None,
'type': None,
}
for name in agent_names
]
28 changes: 28 additions & 0 deletions tests/unittests/cli/test_fast_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,14 @@ def load_agent(self, app_name):
def list_agents(self):
return ["test_app"]

def list_agents_detailed(self):
return [{
"name": "test_app",
"root_agent_name": "test_agent",
"description": "A test agent for unit testing",
"language": "python",
}]

return MockAgentLoader(".")


Expand Down Expand Up @@ -548,6 +556,26 @@ def test_list_apps(test_app):
logger.info(f"Listed apps: {data}")


def test_list_apps_detailed(test_app):
"""Test listing available applications with detailed metadata."""
response = test_app.get("/list-apps?detailed=true")

assert response.status_code == 200
data = response.json()
assert isinstance(data, dict)
assert "apps" in data
assert isinstance(data["apps"], list)

for app in data["apps"]:
assert "name" in app
assert "rootAgentName" in app
assert "description" in app
assert "language" in app
assert app["language"] in ["yaml", "python"]

logger.info(f"Listed apps: {data}")


def test_create_session_with_id(test_app, test_session_info):
"""Test creating a session with a specific ID."""
new_session_id = "new_session_id"
Expand Down