1use std::collections::HashMap;
8
9use axum::extract::State;
10use axum::http::StatusCode;
11use axum::response::IntoResponse;
12use axum::Json;
13use embacle::discovery::resolve_binary;
14use tracing::debug;
15
16use crate::openai_types::HealthResponse;
17use crate::runner::ALL_PROVIDERS;
18use crate::state::SharedState;
19
20pub async fn handle(State(state): State<SharedState>) -> impl IntoResponse {
26 let mut providers = HashMap::new();
27 let mut any_ready = false;
28 let state_guard = state.read().await;
29
30 for &provider in ALL_PROVIDERS {
31 let binary_name = provider.binary_name();
32 let env_key = provider.env_override_key();
33 let env_override = std::env::var(env_key).ok();
34
35 if resolve_binary(binary_name, env_override.as_deref()).is_err() {
36 providers.insert(provider.to_string(), "not_found".to_owned());
37 continue;
38 }
39
40 match state_guard.get_runner(provider).await {
41 Ok(runner) => match runner.health_check().await {
42 Ok(true) => {
43 providers.insert(provider.to_string(), "ready".to_owned());
44 any_ready = true;
45 }
46 Ok(false) => {
47 providers.insert(provider.to_string(), "not_ready".to_owned());
48 }
49 Err(e) => {
50 debug!(provider = %provider, error = %e, "Health check failed");
51 providers.insert(provider.to_string(), format!("error: {e}"));
52 }
53 },
54 Err(e) => {
55 debug!(provider = %provider, error = %e, "Failed to create runner");
56 providers.insert(provider.to_string(), format!("error: {e}"));
57 }
58 }
59 }
60
61 let status_str = if any_ready { "ok" } else { "degraded" };
62 let http_status = if any_ready {
63 StatusCode::OK
64 } else {
65 StatusCode::SERVICE_UNAVAILABLE
66 };
67
68 let resp = HealthResponse {
69 status: status_str,
70 providers,
71 };
72
73 (http_status, Json(resp))
74}