#[cfg(feature = "http-api")]
use std::sync::Arc;
#[cfg(feature = "http-api")]
use async_trait::async_trait;
#[cfg(feature = "http-api")]
use crate::reasoning::circuit_breaker::CircuitBreakerRegistry;
#[cfg(feature = "http-api")]
use crate::reasoning::executor::ActionExecutor;
#[cfg(feature = "http-api")]
use crate::reasoning::inference::ToolDefinition;
#[cfg(feature = "http-api")]
use crate::reasoning::loop_types::{LoopConfig, Observation, ProposedAction};
#[cfg(feature = "http-api")]
use crate::types::AgentId;
#[cfg(feature = "http-api")]
use super::traits::RuntimeApiProvider;
#[cfg(feature = "http-api")]
pub struct CoordinatorExecutor {
provider: Arc<dyn RuntimeApiProvider>,
}
#[cfg(feature = "http-api")]
impl CoordinatorExecutor {
pub fn new(provider: Arc<dyn RuntimeApiProvider>) -> Self {
Self { provider }
}
pub fn tool_definitions() -> Vec<ToolDefinition> {
vec![
ToolDefinition {
name: "list_agents".into(),
description: "List all agents in the fleet with their current status. Includes external agents with their heartbeat state, last result, and recent events.".into(),
parameters: serde_json::json!({
"type": "object",
"properties": {},
"required": []
}),
},
ToolDefinition {
name: "agent_status".into(),
description: "Get the detailed status of a specific agent by ID. For external agents, includes metadata, last result, recent events, and execution mode.".into(),
parameters: serde_json::json!({
"type": "object",
"properties": {
"agent_id": {
"type": "string",
"description": "The UUID of the agent to query."
}
},
"required": ["agent_id"]
}),
},
ToolDefinition {
name: "query_metrics".into(),
description: "Get current system metrics (CPU, memory, uptime, etc.).".into(),
parameters: serde_json::json!({
"type": "object",
"properties": {},
"required": []
}),
},
ToolDefinition {
name: "list_schedules".into(),
description: "List all scheduled jobs with their summaries.".into(),
parameters: serde_json::json!({
"type": "object",
"properties": {},
"required": []
}),
},
ToolDefinition {
name: "scheduler_health".into(),
description: "Get scheduler health and run statistics.".into(),
parameters: serde_json::json!({
"type": "object",
"properties": {},
"required": []
}),
},
ToolDefinition {
name: "system_health".into(),
description: "Get overall system health status.".into(),
parameters: serde_json::json!({
"type": "object",
"properties": {},
"required": []
}),
},
]
}
async fn dispatch_tool(&self, name: &str, arguments: &str) -> Result<String, String> {
match name {
"list_agents" => {
let agent_ids = self
.provider
.list_agents()
.await
.map_err(|e| e.to_string())?;
let mut agents = Vec::new();
for id in &agent_ids {
match self.provider.get_agent_status(*id).await {
Ok(status) => agents.push(serde_json::to_value(status).unwrap()),
Err(e) => {
agents.push(serde_json::json!({
"agent_id": id.0.to_string(),
"error": e.to_string()
}));
}
}
}
serde_json::to_string_pretty(&agents).map_err(|e| e.to_string())
}
"agent_status" => {
let args: serde_json::Value =
serde_json::from_str(arguments).map_err(|e| e.to_string())?;
let agent_id_str = args["agent_id"].as_str().ok_or("missing agent_id")?;
let uuid = uuid::Uuid::parse_str(agent_id_str).map_err(|e| e.to_string())?;
let status = self
.provider
.get_agent_status(AgentId(uuid))
.await
.map_err(|e| e.to_string())?;
serde_json::to_string_pretty(&status).map_err(|e| e.to_string())
}
"query_metrics" => {
let metrics = self
.provider
.get_metrics()
.await
.map_err(|e| e.to_string())?;
serde_json::to_string_pretty(&metrics).map_err(|e| e.to_string())
}
"list_schedules" => {
let schedules = self
.provider
.list_schedules()
.await
.map_err(|e| e.to_string())?;
serde_json::to_string_pretty(&schedules).map_err(|e| e.to_string())
}
"scheduler_health" => {
let health = self
.provider
.get_scheduler_health()
.await
.map_err(|e| e.to_string())?;
serde_json::to_string_pretty(&health).map_err(|e| e.to_string())
}
"system_health" => {
let health = self
.provider
.get_system_health()
.await
.map_err(|e| e.to_string())?;
serde_json::to_string_pretty(&health).map_err(|e| e.to_string())
}
_ => Err(format!("Unknown tool: {}", name)),
}
}
}
#[cfg(feature = "http-api")]
#[async_trait]
impl ActionExecutor for CoordinatorExecutor {
async fn execute_actions(
&self,
actions: &[ProposedAction],
_config: &LoopConfig,
_circuit_breakers: &CircuitBreakerRegistry,
) -> Vec<Observation> {
let mut observations = Vec::new();
for action in actions {
if let ProposedAction::ToolCall {
call_id,
name,
arguments,
} = action
{
match self.dispatch_tool(name, arguments).await {
Ok(result) => {
observations.push(
Observation::tool_result(name.clone(), result)
.with_call_id(call_id.clone()),
);
}
Err(err) => {
observations.push(
Observation::tool_error(name.clone(), err)
.with_call_id(call_id.clone()),
);
}
}
}
}
observations
}
}