Skip to main content

kernel/resolution/
format.rs

1//! Format and capability facts derived from a model's files.
2
3use serde::{Deserialize, Serialize};
4
5use crate::records::{Capability, ExecutionMode, Modality};
6
7/// A recognized model format — the on-disk weight formats plus the logical
8/// sources (a diffusers pipeline directory, an Ollama store entry, a built-in or
9/// remote-endpoint model), and `Unknown` for anything unrecognized.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11#[serde(rename_all = "kebab-case")]
12pub enum ModelFormat {
13    /// A GGUF weight file (llama.cpp).
14    Gguf,
15    /// A legacy GGML `.bin` weight file.
16    GgmlBin,
17    /// A safetensors weight directory.
18    Safetensors,
19    /// An MLX-format safetensors weight directory.
20    MlxSafetensors,
21    /// A diffusers pipeline directory (a `model_index.json`).
22    Diffusers,
23    /// An entry in a local Ollama store.
24    OllamaStore,
25    /// A built-in (platform-provided) model.
26    Builtin,
27    /// A remote inference endpoint.
28    Endpoint,
29    /// An unrecognized format.
30    Unknown,
31}
32
33/// The modality, capabilities, and execution shape implied by a GGUF
34/// architecture.
35#[derive(Debug, Clone, PartialEq)]
36pub struct GgufArchitectureProfile {
37    /// The model's primary modality.
38    pub modality: Modality,
39    /// What the model can do.
40    pub capabilities: Vec<Capability>,
41    /// How its runtime delivers output.
42    pub execution: ExecutionMode,
43}
44
45/// Facts read from a GGUF header.
46#[derive(Debug, Clone, PartialEq)]
47pub struct GgufFacts {
48    /// The `general.architecture` value, if present.
49    pub architecture: Option<String>,
50    /// The resolved context length, if the header declared one.
51    pub context_length: Option<i64>,
52    /// Whether the header carries a chat template.
53    pub has_chat_template: bool,
54}
55
56/// The known-architecture profile for a GGUF `general.architecture` value.
57pub fn gguf_architecture_profile(architecture: &str) -> Option<GgufArchitectureProfile> {
58    let profile = |modality, capabilities, execution| GgufArchitectureProfile {
59        modality,
60        capabilities,
61        execution,
62    };
63    match architecture {
64        "whisper" => Some(profile(
65            Modality::audio(),
66            vec![Capability::transcribe()],
67            ExecutionMode::Stream,
68        )),
69        "qwen2vl" | "mllama" => Some(profile(
70            Modality::text(),
71            vec![
72                Capability::chat(),
73                Capability::complete(),
74                Capability::see(),
75            ],
76            ExecutionMode::Stream,
77        )),
78        "clip" => Some(profile(Modality::vision(), vec![], ExecutionMode::Sync)),
79        "bert" | "nomic-bert" => Some(profile(
80            Modality::embedding(),
81            vec![Capability::embed()],
82            ExecutionMode::Stream,
83        )),
84        _ => None,
85    }
86}
87
88/// The default profile for an Ollama chat model with no more specific match.
89pub fn ollama_chat_profile() -> GgufArchitectureProfile {
90    GgufArchitectureProfile {
91        modality: Modality::text(),
92        capabilities: vec![Capability::chat(), Capability::complete()],
93        execution: ExecutionMode::Stream,
94    }
95}
96
97/// The default profile for an Ollama vision-capable chat model.
98pub fn ollama_vision_profile() -> GgufArchitectureProfile {
99    GgufArchitectureProfile {
100        modality: Modality::text(),
101        capabilities: vec![
102            Capability::chat(),
103            Capability::complete(),
104            Capability::see(),
105        ],
106        execution: ExecutionMode::Stream,
107    }
108}
109
110/// The profile for an Ollama model: vision when it ships a projector, otherwise
111/// the GGUF architecture's profile (read from `weight_path`) if recognized, else
112/// the plain chat default.
113///
114/// `weight_path` must be absolute — a leading `~` is not expanded (the Ollama
115/// scanner always passes a resolved blob path;
116/// a `~`-relative path would fail to open and fall through to the chat default).
117pub fn ollama_profile(has_projector: bool, weight_path: Option<&str>) -> GgufArchitectureProfile {
118    if has_projector {
119        return ollama_vision_profile();
120    }
121    if let Some(path) = weight_path
122        && let Some(architecture) =
123            crate::resolution::gguf::gguf_general_architecture(std::path::Path::new(path))
124        && let Some(profile) = gguf_architecture_profile(&architecture)
125    {
126        return profile;
127    }
128    ollama_chat_profile()
129}