kernel/discovery/scanner.rs
1//! The shared discovery surface: a [`StoreScanner`] inspects one on-disk model
2//! store (an Ollama blob store, a Hugging Face cache, …) and returns the models
3//! it found as [`DiscoveredModel`] hints, plus any per-store issues, in a
4//! [`ScanResult`]. Identification later turns these hints into full records.
5
6use crate::records::{Capability, ExecutionMode, Modality, ModelSource, SourceKind};
7
8/// A model a scanner found on disk, as hints (not yet a resolved record). The
9/// `*_hint` fields are best-effort; identification refines or overrides them.
10#[derive(Debug, Clone, PartialEq)]
11pub struct DiscoveredModel {
12 /// The display name (e.g. `llama3.2:latest`).
13 pub name: String,
14 /// Where the weights live and what kind of store they came from.
15 pub source: ModelSource,
16 /// The guessed modality, if the store hinted one.
17 pub modality_hint: Option<Modality>,
18 /// The guessed capabilities.
19 pub capabilities_hint: Vec<Capability>,
20 /// How the model executes (streaming vs one-shot job).
21 pub execution_hint: ExecutionMode,
22 /// The on-disk footprint in bytes.
23 pub footprint_bytes: i64,
24 /// The primary weight file, if identified.
25 pub primary_weight_path: Option<String>,
26 /// Free-text notes about this specific model.
27 pub diagnostics: Vec<String>,
28 /// A context-window hint, if the store recorded one.
29 pub context_length_hint: Option<i64>,
30 /// Whether the store recorded a chat template.
31 pub has_chat_template_hint: Option<bool>,
32 /// Stop tokens the store recorded.
33 pub stop_tokens_hint: Option<Vec<String>>,
34 /// Whether the model is still downloading (weights incomplete).
35 pub downloading: bool,
36}
37
38impl DiscoveredModel {
39 /// A discovered model with just a name and source; hints default to empty.
40 pub fn new(name: impl Into<String>, source: ModelSource) -> Self {
41 Self {
42 name: name.into(),
43 source,
44 modality_hint: None,
45 capabilities_hint: Vec::new(),
46 execution_hint: ExecutionMode::default(),
47 footprint_bytes: 0,
48 primary_weight_path: None,
49 diagnostics: Vec::new(),
50 context_length_hint: None,
51 has_chat_template_hint: None,
52 stop_tokens_hint: None,
53 downloading: false,
54 }
55 }
56}
57
58/// The outcome of scanning one or more stores: the models found, free-text
59/// issues (a store was reachable but a single entry was malformed), and the set
60/// of source kinds whose scan failed wholesale (unreadable root).
61#[derive(Debug, Clone, Default, PartialEq)]
62pub struct ScanResult {
63 /// The models found across the scanned stores.
64 pub discovered: Vec<DiscoveredModel>,
65 /// Non-fatal issues (one bad entry, an unreadable sidecar) worth surfacing.
66 pub issues: Vec<String>,
67 /// Source kinds whose scan failed entirely (e.g. an unreadable root).
68 pub failed_kinds: Vec<SourceKind>,
69}
70
71/// A scanner over one class of on-disk model store. Synchronous: the kernel does
72/// its filesystem work inline (the runtime layer decides about threads).
73pub trait StoreScanner {
74 /// The source kinds this scanner produces (used to mark wholesale failures).
75 fn kinds(&self) -> Vec<SourceKind>;
76
77 /// Scan the store, returning everything found plus any issues.
78 fn scan(&self) -> ScanResult;
79}