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