adk_server/rest/controllers/
apps.rs1use crate::ServerConfig;
2use axum::{
3 Json,
4 extract::{Query, State},
5 http::StatusCode,
6};
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Serialize)]
11#[serde(rename_all = "camelCase")]
12pub struct AgentDetails {
13 pub name: String,
15 pub description: String,
17 pub kind: &'static str,
19 pub interaction_mode: adk_core::AgentInteractionMode,
21 pub capabilities: adk_core::AgentCapabilities,
23 pub services: RuntimeServices,
25 pub children: Vec<AgentChild>,
27 #[serde(skip_serializing_if = "Option::is_none")]
29 pub topology: Option<adk_core::AgentTopology>,
30}
31
32#[derive(Debug, Serialize)]
34#[serde(rename_all = "camelCase")]
35pub struct RuntimeServices {
36 pub telemetry: bool,
41 pub telemetry_status: TelemetryStatus,
43 pub artifacts: bool,
45 pub memory: bool,
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
51#[serde(rename_all = "camelCase")]
52pub enum TelemetryStatus {
53 Disabled,
55 Configured,
57 Collecting,
59}
60
61#[derive(Debug, Serialize)]
63#[serde(rename_all = "camelCase")]
64pub struct AgentChild {
65 pub name: String,
67 pub description: String,
69 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
84pub 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#[derive(Debug, Deserialize)]
94pub struct ListAppsQuery {
95 #[serde(default)]
96 pub relative_path: Option<String>,
97}
98
99#[derive(Debug, Serialize)]
101pub struct AppInfo {
102 pub name: String,
103 pub description: String,
104}
105
106pub 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
116pub 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}