use std::path::{Path, PathBuf};
use std::time::Duration;
use tokio::process::Command;
const SERVER_BIN: &str = "vllm-mlx";
const MIN_VERSION: (u64, u64, u64) = (0, 3, 0);
const PIP_SPEC: &str = "vllm-mlx>=0.3.0";
#[derive(Debug, thiserror::Error)]
pub enum RuntimeError {
#[error(
"`uv` is required to provision the vllm-mlx runtime but was not found on PATH; \
install uv (https://docs.astral.sh/uv/) or `pip install vllm-mlx` yourself"
)]
UvMissing,
#[error("provisioning step `{step}` failed: {detail}")]
Provision { step: &'static str, detail: String },
#[error("vllm-mlx still not found after provisioning into {0}")]
NotFoundAfterInstall(PathBuf),
}
#[derive(Debug, Clone)]
pub struct VllmRuntime {
pub server: PathBuf,
}
fn managed_root() -> Option<PathBuf> {
std::env::var_os("HOME").map(|h| PathBuf::from(h).join(".car").join("visual-runtime"))
}
fn managed_server_bin() -> Option<PathBuf> {
managed_root().map(|r| crate::managed_venv::venv_program(&r, SERVER_BIN))
}
fn search_dirs() -> Vec<PathBuf> {
let mut dirs: Vec<PathBuf> = std::env::var_os("PATH")
.map(|paths| std::env::split_paths(&paths).collect())
.unwrap_or_default();
if let Some(home) = std::env::var_os("HOME").map(PathBuf::from) {
dirs.push(home.join(".local").join("bin"));
dirs.push(
home.join(".local")
.join("share")
.join("uv")
.join("tools")
.join("vllm-mlx")
.join("bin"),
);
dirs.push(home.join(".car").join("visual-runtime").join("bin"));
}
dedupe(dirs)
}
fn dedupe(paths: Vec<PathBuf>) -> Vec<PathBuf> {
let mut out = Vec::new();
for p in paths {
if !out.contains(&p) {
out.push(p);
}
}
out
}
pub fn resolve_existing() -> Option<PathBuf> {
if let Some(p) = std::env::var_os("CAR_VLLM_MLX_BIN").map(PathBuf::from) {
if p.is_file() {
return absolutize(&p);
}
}
let mut candidates: Vec<(PathBuf, Option<(u64, u64, u64)>)> = Vec::new();
let mut seen: Vec<PathBuf> = Vec::new();
for dir in search_dirs() {
let candidate = dir.join(SERVER_BIN);
if !candidate.is_file() {
continue;
}
let canon = std::fs::canonicalize(&candidate).unwrap_or_else(|_| candidate.clone());
if seen.contains(&canon) {
continue;
}
seen.push(canon);
let version = binary_version(&candidate);
candidates.push((candidate, version));
}
best_candidate(candidates).and_then(|path| absolutize(&path))
}
fn best_candidate(candidates: Vec<(PathBuf, Option<(u64, u64, u64)>)>) -> Option<PathBuf> {
candidates
.into_iter()
.filter_map(|(path, version)| version.map(|v| (v, path)))
.filter(|(version, _)| *version >= MIN_VERSION)
.max_by(|(a, _), (b, _)| a.cmp(b))
.map(|(_, path)| path)
}
fn binary_version(binary: &Path) -> Option<(u64, u64, u64)> {
let head = std::fs::read_to_string(binary).ok()?;
let interp = head.lines().next()?.strip_prefix("#!")?.trim();
let out = std::process::Command::new(interp)
.args([
"-c",
"import importlib.metadata as m; print(m.version('vllm-mlx'))",
])
.output()
.ok()?;
if !out.status.success() {
return None;
}
parse_semver(String::from_utf8_lossy(&out.stdout).trim())
}
fn parse_semver(s: &str) -> Option<(u64, u64, u64)> {
let lead = |part: &str| -> u64 {
part.chars()
.take_while(|c| c.is_ascii_digit())
.collect::<String>()
.parse()
.unwrap_or(0)
};
let mut it = s.split('.');
let major = it.next()?.trim().parse().ok()?;
Some((
major,
lead(it.next().unwrap_or("0")),
lead(it.next().unwrap_or("0")),
))
}
fn absolutize(p: &Path) -> Option<PathBuf> {
std::fs::canonicalize(p)
.ok()
.or_else(|| Some(p.to_path_buf()))
}
pub async fn ensure_runtime() -> Result<VllmRuntime, RuntimeError> {
if let Some(server) = resolve_existing() {
return Ok(VllmRuntime { server });
}
provision().await?;
let server = managed_server_bin()
.filter(|p| p.is_file())
.or_else(resolve_existing)
.ok_or_else(|| RuntimeError::NotFoundAfterInstall(managed_root().unwrap_or_default()))?;
Ok(VllmRuntime { server })
}
async fn provision() -> Result<(), RuntimeError> {
if which("uv").is_none() {
return Err(RuntimeError::UvMissing);
}
let root = managed_root().ok_or_else(|| RuntimeError::Provision {
step: "resolve-home",
detail: "HOME is not set".into(),
})?;
let outcome = crate::managed_venv::ensure_venv(&root, "python3")
.await
.map_err(|e| RuntimeError::Provision {
step: "venv",
detail: e.to_string(),
})?;
if outcome == crate::managed_venv::VenvOutcome::Recreated {
tracing::warn!(
root = %root.display(),
"managed Python runtime had an unusable interpreter; rebuilt it \
(installed packages were discarded and are being reinstalled)"
);
}
let venv_python = crate::managed_venv::interpreter(&root);
run_uv(
"pip-install",
&[
"pip".into(),
"install".into(),
"--python".into(),
venv_python.display().to_string(),
PIP_SPEC.into(),
],
)
.await?;
Ok(())
}
async fn run_uv(step: &'static str, args: &[String]) -> Result<(), RuntimeError> {
let output = Command::new("uv")
.args(args)
.kill_on_drop(true)
.output()
.await
.map_err(|e| RuntimeError::Provision {
step,
detail: e.to_string(),
})?;
if output.status.success() {
Ok(())
} else {
Err(RuntimeError::Provision {
step,
detail: format!(
"uv exited with {}: {}",
output.status,
String::from_utf8_lossy(&output.stderr).trim()
),
})
}
}
pub async fn download_repo(repo: &str) -> Result<(), RuntimeError> {
ensure_runtime().await?;
let hf = managed_root()
.map(|r| crate::managed_venv::venv_program(&r, "hf"))
.filter(|p| p.is_file())
.ok_or_else(|| RuntimeError::Provision {
step: "resolve-hf-cli",
detail: "the managed runtime has no `hf` CLI".into(),
})?;
for disable_xet in [false, true] {
let mut cmd = Command::new(&hf);
cmd.arg("download").arg(repo).kill_on_drop(true);
if disable_xet {
cmd.env("HF_HUB_DISABLE_XET", "1");
}
let output = cmd.output().await.map_err(|e| RuntimeError::Provision {
step: "hf-download",
detail: e.to_string(),
})?;
if output.status.success() {
return Ok(());
}
if disable_xet {
return Err(RuntimeError::Provision {
step: "hf-download",
detail: format!(
"hf download {repo} failed: {}",
String::from_utf8_lossy(&output.stderr).trim()
),
});
}
tracing::warn!(repo, "hf download failed; retrying with Xet disabled");
}
unreachable!("the loop returns on both iterations")
}
pub fn serviceable() -> bool {
resolve_existing().is_some() || which("uv").is_some()
}
pub(crate) fn which(name: &str) -> Option<PathBuf> {
std::env::var_os("PATH")?
.to_str()?
.split(':')
.map(|d| Path::new(d).join(name))
.find(|p| p.is_file())
}
pub async fn health_ok(endpoint: &str, timeout: Duration) -> bool {
let url = format!("{}/health", endpoint.trim_end_matches('/'));
let client = match reqwest::Client::builder().timeout(timeout).build() {
Ok(c) => c,
Err(_) => return false,
};
matches!(client.get(&url).send().await, Ok(r) if r.status().is_success())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn managed_paths_are_under_dot_car() {
if std::env::var_os("HOME").is_none() {
return; }
let root = managed_root().unwrap();
assert!(
root.ends_with(".car/visual-runtime"),
"root: {}",
root.display()
);
let bin = managed_server_bin().unwrap();
assert!(bin.ends_with(".car/visual-runtime/bin/vllm-mlx"));
}
#[test]
fn search_dirs_include_path_and_managed() {
let dirs = search_dirs();
if std::env::var_os("HOME").is_some() {
assert!(
dirs.iter().any(|d| d.ends_with(".car/visual-runtime/bin")),
"managed bin dir missing from search set"
);
}
}
#[test]
fn parse_semver_handles_plain_and_prerelease() {
assert_eq!(parse_semver("0.3.0"), Some((0, 3, 0)));
assert_eq!(parse_semver("1.2.10"), Some((1, 2, 10)));
assert_eq!(parse_semver("0.6.3rc1"), Some((0, 6, 3)));
assert_eq!(parse_semver("0.2"), Some((0, 2, 0)));
assert!(parse_semver("not-a-version").is_none());
assert!((0, 2, 9) < MIN_VERSION);
assert!((0, 3, 0) >= MIN_VERSION);
assert!((0, 6, 3) >= MIN_VERSION);
}
#[test]
fn env_override_resolves_when_file_exists() {
std::env::set_var("CAR_VLLM_MLX_BIN", "/nonexistent/vllm-mlx-xyz");
let resolved = resolve_existing();
assert!(
resolved
.as_ref()
.map(|p| !p.ends_with("vllm-mlx-xyz"))
.unwrap_or(true),
"non-file override must not be returned"
);
std::env::remove_var("CAR_VLLM_MLX_BIN");
}
}
#[cfg(test)]
mod resolution_tests {
use super::*;
fn p(s: &str) -> PathBuf {
PathBuf::from(s)
}
#[test]
fn highest_version_wins_not_first_above_the_floor() {
let picked = best_candidate(vec![
(p("/home/u/.local/bin/vllm-mlx"), Some((0, 3, 0))),
(
p("/home/u/.car/visual-runtime/bin/vllm-mlx"),
Some((0, 4, 1)),
),
]);
assert_eq!(picked, Some(p("/home/u/.car/visual-runtime/bin/vllm-mlx")));
}
#[test]
fn below_the_floor_is_rejected_even_when_it_is_the_only_one() {
assert_eq!(
best_candidate(vec![(p("/a/vllm-mlx"), Some((0, 2, 8)))]),
None
);
}
#[test]
fn an_undeterminable_version_is_not_selected() {
assert_eq!(best_candidate(vec![(p("/a/vllm-mlx"), None)]), None);
assert_eq!(
best_candidate(vec![
(p("/a/vllm-mlx"), None),
(p("/b/vllm-mlx"), Some((0, 4, 1))),
]),
Some(p("/b/vllm-mlx"))
);
}
#[test]
fn no_candidates_means_provision() {
assert_eq!(best_candidate(vec![]), None);
}
}