mirror of
https://github.com/encounter/adk-python.git
synced 2026-07-09 18:19:28 -07:00
feat(web): add list-apps-detailed endpoint
Merge https://github.com/google/adk-python/pull/3430 **Please ensure you have read the [contribution guide](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) before creating a pull request.** ### Link to Issue or Description of Change **1. Link to an existing issue (if applicable):** - Closes: #3429 **2. Or, if no issue exists, describe the change:** _If applicable, please follow the issue templates to provide as much detail as possible._ **Problem:** The existing `/list-apps` endpoint only returns the name of the folder that each agent is in **Solution:** This adds a new endpoint `/list-apps-detailed` which will load each agent using the existing `AgentLoader.load_agent` method, and then return the folder name, display name (with underscores replaced with spaces for a more readable version), description, and the agent type. This does introduce overhead if you had multiple agents since they all need to be loaded, but by maintaining the existing `/list-apps` endpoint, users can choose which one to hit if they don't want to load all agents. Since the existing `load_agents` method will cache results, there's only a penalty on the first hit. ### Testing Plan Created a unit test for this, similar to the `/list-apps`. Also tested this with my own ADK instance to verify it loaded correctly. ``` curl --location "localhost:8000/list-apps-detailed" ``` ```json { "apps": [ { "name": "agent_1", "displayName": "Agent 1", "description": "A test description for a test agent", "agentType": "package" }, { "name": "agent_2", "displayName": "Agent 2", "description": "A test description for a test agent ", "agentType": "package" }, { "name": "agent_3", "displayName": "Agent 3", "description": "A test description for a test agent", "agentType": "package" } ] } ``` **Unit Tests:** - [X] I have added or updated unit tests for my change. - [X] All unit tests pass locally. 3054 passed, 2383 warnings in 46.96s ### Checklist - [X] I have read the [CONTRIBUTING.md](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) document. - [X] I have performed a self-review of my own code. - [X] I have commented my code, particularly in hard-to-understand areas. - [X] I have added tests that prove my fix is effective or that my feature works. - [X] New and existing unit tests pass locally with my changes. - [X] I have manually tested my changes end-to-end. - [X] Any dependent changes have been merged and published in downstream modules. COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3430 from dylan-apex:more-detailed-list-apps e6864fd61a673da5fd2fb28d2d7d72cb90f5af0a PiperOrigin-RevId: 834907771
This commit is contained in:
@@ -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,
|
||||
@@ -699,7 +710,14 @@ class AdkWebServer:
|
||||
)
|
||||
|
||||
@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])
|
||||
|
||||
@@ -20,6 +20,8 @@ import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from typing import Any
|
||||
from typing import Literal
|
||||
from typing import Optional
|
||||
from typing import Union
|
||||
|
||||
@@ -341,6 +343,50 @@ class AgentLoader(BaseAgentLoader):
|
||||
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 = [
|
||||
|
||||
@@ -18,6 +18,7 @@ from __future__ import annotations
|
||||
|
||||
from abc import ABC
|
||||
from abc import abstractmethod
|
||||
from typing import Any
|
||||
from typing import Union
|
||||
|
||||
from ...agents.base_agent import BaseAgent
|
||||
@@ -34,3 +35,15 @@ class BaseAgentLoader(ABC):
|
||||
@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
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user