Skip to main content

adk_server/rest/controllers/
apps.rs

1use crate::ServerConfig;
2use axum::{
3    Json,
4    extract::{Query, State},
5    http::StatusCode,
6};
7use serde::{Deserialize, Serialize};
8
9/// Runtime metadata used by the built-in agent interface.
10#[derive(Debug, Serialize)]
11#[serde(rename_all = "camelCase")]
12pub struct AgentDetails {
13    /// Stable agent name.
14    pub name: String,
15    /// Human-readable purpose.
16    pub description: String,
17    /// Broad presentation category.
18    pub kind: &'static str,
19    /// Primary request/response or realtime interaction pattern.
20    pub interaction_mode: adk_core::AgentInteractionMode,
21    /// Runtime execution capabilities.
22    pub capabilities: adk_core::AgentCapabilities,
23    /// Services configured on the server executing this agent.
24    pub services: RuntimeServices,
25    /// Immediate child agents for legacy composites and workflows.
26    pub children: Vec<AgentChild>,
27    /// Exact portable topology when the root provides one.
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub topology: Option<adk_core::AgentTopology>,
30}
31
32/// Shared runtime services visible to the built-in interface.
33#[derive(Debug, Serialize)]
34#[serde(rename_all = "camelCase")]
35pub struct RuntimeServices {
36    /// Whether an in-process telemetry exporter is configured.
37    ///
38    /// Kept for wire compatibility; use [`Self::telemetry_status`] to
39    /// distinguish a ready exporter from one proven to be collecting.
40    pub telemetry: bool,
41    /// Current in-process telemetry collector state.
42    pub telemetry_status: TelemetryStatus,
43    /// An artifact service is available to tools and agents.
44    pub artifacts: bool,
45    /// A cross-session memory service is available.
46    pub memory: bool,
47}
48
49/// Observable state of the in-process session telemetry collector.
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
51#[serde(rename_all = "camelCase")]
52pub enum TelemetryStatus {
53    /// No in-process exporter is attached to this server.
54    Disabled,
55    /// An exporter is attached but has not retained a supported runtime span.
56    Configured,
57    /// The exporter has retained at least one supported runtime span.
58    Collecting,
59}
60
61/// One immediate child in a legacy agent hierarchy.
62#[derive(Debug, Serialize)]
63#[serde(rename_all = "camelCase")]
64pub struct AgentChild {
65    /// Stable child name.
66    pub name: String,
67    /// Human-readable child purpose.
68    pub description: String,
69    /// Runtime execution capabilities.
70    pub capabilities: adk_core::AgentCapabilities,
71}
72
73#[derive(Clone)]
74pub struct AppsController {
75    config: ServerConfig,
76}
77
78impl AppsController {
79    pub fn new(config: ServerConfig) -> Self {
80        Self { config }
81    }
82}
83
84/// Response format for /api/apps - simple list of agent names
85pub async fn list_apps(
86    State(controller): State<AppsController>,
87) -> Result<Json<Vec<String>>, StatusCode> {
88    let apps = controller.config.agent_loader.list_agents();
89    Ok(Json(apps))
90}
91
92/// Query params for /api/list-apps (adk-go compatible)
93#[derive(Debug, Deserialize)]
94pub struct ListAppsQuery {
95    #[serde(default)]
96    pub relative_path: Option<String>,
97}
98
99/// App info returned by /api/list-apps (adk-go compatible format)
100#[derive(Debug, Serialize)]
101pub struct AppInfo {
102    pub name: String,
103    pub description: String,
104}
105
106/// Response format for /api/list-apps (adk-go compatible)
107/// Returns just the agent names as strings - the frontend expects this format
108pub async fn list_apps_compat(
109    State(controller): State<AppsController>,
110    Query(_query): Query<ListAppsQuery>,
111) -> Result<Json<Vec<String>>, StatusCode> {
112    let apps = controller.config.agent_loader.list_agents();
113    Ok(Json(apps))
114}
115
116/// Return runtime metadata for one executable agent root.
117pub async fn get_agent_details(
118    State(controller): State<AppsController>,
119    axum::extract::Path(name): axum::extract::Path<String>,
120) -> Result<Json<AgentDetails>, StatusCode> {
121    let agent = controller
122        .config
123        .agent_loader
124        .load_agent(&name)
125        .await
126        .map_err(|_| StatusCode::NOT_FOUND)?;
127    let topology = agent.topology();
128    let interaction_mode = agent.interaction_mode();
129    let children = agent
130        .sub_agents()
131        .iter()
132        .map(|child| AgentChild {
133            name: child.name().to_string(),
134            description: child.description().to_string(),
135            capabilities: child.capabilities(),
136        })
137        .collect::<Vec<_>>();
138    let kind = if interaction_mode == adk_core::AgentInteractionMode::Realtime {
139        "realtime"
140    } else if topology.as_ref().is_some_and(|topology| {
141        topology
142            .relationships
143            .iter()
144            .any(|relationship| relationship.kind == adk_core::AgentRelationshipKind::Flow)
145    }) {
146        "workflow"
147    } else if topology.is_some() {
148        "team"
149    } else if children.is_empty() {
150        "agent"
151    } else {
152        "composite"
153    };
154
155    let telemetry_status = match controller.config.span_exporter.as_ref() {
156        Some(exporter) if exporter.is_collecting() => TelemetryStatus::Collecting,
157        Some(_) => TelemetryStatus::Configured,
158        None => TelemetryStatus::Disabled,
159    };
160
161    Ok(Json(AgentDetails {
162        name: agent.name().to_string(),
163        description: agent.description().to_string(),
164        kind,
165        interaction_mode,
166        capabilities: agent.capabilities(),
167        services: RuntimeServices {
168            telemetry: controller.config.span_exporter.is_some(),
169            telemetry_status,
170            artifacts: controller.config.artifact_service.is_some(),
171            memory: controller.config.memory_service.is_some(),
172        },
173        children,
174        topology,
175    }))
176}