Skip to main content

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}