Skip to main content

superfluid_daemon/
runtimes.rs

1//! Where each runtime's worker is, and what it says about itself.
2
3use std::collections::HashMap;
4use std::path::{Path, PathBuf};
5use std::process::Command;
6use std::sync::Mutex;
7use superfluid_engine::artifact::{Device, Format, TokenizerSource};
8use serde_json::Value;
9
10use crate::runtime_pick::RuntimeId;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum Source {
14    Env,
15    BesideDaemon,
16    BuiltIn,
17    Installed,
18    Shipped,
19}
20
21impl Source {
22    pub fn name(self) -> &'static str {
23        match self {
24            Source::Env => "environment",
25            Source::BesideDaemon => "beside superfluid",
26            Source::BuiltIn => "built in",
27            Source::Installed => "installed",
28            Source::Shipped => "shipped with superfluid",
29        }
30    }
31}
32
33#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct Worker {
35    pub bin: PathBuf,
36    pub prefix: Vec<String>,
37    pub source: Source,
38}
39
40impl Worker {
41    pub fn install(&self) -> Option<String> {
42        if self.source != Source::Installed {
43            return None;
44        }
45        self.bin.parent()?.parent()?.file_name().and_then(|n| n.to_str()).map(str::to_string)
46    }
47}
48
49#[derive(Debug, Clone, Default, PartialEq)]
50pub struct Info {
51    pub aliases: Vec<String>,
52    pub formats: Vec<Format>,
53    pub tokenizer: Option<TokenizerSource>,
54    pub named: Option<String>,
55}
56
57impl Info {
58    fn from_report(report: &Value) -> Info {
59        use superfluid_worker::check::{formats_from, tokenizer_from};
60        Info {
61            aliases: report["runtime"]["aliases"]
62                .as_array()
63                .map(|a| a.iter().filter_map(|x| x.as_str().map(str::to_string)).collect())
64                .unwrap_or_default(),
65            formats: formats_from(&report["formats"]),
66            tokenizer: tokenizer_from(&report["tokenizer"]),
67            named: report["named"].as_str().map(str::to_string),
68        }
69    }
70
71    pub fn format_of(&self, path: &Path) -> Option<&Format> {
72        self.formats.iter().find(|f| f.matches(path))
73    }
74
75    pub fn reads_described(&self) -> String {
76        let all: Vec<&str> = self.formats.iter().map(|f| f.describe.as_str()).collect();
77        match all.as_slice() {
78            [] => "no format: its worker declares none, so it is older than this superfluid (rebuild or update it)".to_string(),
79            [one] => one.to_string(),
80            [init @ .., last] => format!("{} or {last}", init.join(", ")),
81        }
82    }
83}
84
85#[derive(Debug, Clone)]
86pub struct Runtime {
87    pub id: RuntimeId,
88    pub worker: Worker,
89    pub version: Option<String>,
90    /// The contract number the runtime states and its adapter checked (baseRT's engine ABI).
91    pub abi: Option<u64>,
92    pub capabilities: Option<String>,
93    pub info: Info,
94    pub devices: Vec<Device>,
95    pub tokenizer: Option<PathBuf>,
96    pub pull: Option<Vec<(String, bool)>>,
97    /// The options it takes on `serve`, as `--<id>-<name> <value>` (see `runtime_options`).
98    pub options: Vec<superfluid_worker::check::OptionDecl>,
99}
100
101impl Runtime {
102    pub fn serve_args(&self, model: &Path, ctx: i32, max_batch: u32, kv_bits: i32) -> Vec<String> {
103        let mut args = self.worker.prefix.clone();
104        args.extend([
105            "--model".to_string(),
106            model.to_string_lossy().into_owned(),
107            "--max-context".to_string(),
108            ctx.to_string(),
109            "--max-batch".to_string(),
110            max_batch.to_string(),
111        ]);
112        if !matches!(self.static_capabilities().get("load", "kv_bits"), crate::capabilities::Cap::No(_)) {
113            args.push("--kv-bits".to_string());
114            args.push(kv_bits.to_string());
115        }
116        // The runtime's own options are in the worker's prefix, from `locate`.
117        args
118    }
119
120    pub fn static_capabilities(&self) -> crate::capabilities::Capabilities {
121        self.capabilities.as_deref().map(crate::capabilities::Capabilities::parse).unwrap_or_default()
122    }
123
124    pub fn in_process_capable(&self) -> bool {
125        crate::linked::get(self.id).is_some()
126    }
127
128    pub fn device(&self) -> Option<&Device> {
129        self.devices.first()
130    }
131}
132
133#[derive(Debug, Clone)]
134pub struct Found {
135    pub id: RuntimeId,
136    pub worker: Option<Worker>,
137    pub info: Option<Info>,
138    pub usable: Result<Runtime, String>,
139}
140
141pub struct Catalog {
142    home: PathBuf,
143    beside: Option<PathBuf>,
144    workerd: PathBuf,
145    usable: Mutex<HashMap<RuntimeId, Runtime>>,
146}
147
148pub fn default_home() -> PathBuf {
149    home_from(std::env::var_os("SUPERFLUID_HOME"), std::env::home_dir())
150}
151
152fn home_from(superfluid_home: Option<std::ffi::OsString>, user_home: Option<PathBuf>) -> PathBuf {
153    if let Some(h) = superfluid_home.filter(|h| !h.is_empty()) {
154        return PathBuf::from(h);
155    }
156    user_home.filter(|h| h.is_absolute()).unwrap_or_else(|| PathBuf::from("/")).join(".superfluid")
157}
158
159pub fn worker_name(id: RuntimeId) -> String {
160    format!("superfluid-worker-{}", id.base().name())
161}
162
163pub fn worker_env(id: RuntimeId) -> String {
164    format!("SUPERFLUID_WORKER_{}", id.base().name().to_ascii_uppercase().replace(['-', '.'], "_"))
165}
166
167impl Catalog {
168    pub fn new(home: PathBuf, beside: Option<PathBuf>, workerd: PathBuf) -> Catalog {
169        Catalog { home, beside, workerd, usable: Mutex::new(HashMap::new()) }
170    }
171
172    pub fn from_env() -> Catalog {
173        let beside = beside_this_executable();
174        let workerd = beside.clone().unwrap_or_default().join("superfluid-workerd");
175        Catalog::new(default_home(), beside, workerd)
176    }
177
178    /// [`Catalog::from_env`], with `workerd` running the linked runtimes'
179    /// workers in place of the `superfluid-workerd` beside this executable.
180    pub fn with_workerd(workerd: PathBuf) -> Catalog {
181        Catalog::new(default_home(), beside_this_executable(), workerd)
182    }
183
184    pub fn home(&self) -> &Path {
185        &self.home
186    }
187
188    /// The program that runs a linked runtime's worker.
189    pub fn workerd(&self) -> &Path {
190        &self.workerd
191    }
192
193    pub fn beside(&self) -> Option<&Path> {
194        self.beside.as_deref()
195    }
196
197    pub fn package_dir(&self, id: RuntimeId) -> PathBuf {
198        self.home.join("runtimes").join(id.base().name())
199    }
200
201    fn shipped_dirs(&self) -> Vec<PathBuf> {
202        let Some(beside) = &self.beside else { return Vec::new() };
203        [beside.join("..").join("libexec").join("superfluid"), beside.join("libexec").join("superfluid")]
204            .into_iter()
205            .filter_map(|d| std::fs::canonicalize(d).ok())
206            .filter(|d| d.is_dir())
207            .collect()
208    }
209
210    pub fn shipped(&self, id: RuntimeId) -> Option<PathBuf> {
211        let own = worker_name(id);
212        self.shipped_dirs().into_iter().map(|d| d.join(&own)).find(|p| is_executable(p))
213    }
214
215    pub fn shipped_ids(&self) -> Vec<RuntimeId> {
216        let mut ids: Vec<RuntimeId> = self.shipped_dirs().iter().flat_map(|d| workers_in(d)).collect();
217        ids.sort();
218        ids.dedup();
219        ids
220    }
221
222    pub fn ids(&self) -> Vec<RuntimeId> {
223        let mut ids: Vec<RuntimeId> = Vec::new();
224        for (k, _) in std::env::vars_os() {
225            if let Some(id) = k.to_str().and_then(|k| k.strip_prefix("SUPERFLUID_WORKER_")) {
226                ids.extend(RuntimeId::parse(&id.to_ascii_lowercase()));
227            }
228        }
229        if let Some(dir) = &self.beside {
230            ids.extend(workers_in(dir));
231        }
232        ids.extend(self.shipped_ids());
233        ids.extend(crate::linked::ids());
234        if let Ok(rd) = std::fs::read_dir(self.home.join("runtimes")) {
235            ids.extend(
236                rd.flatten()
237                    .filter(|e| e.path().is_dir())
238                    .filter_map(|e| e.file_name().to_str().and_then(RuntimeId::parse)),
239            );
240        }
241        ids.sort();
242        ids.dedup();
243        ids
244    }
245
246    /// The worker for `id`, started with the runtime's options from the first probe on.
247    pub fn locate(&self, id: RuntimeId) -> Option<Worker> {
248        let mut worker = self.locate_plain(id)?;
249        for (flag, value) in crate::runtime_options::for_runtime(id) {
250            worker.prefix.push(flag);
251            worker.prefix.push(value);
252        }
253        Some(worker)
254    }
255
256    /// The worker for `id` as found, with no option of the command line.
257    pub(crate) fn locate_plain(&self, id: RuntimeId) -> Option<Worker> {
258        if let Some(install) = id.install() {
259            let bin = self.package_dir(id).join(install).join("bin").join(worker_name(id));
260            return std::fs::canonicalize(bin).ok().filter(|p| p.is_file()).map(|bin| Worker {
261                bin,
262                prefix: Vec::new(),
263                source: Source::Installed,
264            });
265        }
266        if let Some(p) = std::env::var_os(worker_env(id)) {
267            return Some(Worker { bin: PathBuf::from(p), prefix: Vec::new(), source: Source::Env });
268        }
269        if crate::linked::get(id).is_some() {
270            return Some(Worker {
271                bin: self.workerd.clone(),
272                prefix: vec!["--engine".into(), id.name().into()],
273                source: Source::BuiltIn,
274            });
275        }
276        let own = worker_name(id);
277        if let Some(bin) = self.beside.as_ref().map(|d| d.join(&own)).filter(|p| is_executable(p)) {
278            return Some(Worker { bin, prefix: Vec::new(), source: Source::BesideDaemon });
279        }
280        let bin = self.package_dir(id).join("current").join("bin").join(&own);
281        let installed = std::fs::canonicalize(bin).ok().filter(|p| p.is_file()).map(|bin| Worker {
282            bin,
283            prefix: Vec::new(),
284            source: Source::Installed,
285        });
286        installed.or_else(|| self.shipped(id).map(|bin| Worker { bin, prefix: Vec::new(), source: Source::Shipped }))
287    }
288
289    pub fn require(&self, id: RuntimeId) -> Result<Runtime, String> {
290        if let Some(r) = self.remembered(id) {
291            return Ok(r);
292        }
293        let found = self.inspect(id);
294        if let Ok(runtime) = &found.usable {
295            self.usable.lock().expect("runtime catalog").insert(id, runtime.clone());
296        }
297        found.usable
298    }
299
300    fn remembered(&self, id: RuntimeId) -> Option<Runtime> {
301        let mut usable = self.usable.lock().expect("runtime catalog");
302        let r = usable.get(&id)?;
303        if r.worker.source != Source::BuiltIn && !r.worker.bin.is_file() {
304            usable.remove(&id);
305            return None;
306        }
307        Some(r.clone())
308    }
309
310    pub fn inspect(&self, id: RuntimeId) -> Found {
311        if let Some(r) = self.remembered(id) {
312            return Found { id, worker: Some(r.worker.clone()), info: Some(r.info.clone()), usable: Ok(r) };
313        }
314        let Some(worker) = self.locate(id) else {
315            return Found { id, worker: None, info: None, usable: Err(self.missing(id)) };
316        };
317        let report = if worker.source == Source::BuiltIn && !worker.bin.is_file() {
318            crate::linked::report(id, None).ok_or_else(|| format!("this build does not link the {id} runtime"))
319        } else {
320            run_check(&worker)
321        };
322        let report = match report {
323            Ok(r) => r,
324            Err(why) => return Found { id, worker: Some(worker), info: None, usable: Err(why) },
325        };
326        let info = Info::from_report(&report);
327        let usable = runtime_from_check(id, worker.clone(), &report).and_then(|r| self.with_tokenizer(r));
328        // A check starts the worker (for MLX, a Python and its imports):
329        // run it once per runtime, not once per question asked of it.
330        if let Ok(runtime) = &usable {
331            self.usable.lock().expect("runtime catalog").insert(id, runtime.clone());
332        }
333        Found { id, worker: Some(worker), info: Some(info), usable }
334    }
335
336    fn with_tokenizer(&self, mut runtime: Runtime) -> Result<Runtime, String> {
337        let linked = crate::linked::get(runtime.id.base()).is_some();
338        match runtime.info.tokenizer.clone() {
339            Some(TokenizerSource::Library(stem)) => {
340                runtime.tokenizer = crate::dylib_tokenizer::find_library(&runtime.worker.bin, &stem);
341                if runtime.tokenizer.is_none() && !linked {
342                    return Err(format!(
343                        "the {} runtime at {} ships no tokenizer library ({} beside it or in ../lib)",
344                        runtime.id,
345                        runtime.worker.bin.display(),
346                        crate::dylib_tokenizer::library_file(&stem)
347                    ));
348                }
349            }
350            Some(TokenizerSource::HuggingFace) => {}
351            Some(TokenizerSource::Engine) if linked => {}
352            Some(TokenizerSource::Engine) => {
353                return Err(format!(
354                    "only the {id} runtime's engine tokenizes its artifacts, and this superfluid does not link it \
355                     (a source build adds it with --features {feature})",
356                    id = runtime.id,
357                    feature = crate::linked::feature_for(runtime.id.base().name()).unwrap_or("<the runtime>"),
358                ))
359            }
360            None => {}
361        }
362        Ok(runtime)
363    }
364
365    fn missing(&self, id: RuntimeId) -> String {
366        if let Some(install) = id.install() {
367            let have: Vec<String> = crate::installs::list(self, id).into_iter().map(|i| i.name).collect();
368            return format!(
369                "{} has no install {install} here (installed: {})",
370                id.base(),
371                if have.is_empty() { "none".to_string() } else { have.join(", ") }
372            );
373        }
374        let shipped = self.shipped_ids();
375        if !shipped.is_empty() {
376            return format!(
377                "the {id} runtime is not here: this superfluid ships no adapter for it (it ships: {}), and none is installed in {}",
378                shipped.iter().map(|i| i.name()).collect::<Vec<_>>().join(", "),
379                self.package_dir(id).display()
380            );
381        }
382        format!(
383            "the {id} runtime is not installed: no {w} in {pkg} or beside superfluid \
384             (install it with `superfluid runtime install {id}`, or build {w} with `cargo build -p superfluid-adapter-{id}`)",
385            w = worker_name(id),
386            pkg = self.package_dir(id).join("current").join("bin").display(),
387        )
388    }
389
390    pub fn survey(&self) -> Vec<Found> {
391        self.ids().into_iter().map(|id| self.inspect(id)).collect()
392    }
393
394    pub fn named(&self, name: &str) -> Result<RuntimeId, String> {
395        let name = name.trim();
396        if let Some((base, install)) = name.split_once('@') {
397            let id = self.named(base)?;
398            let at = RuntimeId::parse(&format!("{id}@{install}")).ok_or_else(|| format!("'{install}' is not an install's name"))?;
399            if crate::installs::list(self, id).iter().any(|i| i.name == install) {
400                return Ok(at);
401            }
402            return Err(self.missing(at));
403        }
404        let ids = self.ids();
405        if let Some(id) = ids.iter().copied().find(|id| id.name() == name) {
406            return Ok(id);
407        }
408        for found in self.survey() {
409            if found.info.is_some_and(|i| i.aliases.iter().any(|a| a == name)) {
410                return Ok(found.id);
411            }
412        }
413        Err(format!("no runtime '{name}' here"))
414    }
415
416    pub fn reads(&self, id: RuntimeId, path: &Path) -> Result<(), String> {
417        let found = self.inspect(id);
418        let Some(info) = &found.info else {
419            return found.usable.map(|_| ());
420        };
421        if info.format_of(path).is_some() {
422            return Ok(());
423        }
424        let mut why = format!("runtime {id} cannot serve {}: it reads {}", path.display(), info.reads_described());
425        match self.readers(path).first() {
426            Some((other, format)) => why.push_str(&format!(
427                ", and this is {} (pass --runtime {other}, or convert the model)",
428                format.describe
429            )),
430            None => why.push_str(", and no runtime here reads this file"),
431        }
432        Err(why)
433    }
434
435    pub fn readers(&self, path: &Path) -> Vec<(RuntimeId, Format)> {
436        let mut known = self.known_readers(path);
437        if known.is_empty() {
438            let found = self.survey();
439            let cannot_run = |id: &RuntimeId| found.iter().any(|f| f.id == *id && f.usable.is_err());
440            known = readers_in(&found, path);
441            known.sort_by_key(|(id, _)| (cannot_run(id), *id));
442            return known;
443        }
444        known
445    }
446
447    fn known_readers(&self, path: &Path) -> Vec<(RuntimeId, Format)> {
448        let mut known: Vec<(RuntimeId, Format)> = self
449            .usable
450            .lock()
451            .expect("runtime catalog")
452            .values()
453            .filter(|r| r.id.install().is_none())
454            .filter_map(|r| Some((r.id, r.info.format_of(path)?.clone())))
455            .collect();
456        known.sort_by_key(|(id, _)| *id);
457        known
458    }
459
460    pub fn reader_of(&self, path: &Path) -> Result<RuntimeId, String> {
461        if let Some((id, _)) = self.known_readers(path).first() {
462            return Ok(*id);
463        }
464        let mut cannot_run = None;
465        for id in self.ids() {
466            let found = self.inspect(id);
467            if found.info.as_ref().and_then(|info| info.format_of(path)).is_none() {
468                continue;
469            }
470            match found.usable {
471                Ok(runtime) => {
472                    self.usable.lock().expect("runtime catalog").insert(id, runtime);
473                    return Ok(id);
474                }
475                Err(_) => {
476                    cannot_run.get_or_insert(id);
477                }
478            }
479        }
480        if let Some(id) = cannot_run {
481            return Ok(id);
482        }
483        Err(format!(
484            "no runtime here reads {} ({}); `superfluid runtimes {}` says more, and `superfluid runtime install <id>` adds a runtime",
485            path.display(),
486            self.here_described(),
487            path.display()
488        ))
489    }
490
491    pub fn not_here(&self, name: &str, path: &Path, why: &str) -> String {
492        let mut msg = format!("{why} to serve {} ({})", path.display(), self.here_described());
493        if let Some((other, format)) = self.readers(path).first() {
494            msg.push_str(&format!("; it is {}, which {other} here reads: pass --runtime {other}", format.describe));
495        }
496        let runtime = name.split('@').next().unwrap_or(name);
497        msg.push_str(&format!("; `superfluid runtime install {runtime}` installs {runtime}"));
498        msg
499    }
500
501    pub fn here_described(&self) -> String {
502        let all = self.survey();
503        if all.is_empty() {
504            return "no runtime is installed".to_string();
505        }
506        let parts: Vec<String> = all
507            .iter()
508            .map(|f| {
509                let state = if f.usable.is_ok() { "" } else { " (not usable)" };
510                match &f.info {
511                    Some(info) => format!("{}{state} reads {}", f.id, info.reads_described()),
512                    None => format!("{}{state}", f.id),
513                }
514            })
515            .collect();
516        format!("runtimes here: {}", parts.join("; "))
517    }
518}
519
520fn repair_command(id: RuntimeId) -> String {
521    match id.install() {
522        Some(install) => format!("superfluid runtime repair {} {install}", id.base()),
523        None => format!("superfluid runtime repair {id}"),
524    }
525}
526
527pub fn readers_in(found: &[Found], path: &Path) -> Vec<(RuntimeId, Format)> {
528    found
529        .iter()
530        .filter_map(|f| {
531            let format = f.info.as_ref()?.format_of(path)?.clone();
532            Some((f.id, format))
533        })
534        .collect()
535}
536
537fn workers_in(dir: &Path) -> Vec<RuntimeId> {
538    std::fs::read_dir(dir)
539        .map(|rd| {
540            rd.flatten()
541                .filter(|e| is_executable(&e.path()))
542                .filter_map(|e| e.file_name().to_str().and_then(|n| n.strip_prefix("superfluid-worker-")).and_then(RuntimeId::parse))
543                .collect()
544        })
545        .unwrap_or_default()
546}
547
548fn is_executable(p: &Path) -> bool {
549    use std::os::unix::fs::PermissionsExt;
550    std::fs::metadata(p).is_ok_and(|m| m.is_file() && m.permissions().mode() & 0o111 != 0)
551}
552
553pub fn check_worker(id: RuntimeId, worker: &Worker) -> Result<Runtime, String> {
554    let report = run_check(worker)?;
555    runtime_from_check(id, worker.clone(), &report)
556}
557
558fn run_check(w: &Worker) -> Result<Value, String> {
559    run_check_model(w, None)
560}
561
562pub fn run_check_model(w: &Worker, model: Option<&Path>) -> Result<Value, String> {
563    let mut cmd = Command::new(&w.bin);
564    crate::exec::without_secrets(&mut cmd).arg("check").args(&w.prefix);
565    if let Some(m) = model {
566        cmd.arg("--model").arg(m);
567    }
568    let out = crate::exec::output_within(&mut cmd, crate::exec::ANSWER_WITHIN)
569        .map_err(|e| format!("{} could not start: {e}", w.bin.display()))?
570        .ok_or_else(|| format!("{} check did not answer within {}s", w.bin.display(), crate::exec::ANSWER_WITHIN.as_secs()))?;
571    serde_json::from_slice::<Value>(&out.stdout)
572        .map_err(|_| format!("{} check failed ({}){}", w.bin.display(), out.status, crate::exec::stderr_tail(&out.stderr)))
573}
574
575pub fn sizing_of(rt: &Runtime, model: &Path) -> Result<superfluid_adapter_kit::Sizing, String> {
576    let report = run_check_model(&rt.worker, Some(model))?;
577    match &report["model"]["sizing"] {
578        Value::Null => Err(format!("the {} runtime sizes no context window here", rt.id.base().name())),
579        Value::String(why) => Err(why.clone()),
580        said => superfluid_adapter_kit::Sizing::from_json(said)
581            .ok_or_else(|| format!("{} check reported a sizing this superfluid does not read: {said}", rt.worker.bin.display())),
582    }
583}
584
585/// This executable's directory with links resolved, so a symlinked `superfluid` still finds its workerd and adapters.
586fn beside_this_executable() -> Option<PathBuf> {
587    let exe = std::env::current_exe().ok()?;
588    let exe = std::fs::canonicalize(&exe).unwrap_or(exe);
589    exe.parent().map(Path::to_path_buf)
590}
591
592fn runtime_from_check(id: RuntimeId, worker: Worker, report: &Value) -> Result<Runtime, String> {
593    let reported = report["runtime"]["id"].as_str().unwrap_or_default();
594    if reported != id.base().name() {
595        return Err(format!("{} is not the {id} runtime's worker (it reports '{reported}')", worker.bin.display()));
596    }
597    let speaks: Vec<u64> = report["link_w"].as_array().map(|a| a.iter().filter_map(Value::as_u64).collect()).unwrap_or_default();
598    let ours = u64::from(superfluid_agent::client::PROTO_VERSION);
599    if !speaks.contains(&ours) {
600        let fix = if worker.source == Source::Installed && speaks.iter().all(|v| *v < ours) {
601            format!("`{}` installs it again with this superfluid's worker", repair_command(id))
602        } else {
603            "update the one that is older".to_string()
604        };
605        return Err(format!(
606            "the {id} runtime at {} speaks Link W {speaks:?} and this superfluid speaks {ours}: {fix}",
607            worker.bin.display()
608        ));
609    }
610    match &report["available"] {
611        Value::Bool(true) => {}
612        Value::String(why) if worker.source == Source::Shipped => {
613            return Err(format!("the {id} runtime is not installed: {why}"));
614        }
615        Value::String(why) => {
616            return Err(format!(
617                "the {id} runtime at {} cannot run here: {why}{}",
618                worker.bin.display(),
619                if worker.source == Source::Installed {
620                    format!(" (`{}` reinstalls it)", repair_command(id))
621                } else {
622                    String::new()
623                }
624            ))
625        }
626        other => return Err(format!("{} check reported availability {other}", worker.bin.display())),
627    }
628    Ok(Runtime {
629        id,
630        version: report["runtime"]["version"].as_str().map(str::to_string),
631        abi: report["runtime"]["abi"].as_u64(),
632        capabilities: report.get("capabilities").filter(|c| c.as_object().is_some_and(|o| !o.is_empty())).map(Value::to_string),
633        info: Info::from_report(report),
634        devices: superfluid_worker::check::devices_from(&report["devices"]),
635        tokenizer: None,
636        pull: report.get("pull").map(|p| {
637            p["options"]
638                .as_array()
639                .map(|a| a.iter().filter_map(|o| Some((o["name"].as_str()?.to_string(), o["value"].as_bool().unwrap_or(false)))).collect())
640                .unwrap_or_default()
641        }),
642        options: superfluid_worker::check::options_from(&report["options"]),
643        worker,
644    })
645}
646
647#[cfg(test)]
648pub(crate) mod tests {
649    use super::*;
650    use std::os::unix::fs::PermissionsExt;
651
652    fn scratch(tag: &str) -> PathBuf {
653        let d = std::env::temp_dir().join(format!("superfluid-runtimes-{tag}-{}", std::process::id()));
654        let _ = std::fs::remove_dir_all(&d);
655        std::fs::create_dir_all(&d).unwrap();
656        d
657    }
658
659    pub(crate) fn fake_worker(dir: &Path, name: &str, report: &str, exit: i32) -> PathBuf {
660        let p = dir.join(name);
661        std::fs::write(&p, format!("#!/bin/sh\ncat <<'EOF'\n{report}\nEOF\nexit {exit}\n")).unwrap();
662        std::fs::set_permissions(&p, std::fs::Permissions::from_mode(0o755)).unwrap();
663        p
664    }
665
666    pub(crate) fn report(id: &str, link_w: u16, available: &str) -> String {
667        let (aliases, formats, tokenizer) = match id {
668            "llamacpp" => (
669                r#"["llama.cpp"]"#,
670                r#"[{"id":"gguf","describe":"a GGUF file","file":{"extensions":["gguf"],"magic":"GGUF"}}]"#,
671                r#"{"from":"library","name":"superfluid_tokenizer_llamacpp"}"#,
672            ),
673            "mlx" => (
674                "[]",
675                r#"[{"id":"mlx","describe":"an MLX model directory","directory":{"files":["config.json"],"extensions":["safetensors"]}}]"#,
676                r#"{"from":"huggingface"}"#,
677            ),
678            _ => (
679                r#"["native"]"#,
680                r#"[{"id":"base","describe":"a .base bundle","file":{"extensions":["base"],"magic":"BASE"}}]"#,
681                r#"{"from":"engine"}"#,
682            ),
683        };
684        format!(
685            r#"{{"runtime":{{"id":"{id}","version":"v1","aliases":{aliases}}},"link_w":[{link_w}],"available":{available},"formats":{formats},"tokenizer":{tokenizer},"devices":[{{"backend":"Metal","name":"Apple M5 Pro","memory":36000000000}}],"capabilities":{{"descriptor_version":1,"load":{{"kv_bits":"no such knob"}},"serving":{{"park_lossy":"no lossy encoding"}}}}}}"#
686        )
687    }
688
689    const V: u16 = superfluid_agent::client::PROTO_VERSION;
690
691    #[test]
692    fn a_worker_is_started_with_its_runtime_options_from_the_first_probe_on() {
693        // The options set for this process (first call wins; only this test sets them).
694        crate::runtime_options::set(vec![(RuntimeId::new("optrt"), vec![("--lib".to_string(), "/custom/lib".to_string())])]);
695        let root = std::env::temp_dir().join(format!("superfluid-runtime-options-probe-{}", std::process::id()));
696        let _ = std::fs::remove_dir_all(&root);
697        let (beside, libexec) = (root.join("bin"), root.join("libexec").join("superfluid"));
698        std::fs::create_dir_all(&beside).unwrap();
699        std::fs::create_dir_all(&libexec).unwrap();
700        let toy = libexec.join("superfluid-worker-optrt");
701        std::fs::write(&toy, "#!/bin/sh\nprintf '{\"runtime\":{\"id\":\"optrt\",\"version\":\"v1\"},\"received\":\"%s\"}\n' \"$*\"\n").unwrap();
702        std::fs::set_permissions(&toy, std::fs::Permissions::from_mode(0o755)).unwrap();
703        let cat = Catalog::new(root.join("home"), Some(beside.clone()), beside.join("superfluid-workerd"));
704        let id = RuntimeId::new("optrt");
705        let w = cat.locate(id).unwrap();
706        assert_eq!(w.prefix, ["--lib", "/custom/lib"], "the options ride in the prefix");
707        assert!(cat.locate_plain(id).unwrap().prefix.is_empty());
708        let report = run_check_model(&w, None).unwrap();
709        assert_eq!(report["received"], "check --lib /custom/lib", "the first probe already carries them");
710        let rt = Runtime {
711            id,
712            worker: w,
713            version: None,
714            abi: None,
715            capabilities: None,
716            info: Info::default(),
717            devices: Vec::new(),
718            tokenizer: None,
719            pull: None,
720            options: Vec::new(),
721        };
722        let args = rt.serve_args(Path::new("/m"), 4096, 8, 0);
723        assert_eq!(args.iter().filter(|a| *a == "--lib").count(), 1, "once, from the prefix: {args:?}");
724        assert!(cat.locate(RuntimeId::new("otherrt")).is_none_or(|w| w.prefix.is_empty()), "another runtime's worker gets nothing");
725        let _ = std::fs::remove_dir_all(&root);
726    }
727
728    pub(crate) fn toy_report(id: &str, available: &str) -> String {
729        format!(
730            r#"{{"runtime":{{"id":"{id}","version":"v1"}},"link_w":[{V}],"available":{available},"formats":[{{"id":"toy","describe":"a toy file","file":{{"extensions":["toy"]}}}}],"tokenizer":{{"from":"huggingface"}}}}"#
731        )
732    }
733
734    fn catalog(home: &Path) -> Catalog {
735        Catalog::new(home.to_path_buf(), None, home.join("superfluid-workerd"))
736    }
737
738    fn install_fake(home: &Path, id: &str, report_text: &str, exit: i32) -> PathBuf {
739        let bin_dir = home.join("runtimes").join(id).join("current/bin");
740        std::fs::create_dir_all(&bin_dir).unwrap();
741        fake_worker(&bin_dir, &format!("superfluid-worker-{id}"), report_text, exit)
742    }
743
744    #[test]
745    fn a_models_sizing_is_read_from_its_runtimes_check() {
746        let dir = scratch("sizing");
747        let with_model = |model: &str| {
748            let r = report("llamacpp", V, "true");
749            format!("{},\"model\":{model}}}", &r[..r.len() - 1])
750        };
751        let rt = |name: &str, text: &str| Runtime {
752            id: RuntimeId::new("llamacpp"),
753            worker: Worker { bin: fake_worker(&dir, name, text, 0), prefix: Vec::new(), source: Source::Env },
754            version: None,
755            abi: None,
756            capabilities: None,
757            info: Info::default(),
758            devices: Vec::new(),
759            tokenizer: None,
760            pull: None,
761            options: Vec::new(),
762        };
763        let model = Path::new("/m.gguf");
764        let figures = r#"{"budget_bytes":34000000000,"weight_bytes":600000000,"kv_bytes_per_token":114688,"trained_context":40960}"#;
765        let sized = rt("sized", &with_model(&format!(r#"{{"path":"/m.gguf","reads":true,"sizing":{figures}}}"#)));
766        assert_eq!(
767            sizing_of(&sized, model),
768            Ok(superfluid_adapter_kit::Sizing {
769                budget_bytes: 34_000_000_000,
770                weight_bytes: 600_000_000,
771                kv_bytes_per_token: 114_688,
772                trained_context: 40_960
773            })
774        );
775        let declined = rt(
776            "declined",
777            &with_model(r#"{"path":"/m.gguf","reads":true,"sizing":"the llamacpp runtime sizes no window on Vulkan yet"}"#),
778        );
779        assert_eq!(sizing_of(&declined, model), Err("the llamacpp runtime sizes no window on Vulkan yet".to_string()));
780        let silent = rt("silent", &with_model(r#"{"path":"/m.gguf","reads":true}"#));
781        assert_eq!(sizing_of(&silent, model), Err("the llamacpp runtime sizes no context window here".to_string()));
782        let garbled = rt("garbled", &with_model(r#"{"path":"/m.gguf","reads":true,"sizing":{"budget_bytes":1}}"#));
783        assert!(sizing_of(&garbled, model).unwrap_err().contains("reported a sizing this superfluid does not read"));
784    }
785
786    #[test]
787    fn an_installed_package_is_found_checked_and_remembered() {
788        let llamacpp = RuntimeId::new("llamacpp");
789        if crate::linked::get(llamacpp).is_some() {
790            return;
791        }
792        let home = scratch("installed");
793        let w = install_fake(&home, "llamacpp", &report("llamacpp", V, "true"), 0);
794        let cat = catalog(&home);
795        let found: Vec<RuntimeId> = cat.ids().into_iter().filter(|i| crate::linked::get(*i).is_none()).collect();
796        assert_eq!(found, vec![llamacpp]);
797        let e = cat.require(llamacpp).unwrap_err();
798        assert!(e.ends_with("ships no tokenizer library (libsuperfluid_tokenizer_llamacpp.dylib beside it or in ../lib)")
799            || e.ends_with("ships no tokenizer library (libsuperfluid_tokenizer_llamacpp.so beside it or in ../lib)"), "{e}");
800        let lib_dir = home.join("runtimes/llamacpp/current/lib");
801        std::fs::create_dir_all(&lib_dir).unwrap();
802        let lib = lib_dir.join(crate::dylib_tokenizer::library_file("superfluid_tokenizer_llamacpp"));
803        std::fs::write(&lib, b"").unwrap();
804        let rt = cat.require(llamacpp).unwrap();
805        let pinned = w.canonicalize().unwrap();
806        assert_eq!(rt.worker, Worker { bin: pinned, prefix: vec![], source: Source::Installed });
807        assert_eq!(rt.version.as_deref(), Some("v1"));
808        assert_eq!(rt.info.aliases, ["llama.cpp"]);
809        assert_eq!(rt.device().unwrap().summary(), "Metal: Apple M5 Pro, 36.0 GB");
810        assert_eq!(rt.tokenizer.as_deref().map(|p| p.canonicalize().unwrap()), Some(lib.canonicalize().unwrap()));
811        assert_eq!(rt.static_capabilities().check_startup_flags(true, None, None).unwrap_err(), "--park-lossy: no lossy encoding");
812        let args = rt.serve_args(Path::new("/m.gguf"), 4096, 4, 8);
813        assert_eq!(args, ["--model", "/m.gguf", "--max-context", "4096", "--max-batch", "4"], "its record refuses --kv-bits");
814    }
815
816    #[test]
817    fn the_home_is_never_relative_to_the_working_directory() {
818        assert_eq!(home_from(Some("/srv/superfluid".into()), Some("/home/u".into())), PathBuf::from("/srv/superfluid"));
819        assert_eq!(home_from(None, Some("/home/u".into())), PathBuf::from("/home/u/.superfluid"));
820        assert_eq!(home_from(Some("".into()), Some("/home/u".into())), PathBuf::from("/home/u/.superfluid"), "an empty variable is unset");
821        assert_eq!(home_from(None, None), PathBuf::from("/.superfluid"));
822        assert_eq!(home_from(None, Some("".into())), PathBuf::from("/.superfluid"));
823        assert_eq!(home_from(None, Some(".".into())), PathBuf::from("/.superfluid"));
824    }
825
826    fn counting_worker(dir: &Path, name: &str, report: &str, tally: &Path) -> PathBuf {
827        std::fs::create_dir_all(dir).unwrap();
828        let p = dir.join(name);
829        std::fs::write(&p, format!("#!/bin/sh\necho run >> '{}'\ncat <<'EOF'\n{report}\nEOF\n", tally.display())).unwrap();
830        std::fs::set_permissions(&p, std::fs::Permissions::from_mode(0o755)).unwrap();
831        p
832    }
833
834    fn runs(tally: &Path) -> usize {
835        std::fs::read_to_string(tally).map(|t| t.lines().count()).unwrap_or(0)
836    }
837
838    #[test]
839    fn a_remembered_runtime_whose_worker_is_gone_is_looked_for_again() {
840        let home = scratch("regone");
841        let toyrt = RuntimeId::new("toyrt");
842        let tally = home.join("tally");
843        let pkg = home.join("runtimes/toyrt");
844        counting_worker(&pkg.join("b1/bin"), "superfluid-worker-toyrt", &toy_report("toyrt", "true"), &tally);
845        std::os::unix::fs::symlink("b1", pkg.join("current")).unwrap();
846        let cat = catalog(&home);
847        let first = cat.require(toyrt).unwrap();
848        assert!(cat.require(toyrt).is_ok());
849        assert_eq!(runs(&tally), 1, "a usable runtime is checked once");
850        counting_worker(&pkg.join("b2/bin"), "superfluid-worker-toyrt", &toy_report("toyrt", "true"), &tally);
851        std::fs::remove_file(pkg.join("current")).unwrap();
852        std::os::unix::fs::symlink("b2", pkg.join("current")).unwrap();
853        assert_eq!(cat.require(toyrt).unwrap().worker, first.worker);
854        assert_eq!(runs(&tally), 1);
855        std::fs::remove_dir_all(pkg.join("b1")).unwrap();
856        let again = cat.require(toyrt).unwrap();
857        assert_eq!(again.worker.bin, pkg.join("b2/bin/superfluid-worker-toyrt").canonicalize().unwrap());
858        assert_eq!(runs(&tally), 2);
859        std::fs::remove_dir_all(pkg.join("b2")).unwrap();
860        let e = cat.require(toyrt).unwrap_err();
861        assert!(e.starts_with("the toyrt runtime is not installed"), "{e}");
862    }
863
864    #[test]
865    fn a_model_asks_no_runtime_past_the_first_that_serves_it() {
866        let home = scratch("lazy");
867        let (aart, zzrt) = (RuntimeId::new("aart"), RuntimeId::new("zzrt"));
868        let (asked_a, asked_z) = (home.join("asked-a"), home.join("asked-z"));
869        counting_worker(&home.join("runtimes/aart/current/bin"), "superfluid-worker-aart", &toy_report("aart", "true"), &asked_a);
870        let reads_zz = toy_report("zzrt", "true").replace(r#""extensions":["toy"]"#, r#""extensions":["zz"]"#);
871        counting_worker(&home.join("runtimes/zzrt/current/bin"), "superfluid-worker-zzrt", &reads_zz, &asked_z);
872        let cat = catalog(&home);
873        let toy = home.join("m.toy");
874        std::fs::write(&toy, b"toy").unwrap();
875        assert_eq!(cat.reader_of(&toy).unwrap(), aart);
876        assert_eq!((runs(&asked_a), runs(&asked_z)), (1, 0), "the runtime after the reader was not run");
877        assert!(cat.require(aart).is_ok());
878        assert_eq!(runs(&asked_a), 1, "and the reader was not checked a second time");
879        let zz = home.join("m.zz");
880        std::fs::write(&zz, b"zz").unwrap();
881        assert_eq!(cat.reader_of(&zz).unwrap(), zzrt);
882        assert_eq!(runs(&asked_z), 1);
883    }
884
885    #[test]
886    fn a_pick_of_one_install_leaves_later_models_on_the_default() {
887        let home = scratch("pinned-reader");
888        let toyrt = RuntimeId::new("toyrt");
889        let pkg = home.join("runtimes/toyrt");
890        for b in ["b1", "b2"] {
891            let bin = pkg.join(b).join("bin");
892            std::fs::create_dir_all(&bin).unwrap();
893            fake_worker(&bin, "superfluid-worker-toyrt", &toy_report("toyrt", "true"), 0);
894        }
895        std::os::unix::fs::symlink("b2", pkg.join("current")).unwrap();
896        let cat = catalog(&home);
897        let pinned = cat.require(RuntimeId::parse("toyrt@b1").unwrap()).unwrap();
898        assert_eq!(pinned.worker.bin, pkg.join("b1/bin/superfluid-worker-toyrt").canonicalize().unwrap());
899        let model = home.join("m.toy");
900        std::fs::write(&model, b"toy").unwrap();
901        assert_eq!(cat.reader_of(&model).unwrap(), toyrt, "the runtime, not the install another model picked");
902        assert_eq!(cat.readers(&model).into_iter().map(|(id, _)| id).collect::<Vec<_>>(), [toyrt]);
903        let served = cat.require(toyrt).unwrap();
904        assert_eq!(served.worker.bin, pkg.join("b2/bin/superfluid-worker-toyrt").canonicalize().unwrap());
905    }
906
907    #[test]
908    fn an_adapter_a_release_ships_is_known_before_its_runtime_is_installed() {
909        for flat in [false, true] {
910            let root = scratch(if flat { "shipped-flat" } else { "shipped-sdk" });
911            let beside = if flat { root.clone() } else { root.join("bin") };
912            let libexec = root.join("libexec").join("superfluid");
913            std::fs::create_dir_all(&beside).unwrap();
914            std::fs::create_dir_all(&libexec).unwrap();
915            let not_found = r#""toyrt not found: install it with `superfluid runtime install toyrt`""#;
916            let adapter = fake_worker(&libexec, "superfluid-worker-toyrt", &toy_report("toyrt", not_found), 1).canonicalize().unwrap();
917            std::fs::write(libexec.join("libsuperfluid_tokenizer_toyrt.dylib"), b"").unwrap();
918            let home = root.join("home");
919            let catalog = || Catalog::new(home.clone(), Some(beside.clone()), beside.join("superfluid-workerd"));
920            let (toyrt, vllm) = (RuntimeId::new("toyrt"), RuntimeId::new("vllm"));
921
922            let cat = catalog();
923            assert_eq!(cat.shipped_ids(), vec![toyrt], "flat: {flat}");
924            assert!(cat.ids().contains(&toyrt));
925            assert_eq!(cat.named("toyrt").unwrap(), toyrt);
926            assert_eq!(cat.locate(toyrt).unwrap(), Worker { bin: adapter.clone(), prefix: vec![], source: Source::Shipped });
927            assert_eq!(
928                cat.require(toyrt).unwrap_err(),
929                "the toyrt runtime is not installed: toyrt not found: install it with `superfluid runtime install toyrt`"
930            );
931            let model = root.join("m.toy");
932            std::fs::write(&model, b"toy").unwrap();
933            assert_eq!(cat.reader_of(&model).unwrap(), toyrt);
934            assert_eq!(cat.reads(toyrt, &model), Ok(()));
935            assert_eq!(crate::installs::installer(&cat, toyrt).unwrap(), adapter);
936            let e = cat.require(vllm).unwrap_err();
937            assert!(e.starts_with("the vllm runtime is not here: this superfluid ships no adapter for it (it ships: toyrt)"), "{e}");
938            assert_eq!(
939                crate::installs::installer(&cat, vllm).unwrap_err(),
940                "this superfluid ships no adapter to install the vllm runtime with (it ships: toyrt)"
941            );
942
943            let installed = install_fake(&home, "toyrt", &toy_report("toyrt", "true"), 0).canonicalize().unwrap();
944            let cat = catalog();
945            assert_eq!(cat.locate(toyrt).unwrap(), Worker { bin: installed, prefix: vec![], source: Source::Installed });
946            assert!(cat.require(toyrt).is_ok());
947            assert_eq!(crate::installs::installer(&cat, toyrt).unwrap(), adapter);
948        }
949    }
950
951    #[test]
952    fn a_source_build_ships_no_adapter() {
953        let beside = scratch("no-libexec");
954        let cat = Catalog::new(scratch("no-libexec-home"), Some(beside), PathBuf::from("/nonexistent/superfluid-workerd"));
955        let vllm = RuntimeId::new("vllm");
956        assert!(cat.shipped_ids().is_empty());
957        assert_eq!(cat.shipped(vllm), None);
958        let e = cat.require(vllm).unwrap_err();
959        assert!(e.contains("cargo build -p superfluid-adapter-vllm"), "{e}");
960        let e = crate::installs::installer(&cat, vllm).unwrap_err();
961        assert!(e.starts_with("no superfluid-worker-vllm to install the vllm runtime with") && e.contains("cargo build"), "{e}");
962    }
963
964    #[test]
965    fn a_worker_beside_superfluid_wins_over_an_installed_one() {
966        let vllm = RuntimeId::new("vllm");
967        let home = scratch("beside-home");
968        let beside = scratch("beside-exe");
969        install_fake(&home, "vllm", &report("vllm", V, "true"), 0);
970        let mine = fake_worker(&beside, "superfluid-worker-vllm", &report("vllm", V, "true"), 0);
971        std::fs::write(beside.join("superfluid-worker-vllm.d"), b"").unwrap();
972        let cat = Catalog::new(home, Some(beside), PathBuf::from("/nonexistent/superfluid-workerd"));
973        assert_eq!(cat.locate(vllm).unwrap(), Worker { bin: mine, prefix: vec![], source: Source::BesideDaemon });
974        assert!(cat.ids().contains(&vllm) && !cat.ids().iter().any(|i| i.name().ends_with(".d")));
975    }
976
977    #[test]
978    fn a_runtime_this_build_links_is_served_by_this_build() {
979        let beside = scratch("linked-beside");
980        let workerd = beside.join("superfluid-workerd");
981        for id in crate::linked::ids() {
982            fake_worker(&beside, &worker_name(id), &report(id.name(), V, "true"), 0);
983            let cat = Catalog::new(scratch("linked-home"), Some(beside.clone()), workerd.clone());
984            let worker = cat.locate(id).unwrap();
985            assert_eq!(worker, Worker { bin: workerd.clone(), prefix: vec!["--engine".into(), id.name().into()], source: Source::BuiltIn });
986        }
987    }
988
989    #[test]
990    fn a_program_named_as_the_workerd_is_started_as_one() {
991        let program = scratch("embedder").join("embedding-program");
992        let cat = Catalog::with_workerd(program.clone());
993        assert_eq!(cat.workerd(), program.as_path());
994        for id in crate::linked::ids() {
995            let worker = cat.locate(id).unwrap();
996            assert_eq!(worker.bin, program);
997            // The daemon serves a model with the prefix first, and checks the
998            // runtime with `check` before it: the shapes the program must tell
999            // from its own command line.
1000            let serve: Vec<String> = worker.prefix.iter().cloned().chain(["--model".into(), "m".into()]).collect();
1001            assert!(crate::workerd::is_invocation(&serve), "{serve:?}");
1002            let check: Vec<String> = std::iter::once("check".to_string()).chain(worker.prefix.iter().cloned()).collect();
1003            assert!(crate::workerd::is_invocation(&check), "{check:?}");
1004        }
1005    }
1006
1007    #[test]
1008    fn what_is_missing_or_broken_says_why_and_how_to_fix_it() {
1009        let mlx = RuntimeId::new("mlx");
1010        if crate::linked::get(mlx).is_some() {
1011            return;
1012        }
1013        let home = scratch("broken");
1014        let cat = catalog(&home);
1015        let e = cat.require(mlx).unwrap_err();
1016        assert!(e.starts_with("the mlx runtime is not installed: no superfluid-worker-mlx in "), "{e}");
1017        assert!(e.contains("`superfluid runtime install mlx`") && e.contains("cargo build -p superfluid-adapter-mlx"), "{e}");
1018
1019        let w = install_fake(&home, "mlx", &report("mlx", V, r#""ModuleNotFoundError: No module named 'mlx'""#), 1);
1020        let e = cat.require(mlx).unwrap_err();
1021        let pinned = w.canonicalize().unwrap();
1022        assert_eq!(
1023            e,
1024            format!(
1025                "the mlx runtime at {} cannot run here: ModuleNotFoundError: No module named 'mlx' (`superfluid runtime repair mlx` reinstalls it)",
1026                pinned.display()
1027            )
1028        );
1029        let found = cat.inspect(mlx);
1030        assert_eq!(found.info.unwrap().formats[0].id, "mlx");
1031
1032        install_fake(&home, "mlx", &report("mlx", V + 1, "true"), 0);
1033        let e = cat.require(mlx).unwrap_err();
1034        assert!(e.contains(&format!("speaks Link W [{}] and this superfluid speaks {V}", V + 1)), "{e}");
1035        assert!(e.ends_with("update the one that is older"), "a newer worker: superfluid is the older one: {e}");
1036        install_fake(&home, "mlx", &report("mlx", V - 1, "true"), 0);
1037        let e = cat.require(mlx).unwrap_err();
1038        assert!(e.contains(&format!("speaks Link W [{}] and this superfluid speaks {V}", V - 1)), "{e}");
1039        assert!(e.ends_with("`superfluid runtime repair mlx` installs it again with this superfluid's worker"), "{e}");
1040        let old = home.join("runtimes/mlx/old/bin");
1041        std::fs::create_dir_all(&old).unwrap();
1042        fake_worker(&old, "superfluid-worker-mlx", &report("mlx", V - 1, "true"), 0);
1043        let e = cat.require(RuntimeId::parse("mlx@old").unwrap()).unwrap_err();
1044        assert!(e.ends_with("`superfluid runtime repair mlx old` installs it again with this superfluid's worker"), "{e}");
1045        fake_worker(&old, "superfluid-worker-mlx", &report("mlx", V, r#""no Metal device""#), 1);
1046        let e = cat.require(RuntimeId::parse("mlx@old").unwrap()).unwrap_err();
1047        assert!(e.ends_with("cannot run here: no Metal device (`superfluid runtime repair mlx old` reinstalls it)"), "{e}");
1048        let e = cat.not_here("mlx@gone", Path::new("/m"), "mlx has no install gone here");
1049        assert!(e.ends_with("`superfluid runtime install mlx` installs mlx"), "{e}");
1050
1051        install_fake(&home, "mlx", &report("llamacpp", V, "true"), 0);
1052        assert!(cat.require(mlx).unwrap_err().contains("is not the mlx runtime's worker"));
1053
1054        std::fs::write(&w, "#!/bin/sh\necho 'dyld: Library not loaded: libpython3.12.dylib' >&2\nexit 134\n").unwrap();
1055        let e = cat.require(mlx).unwrap_err();
1056        assert!(e.contains("check failed") && e.ends_with("dyld: Library not loaded: libpython3.12.dylib"), "{e}");
1057
1058        install_fake(&home, "mlx", &report("mlx", V, "true"), 0);
1059        assert_eq!(cat.require(mlx).unwrap().version.as_deref(), Some("v1"));
1060    }
1061
1062    #[test]
1063    fn a_worker_that_declares_no_formats_is_named_as_older() {
1064        let old = RuntimeId::new("oldrt");
1065        let home = scratch("old");
1066        install_fake(&home, "oldrt", &format!(r#"{{"runtime":{{"id":"oldrt","version":"v0"}},"link_w":[{V}],"available":true}}"#), 0);
1067        let cat = catalog(&home);
1068        assert!(cat.inspect(old).info.unwrap().formats.is_empty());
1069        let file = scratch("old-model").join("m.weights");
1070        std::fs::write(&file, b"????").unwrap();
1071        let e = cat.reader_of(&file).unwrap_err();
1072        assert!(e.contains("oldrt reads no format: its worker declares none, so it is older than this superfluid (rebuild or update it)"), "{e}");
1073    }
1074
1075    #[test]
1076    fn a_model_two_runtimes_read_goes_to_the_one_that_can_run() {
1077        let (broken, working) = (RuntimeId::new("abroken"), RuntimeId::new("zworking"));
1078        let toy = |id: &str, available: &str| {
1079            format!(
1080                r#"{{"runtime":{{"id":"{id}","version":"v1"}},"link_w":[{V}],"available":{available},"formats":[{{"id":"toy","describe":"a toy file","file":{{"extensions":["toy"]}}}}],"tokenizer":{{"from":"huggingface"}}}}"#
1081            )
1082        };
1083        let home = scratch("two-readers");
1084        install_fake(&home, "abroken", &toy("abroken", r#""library missing""#), 1);
1085        install_fake(&home, "zworking", &toy("zworking", "true"), 0);
1086        let file = scratch("two-readers-model").join("m.toy");
1087        std::fs::write(&file, b"toy").unwrap();
1088
1089        let cat = catalog(&home);
1090        let listed: Vec<RuntimeId> = cat.readers(&file).into_iter().map(|(id, _)| id).collect();
1091        assert_eq!(listed, vec![working, broken], "the one that cannot run stays listed, after the one that can");
1092        assert_eq!(cat.reader_of(&file).unwrap(), working, "the reader that can run, not the first by name");
1093        assert_eq!(crate::runtime_pick::RuntimePick::default().resolve(&file, &cat).unwrap(), working);
1094        assert!(cat.require(working).is_ok());
1095        let fresh = catalog(&home);
1096        let e = fresh.not_here("vllm", &file, "no runtime 'vllm' here");
1097        assert!(e.contains("which zworking here reads: pass --runtime zworking"), "{e}");
1098
1099        let alone = scratch("one-broken-reader");
1100        install_fake(&alone, "abroken", &toy("abroken", r#""library missing""#), 1);
1101        let cat = catalog(&alone);
1102        assert_eq!(cat.reader_of(&file).unwrap(), broken);
1103        let e = cat.require(broken).unwrap_err();
1104        assert!(e.contains("cannot run here: library missing"), "{e}");
1105    }
1106
1107    #[test]
1108    fn a_runtime_is_named_by_its_id_or_an_alias_it_declares() {
1109        if crate::linked::get(RuntimeId::new("llamacpp")).is_some() {
1110            return;
1111        }
1112        let home = scratch("named");
1113        install_fake(&home, "llamacpp", &report("llamacpp", V, "true"), 0);
1114        let cat = catalog(&home);
1115        assert_eq!(cat.named("llamacpp").unwrap(), RuntimeId::new("llamacpp"));
1116        assert_eq!(cat.named("llama.cpp").unwrap(), RuntimeId::new("llamacpp"), "an alias the runtime declares");
1117        assert_eq!(cat.named("vllm").unwrap_err(), "no runtime 'vllm' here");
1118    }
1119
1120    #[test]
1121    fn the_runtime_for_a_model_is_the_one_whose_format_it_is() {
1122        let (llamacpp, mlx) = (RuntimeId::new("llamacpp"), RuntimeId::new("mlx"));
1123        if crate::linked::get(llamacpp).is_some() || crate::linked::get(mlx).is_some() {
1124            return;
1125        }
1126        let home = scratch("formats");
1127        install_fake(&home, "llamacpp", &report("llamacpp", V, "true"), 0);
1128        let lib = home.join("runtimes/llamacpp/current/lib");
1129        std::fs::create_dir_all(&lib).unwrap();
1130        std::fs::write(lib.join(crate::dylib_tokenizer::library_file("superfluid_tokenizer_llamacpp")), b"").unwrap();
1131        install_fake(&home, "mlx", &report("mlx", V, "true"), 0);
1132        let cat = catalog(&home);
1133        let models = scratch("formats-models");
1134        let gguf = models.join("weights.bin");
1135        std::fs::write(&gguf, b"GGUF\x03\x00").unwrap();
1136        let dir = models.join("qwen-mlx");
1137        std::fs::create_dir_all(&dir).unwrap();
1138        std::fs::write(dir.join("config.json"), b"{}").unwrap();
1139        std::fs::write(dir.join("model.safetensors"), b"").unwrap();
1140        let notes = models.join("notes.txt");
1141        std::fs::write(&notes, b"hello").unwrap();
1142
1143        assert_eq!(cat.reader_of(&gguf).unwrap(), llamacpp, "by the magic its format declares");
1144        assert_eq!(cat.reader_of(&dir).unwrap(), mlx);
1145        let e = cat.reader_of(&notes).unwrap_err();
1146        assert!(e.starts_with(&format!("no runtime here reads {}", notes.display())), "{e}");
1147        assert!(e.contains("llamacpp reads a GGUF file; mlx reads an MLX model directory"), "{e}");
1148        assert!(e.contains("`superfluid runtime install <id>`"), "{e}");
1149
1150        let e = cat.reads(mlx, &gguf).unwrap_err();
1151        assert_eq!(
1152            e,
1153            format!(
1154                "runtime mlx cannot serve {}: it reads an MLX model directory, and this is a GGUF file (pass --runtime llamacpp, or convert the model)",
1155                gguf.display()
1156            )
1157        );
1158        let mut pick = crate::runtime_pick::RuntimePick::default();
1159        pick.add("vllm").unwrap();
1160        let e = pick.resolve(&gguf, &cat).unwrap_err();
1161        assert!(e.starts_with(&format!("no runtime 'vllm' here to serve {}", gguf.display())), "{e}");
1162        assert!(e.contains("it is a GGUF file, which llamacpp here reads: pass --runtime llamacpp"), "{e}");
1163        assert!(e.ends_with("`superfluid runtime install vllm` installs vllm"), "{e}");
1164        assert_eq!(crate::runtime_pick::RuntimePick::default().resolve(&dir, &cat).unwrap(), mlx);
1165        let mut pick = crate::runtime_pick::RuntimePick::default();
1166        pick.add("llama.cpp").unwrap();
1167        assert_eq!(pick.resolve(&gguf, &cat).unwrap(), llamacpp);
1168    }
1169}