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