impl InferenceConfig {
#[must_use]
pub fn new(model_path: impl Into<PathBuf>) -> Self {
Self {
model_path: model_path.into(),
port: 8080,
max_batch_size: 32,
timeout_ms: 100,
enable_cors: true,
metrics_path: Some("/metrics".into()),
health_path: Some("/health".into()),
}
}
#[must_use]
pub const fn with_port(mut self, port: u16) -> Self {
self.port = port;
self
}
#[must_use]
pub const fn with_batch_size(mut self, size: u32) -> Self {
self.max_batch_size = size;
self
}
#[must_use]
pub const fn with_timeout_ms(mut self, ms: u64) -> Self {
self.timeout_ms = ms;
self
}
#[must_use]
pub const fn without_cors(mut self) -> Self {
self.enable_cors = false;
self
}
#[must_use]
pub fn predict_url(&self) -> String {
format!("http://localhost:{}/predict", self.port)
}
#[must_use]
pub fn batch_predict_url(&self) -> String {
format!("http://localhost:{}/batch_predict", self.port)
}
}
impl Default for InferenceConfig {
fn default() -> Self {
Self::new("model.apr")
}
}
#[derive(Debug, Clone)]
pub struct StackHealth {
pub components: HashMap<StackComponent, ComponentHealth>,
pub overall: HealthStatus,
pub checked_at: String,
}
impl StackHealth {
#[must_use]
pub fn new() -> Self {
Self {
components: HashMap::new(),
overall: HealthStatus::Unknown,
checked_at: "2025-01-01T00:00:00Z".into(),
}
}
pub fn set_component(&mut self, component: StackComponent, health: ComponentHealth) {
self.components.insert(component, health);
self.update_overall();
}
fn update_overall(&mut self) {
if self.components.is_empty() {
self.overall = HealthStatus::Unknown;
return;
}
let all_healthy = self
.components
.values()
.all(|h| h.status == HealthStatus::Healthy);
let any_unhealthy = self
.components
.values()
.any(|h| h.status == HealthStatus::Unhealthy);
self.overall = if all_healthy {
HealthStatus::Healthy
} else if any_unhealthy {
HealthStatus::Unhealthy
} else {
HealthStatus::Degraded
};
}
#[must_use]
pub fn is_healthy(&self) -> bool {
self.overall == HealthStatus::Healthy
}
}
impl Default for StackHealth {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct ComponentHealth {
pub status: HealthStatus,
pub version: Option<String>,
pub response_time_ms: Option<u64>,
pub error: Option<String>,
}
impl ComponentHealth {
#[must_use]
pub fn healthy(version: impl Into<String>) -> Self {
Self {
status: HealthStatus::Healthy,
version: Some(version.into()),
response_time_ms: None,
error: None,
}
}
#[must_use]
pub fn unhealthy(error: impl Into<String>) -> Self {
Self {
status: HealthStatus::Unhealthy,
version: None,
response_time_ms: None,
error: Some(error.into()),
}
}
#[must_use]
pub fn degraded(version: impl Into<String>, reason: impl Into<String>) -> Self {
Self {
status: HealthStatus::Degraded,
version: Some(version.into()),
response_time_ms: None,
error: Some(reason.into()),
}
}
#[must_use]
pub const fn with_response_time(mut self, ms: u64) -> Self {
self.response_time_ms = Some(ms);
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum HealthStatus {
Healthy,
Degraded,
Unhealthy,
#[default]
Unknown,
}
impl HealthStatus {
#[must_use]
pub const fn name(&self) -> &'static str {
match self {
Self::Healthy => "healthy",
Self::Degraded => "degraded",
Self::Unhealthy => "unhealthy",
Self::Unknown => "unknown",
}
}
#[must_use]
pub const fn is_operational(&self) -> bool {
matches!(self, Self::Healthy | Self::Degraded)
}
}
impl std::fmt::Display for HealthStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name())
}
}
#[derive(Debug, Clone, Copy)]
pub struct FormatCompatibility {
pub apr_version: (u8, u8),
pub ald_version: (u8, u8),
pub compatible: bool,
}
impl FormatCompatibility {
#[must_use]
pub const fn current() -> Self {
Self {
apr_version: (1, 0),
ald_version: (1, 2),
compatible: true,
}
}
#[must_use]
pub const fn is_apr_compatible(&self, major: u8, minor: u8) -> bool {
major == self.apr_version.0 && minor <= self.apr_version.1
}
#[must_use]
pub const fn is_ald_compatible(&self, major: u8, minor: u8) -> bool {
major == self.ald_version.0 && minor <= self.ald_version.1
}
}
impl Default for FormatCompatibility {
fn default() -> Self {
Self::current()
}
}
#[cfg(test)]
mod tests;