use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::Duration;
use super::spec::InferFlavor;
use crate::model_manifest::ModelRegistry;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RestartPolicy {
pub max_restarts: u32,
pub base: Duration,
pub cap: Duration,
pub stable_after: Duration,
}
impl Default for RestartPolicy {
fn default() -> Self {
Self {
max_restarts: 5,
base: Duration::from_secs(2),
cap: Duration::from_secs(60),
stable_after: Duration::from_secs(60),
}
}
}
impl RestartPolicy {
pub fn delay(&self, attempt: u32) -> Duration {
self.base
.saturating_mul(1u32 << attempt.min(16))
.min(self.cap)
}
}
#[derive(Clone)]
pub struct ManagedConfig {
pub serve_program: Option<PathBuf>,
pub version_probe: Option<(PathBuf, Vec<String>)>,
pub data_root: PathBuf,
pub models: Arc<ModelRegistry>,
pub env: Vec<(String, String)>,
pub env_passthrough: Vec<String>,
pub run_as: Option<(u32, u32)>,
pub restart: RestartPolicy,
pub keep_alive: String,
pub ledger: Option<Arc<super::residency::ResidencyLedger>>,
}
impl ManagedConfig {
pub fn new(models: Arc<ModelRegistry>, data_root: PathBuf) -> Self {
Self {
serve_program: None,
version_probe: None,
data_root,
models,
env: Vec::new(),
env_passthrough: vec!["HOME".into(), "PATH".into()],
run_as: None,
restart: RestartPolicy::default(),
keep_alive: "30m".into(),
ledger: None,
}
}
pub fn with_ledger(mut self, ledger: Arc<super::residency::ResidencyLedger>) -> Self {
self.ledger = Some(ledger);
self
}
pub fn with_serve_program(mut self, p: impl Into<PathBuf>) -> Self {
self.serve_program = Some(p.into());
self
}
}
pub fn lab_serve_program(home: &Path, flavor: InferFlavor) -> Option<PathBuf> {
let name = match flavor {
InferFlavor::LlamaCpp => "serve-llamacpp",
InferFlavor::MlxLm => "serve",
InferFlavor::Ollama => return None,
};
Some(home.join("llm").join("bin").join(name))
}
#[derive(Clone)]
pub enum InferMode {
Adopted,
Managed(Box<ManagedConfig>),
}
#[derive(Clone)]
pub struct InferConfig {
pub flavor: InferFlavor,
pub mode: InferMode,
pub probe_timeout: Duration,
pub probe_port: Option<u16>,
pub allowed_ports: Option<(u16, u16)>,
pub deep_timeout: Duration,
}
impl InferConfig {
pub fn with_port_range(mut self, lo: u16, hi: u16) -> Self {
self.allowed_ports = Some((lo, hi));
self
}
pub fn with_probe_port(mut self, port: u16) -> Self {
self.probe_port = Some(port);
self
}
pub fn adopted(flavor: InferFlavor) -> Self {
Self {
flavor,
mode: InferMode::Adopted,
probe_timeout: Duration::from_secs(2),
probe_port: None,
allowed_ports: None,
deep_timeout: Duration::from_secs(300),
}
}
pub fn managed(flavor: InferFlavor, cfg: ManagedConfig) -> Self {
Self {
flavor,
mode: InferMode::Managed(Box::new(cfg)),
probe_timeout: Duration::from_secs(2),
probe_port: None,
allowed_ports: None,
deep_timeout: Duration::from_secs(300),
}
}
}