routa_server/api/
agents.rs1use axum::{
2 extract::{Query, State},
3 routing::{get, post},
4 Json, Router,
5};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9use crate::error::ServerError;
10use crate::models::agent::{Agent, AgentRole, AgentStatus, ModelTier};
11use crate::state::AppState;
12
13pub fn router() -> Router<AppState> {
14 Router::new()
15 .route("/", get(list_agents).post(create_agent))
16 .route("/{id}", get(get_agent_by_path).delete(delete_agent))
17 .route("/{id}/status", post(update_agent_status))
18}
19
20#[derive(Debug, Deserialize)]
21#[serde(rename_all = "camelCase")]
22struct ListAgentsQuery {
23 id: Option<String>,
25 workspace_id: Option<String>,
26 role: Option<String>,
27 status: Option<String>,
28 parent_id: Option<String>,
29 #[allow(dead_code)]
30 summary: Option<String>,
31}
32
33async fn list_agents(
34 State(state): State<AppState>,
35 Query(query): Query<ListAgentsQuery>,
36) -> Result<Json<serde_json::Value>, ServerError> {
37 if let Some(id) = &query.id {
39 let agent = state.agent_store.get(id).await?;
40 return Ok(Json(serde_json::json!(agent)));
41 }
42
43 let workspace_id = query.workspace_id.as_deref().unwrap_or("default");
44
45 let agents = if let Some(parent_id) = &query.parent_id {
46 state.agent_store.list_by_parent(parent_id).await?
47 } else if let Some(role_str) = &query.role {
48 let role = AgentRole::from_str(role_str)
49 .ok_or_else(|| ServerError::BadRequest(format!("Invalid role: {role_str}")))?;
50 state.agent_store.list_by_role(workspace_id, &role).await?
51 } else if let Some(status_str) = &query.status {
52 let status = AgentStatus::from_str(status_str)
53 .ok_or_else(|| ServerError::BadRequest(format!("Invalid status: {status_str}")))?;
54 state
55 .agent_store
56 .list_by_status(workspace_id, &status)
57 .await?
58 } else {
59 state.agent_store.list_by_workspace(workspace_id).await?
60 };
61
62 Ok(Json(serde_json::json!({ "agents": agents })))
63}
64
65async fn get_agent_by_path(
67 State(state): State<AppState>,
68 axum::extract::Path(id): axum::extract::Path<String>,
69) -> Result<Json<Agent>, ServerError> {
70 state
71 .agent_store
72 .get(&id)
73 .await?
74 .map(Json)
75 .ok_or_else(|| ServerError::NotFound(format!("Agent {id} not found")))
76}
77
78#[derive(Debug, Deserialize)]
79#[serde(rename_all = "camelCase")]
80struct CreateAgentRequest {
81 name: String,
82 role: String,
83 workspace_id: Option<String>,
84 parent_id: Option<String>,
85 model_tier: Option<String>,
86 metadata: Option<HashMap<String, String>>,
87}
88
89#[derive(Debug, Serialize)]
90#[serde(rename_all = "camelCase")]
91struct CreateAgentResponse {
92 agent_id: String,
93 agent: Agent,
94}
95
96async fn create_agent(
97 State(state): State<AppState>,
98 Json(body): Json<CreateAgentRequest>,
99) -> Result<Json<CreateAgentResponse>, ServerError> {
100 let role = AgentRole::from_str(&body.role)
101 .ok_or_else(|| ServerError::BadRequest(format!("Invalid role: {}", body.role)))?;
102 let model_tier = body.model_tier.as_deref().and_then(ModelTier::from_str);
103 let workspace_id = body.workspace_id.unwrap_or_else(|| "default".to_string());
104
105 state.workspace_store.ensure_default().await?;
106
107 let agent = Agent::new(
108 uuid::Uuid::new_v4().to_string(),
109 body.name,
110 role,
111 workspace_id,
112 body.parent_id,
113 model_tier,
114 body.metadata,
115 );
116
117 state.agent_store.save(&agent).await?;
118
119 Ok(Json(CreateAgentResponse {
120 agent_id: agent.id.clone(),
121 agent,
122 }))
123}
124
125async fn delete_agent(
126 State(state): State<AppState>,
127 axum::extract::Path(id): axum::extract::Path<String>,
128) -> Result<Json<serde_json::Value>, ServerError> {
129 state.agent_store.delete(&id).await?;
130 Ok(Json(serde_json::json!({ "deleted": true })))
131}
132
133#[derive(Debug, Deserialize)]
134struct UpdateStatusRequest {
135 status: String,
136}
137
138async fn update_agent_status(
139 State(state): State<AppState>,
140 axum::extract::Path(id): axum::extract::Path<String>,
141 Json(body): Json<UpdateStatusRequest>,
142) -> Result<Json<serde_json::Value>, ServerError> {
143 let status = AgentStatus::from_str(&body.status)
144 .ok_or_else(|| ServerError::BadRequest(format!("Invalid status: {}", body.status)))?;
145 state.agent_store.update_status(&id, &status).await?;
146 Ok(Json(serde_json::json!({ "updated": true })))
147}