use std::collections::HashMap;
use std::sync::{Arc, OnceLock};
use autumn_web::actuator::{HealthCheckOutput, HealthIndicator, HealthStatus, IndicatorGroup};
use crate::agent::AgentRuntime;
pub struct AgentHealthIndicator {
active: Arc<OnceLock<Arc<AgentRuntime>>>,
}
impl std::fmt::Debug for AgentHealthIndicator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AgentHealthIndicator")
.field("initialized", &self.active.get().is_some())
.finish_non_exhaustive()
}
}
impl AgentHealthIndicator {
#[must_use]
pub const fn new(active: Arc<OnceLock<Arc<AgentRuntime>>>) -> Self {
Self { active }
}
}
impl HealthIndicator for AgentHealthIndicator {
fn check(&self) -> futures::future::BoxFuture<'_, HealthCheckOutput> {
Box::pin(run_check(self.active.get()))
}
fn group(&self) -> IndicatorGroup {
IndicatorGroup::HealthOnly
}
}
async fn run_check(active: Option<&Arc<AgentRuntime>>) -> HealthCheckOutput {
let mut details = HashMap::new();
let Some(runtime) = active else {
details.insert(
"error".to_owned(),
serde_json::Value::String("agent plugin has not finished starting".to_owned()),
);
return HealthCheckOutput {
status: HealthStatus::Down,
details,
};
};
details.insert(
"provider".to_owned(),
serde_json::Value::String(runtime.config().provider.as_str().to_owned()),
);
match runtime.client().list_models().await {
Ok(models) => {
details.insert(
"models".to_owned(),
serde_json::Value::Number(models.len().into()),
);
HealthCheckOutput {
status: HealthStatus::Up,
details,
}
}
Err(err) => {
details.insert(
"error".to_owned(),
serde_json::Value::String(err.to_string()),
);
HealthCheckOutput {
status: HealthStatus::Down,
details,
}
}
}
}
#[cfg(test)]
mod tests;