use std::sync::atomic::{AtomicU8, Ordering};
use serde::Serialize;
#[derive(Debug, Serialize)]
pub struct HealthSnapshot {
pub status: &'static str,
pub model: String,
pub backend: &'static str,
pub warm_up_state: &'static str,
}
mod state {
pub const STARTING: u8 = 0;
pub const READY: u8 = 1;
pub const UNHEALTHY: u8 = 2;
}
pub struct HealthState {
model: String,
state: AtomicU8,
}
pub const fn compiled_backend() -> &'static str {
"llama-server"
}
impl HealthState {
pub fn new(model: &str) -> Self {
Self {
model: model.into(),
state: AtomicU8::new(state::STARTING),
}
}
pub fn set_ready(&self) {
self.state.store(state::READY, Ordering::SeqCst);
}
pub fn set_unhealthy(&self) {
self.state.store(state::UNHEALTHY, Ordering::SeqCst);
}
pub fn is_ready(&self) -> bool {
self.state.load(Ordering::SeqCst) == state::READY
}
pub fn snapshot(&self) -> HealthSnapshot {
let s = self.state.load(Ordering::SeqCst);
let (status, warm_up_state) = match s {
state::READY => ("ok", "ready"),
state::UNHEALTHY => ("unhealthy", "unhealthy"),
_ => ("starting", "loading"),
};
HealthSnapshot {
status,
model: self.model.clone(),
backend: compiled_backend(),
warm_up_state,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn health_transitions_warmup() {
let h = HealthState::new("qwen3-4b");
assert_eq!(h.snapshot().warm_up_state, "loading");
h.set_ready();
let s = h.snapshot();
assert_eq!(s.warm_up_state, "ready");
assert_eq!(s.model, "qwen3-4b");
}
#[test]
fn health_reports_llama_server_backend() {
assert_eq!(compiled_backend(), "llama-server");
assert_eq!(HealthState::new("x").snapshot().backend, "llama-server");
}
#[test]
fn health_status_field() {
let h = HealthState::new("test");
assert_eq!(h.snapshot().status, "starting");
h.set_ready();
assert_eq!(h.snapshot().status, "ok");
}
#[test]
fn health_unhealthy_state() {
let h = HealthState::new("test");
h.set_unhealthy();
let s = h.snapshot();
assert_eq!(s.status, "unhealthy");
assert_eq!(s.warm_up_state, "unhealthy");
assert!(!h.is_ready(), "unhealthy ≠ ready");
}
#[test]
fn health_is_ready_only_when_ready() {
let h = HealthState::new("test");
assert!(!h.is_ready(), "starting n'est pas ready");
h.set_ready();
assert!(h.is_ready(), "ready est ready");
h.set_unhealthy();
assert!(!h.is_ready(), "unhealthy n'est pas ready");
}
}