use std::path::PathBuf;
use super::config::ManagedConfig;
use super::spec::{InferFlavor, InferenceSpec};
use crate::model_manifest::{ModelFormat, ResolvedModel};
use crate::workload_runtime::types::RuntimeError;
#[derive(Debug, Clone)]
pub struct Launch {
pub program: PathBuf,
pub args: Vec<String>,
pub dir: PathBuf,
pub env: Vec<(String, String)>,
}
fn refuse(m: impl Into<String>) -> RuntimeError {
RuntimeError::AdmissionRefused(m.into())
}
pub fn check_format(flavor: InferFlavor, m: &ResolvedModel) -> Result<(), RuntimeError> {
let ok = match flavor {
InferFlavor::LlamaCpp => m.format == ModelFormat::Gguf,
InferFlavor::MlxLm => matches!(m.format, ModelFormat::Mlx | ModelFormat::Safetensors),
InferFlavor::Ollama => m.format == ModelFormat::Ollama,
};
if ok {
Ok(())
} else {
Err(refuse(format!(
"{} cannot load a {} model ({})",
flavor.id(),
m.format.as_str(),
m.name
)))
}
}
fn weights_arg(flavor: InferFlavor, m: &ResolvedModel) -> Result<String, RuntimeError> {
let p = match flavor {
InferFlavor::LlamaCpp => m
.shards
.first()
.cloned()
.ok_or_else(|| refuse("model has no shards"))?,
_ => m.root.clone(),
};
p.to_str()
.map(str::to_string)
.ok_or_else(|| refuse("model path is not UTF-8"))
}
pub fn build_launch(
flavor: InferFlavor,
spec: &InferenceSpec,
model: &ResolvedModel,
draft: Option<&ResolvedModel>,
cfg: &ManagedConfig,
instance_id: &str,
) -> Result<Launch, RuntimeError> {
check_format(flavor, model)?;
let program = cfg.serve_program.clone().ok_or_else(|| {
refuse(format!(
"{} managed mode needs a serve program",
flavor.id()
))
})?;
let mut args = vec![weights_arg(flavor, model)?];
args.extend(["--port".into(), spec.port()?.to_string()]);
args.extend(["--host".into(), spec.bind_ip()?.to_string()]);
if flavor == InferFlavor::LlamaCpp {
if let Some(c) = spec.serve.ctx {
args.extend(["--ctx".into(), c.to_string()]);
}
if let Some(q) = &spec.serve.kv_quant {
args.extend(["--kv".into(), q.clone()]);
}
if let Some(d) = draft {
check_format(flavor, d)?;
args.extend(["--draft".into(), weights_arg(flavor, d)?]);
}
}
args.extend(spec.serve.extra_args.iter().cloned());
let mut env = cfg.env.clone();
for name in &cfg.env_passthrough {
if let Ok(v) = std::env::var(name) {
env.push((name.clone(), v));
}
}
Ok(Launch {
program,
args,
dir: cfg.data_root.join(instance_id),
env,
})
}