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 /// The weight type `general.file_type` names, as llama.cpp spells it
55 /// (`Q4_K_M`, `Q8_0`, `F16`), when the header carries a known one.
56 pub quantization: Option<String>,
57}
58
59/// The known-architecture profile for a GGUF `general.architecture` value.
60///
61/// The table names the architectures that are *not* a text chat model, which is
62/// what an architecture missing from it is taken to be: the great majority of
63/// them are, and a name nobody has taught this table is far more likely to be
64/// another text model than a component. What is listed here is the exceptions:
65/// the encoders that only embed, the models that can also see, and the pieces
66/// that are half of a pipeline and serve nothing on their own.
67pub fn gguf_architecture_profile(architecture: &str) -> Option<GgufArchitectureProfile> {
68 let profile = |modality, capabilities, execution| GgufArchitectureProfile {
69 modality,
70 capabilities,
71 execution,
72 };
73 let embedding = || {
74 Some(profile(
75 Modality::embedding(),
76 vec![Capability::embed()],
77 ExecutionMode::Stream,
78 ))
79 };
80 let sees = || {
81 Some(profile(
82 Modality::text(),
83 vec![
84 Capability::chat(),
85 Capability::complete(),
86 Capability::see(),
87 ],
88 ExecutionMode::Stream,
89 ))
90 };
91 // A piece of a pipeline: it has a modality but nothing can be asked of it
92 // directly, so no runtime offers to serve it and it is never mistaken for a
93 // model that answers.
94 let component = |modality| Some(profile(modality, vec![], ExecutionMode::Sync));
95 match architecture {
96 "whisper" => Some(profile(
97 Modality::audio(),
98 vec![Capability::transcribe()],
99 ExecutionMode::Stream,
100 )),
101 "qwen2vl" | "qwen3vl" | "qwen3vlmoe" | "mllama" | "cogvlm" | "hunyuan-vl" => sees(),
102 "bert" | "nomic-bert" | "nomic-bert-moe" | "jina-bert-v2" | "jina-bert-v3" | "neo-bert"
103 | "modern-bert" | "eurobert" | "gemma-embedding" | "llama-embed" | "t5encoder" => {
104 embedding()
105 }
106 "clip" => component(Modality::vision()),
107 // The vocoder half of a text-to-speech pair: it turns another model's
108 // tokens into audio and cannot be prompted.
109 "wavtokenizer-dec" => component(Modality::audio()),
110 _ => None,
111 }
112}
113
114/// The default profile for an Ollama chat model with no more specific match.
115pub fn ollama_chat_profile() -> GgufArchitectureProfile {
116 GgufArchitectureProfile {
117 modality: Modality::text(),
118 capabilities: vec![Capability::chat(), Capability::complete()],
119 execution: ExecutionMode::Stream,
120 }
121}
122
123/// The default profile for an Ollama vision-capable chat model.
124pub fn ollama_vision_profile() -> GgufArchitectureProfile {
125 GgufArchitectureProfile {
126 modality: Modality::text(),
127 capabilities: vec![
128 Capability::chat(),
129 Capability::complete(),
130 Capability::see(),
131 ],
132 execution: ExecutionMode::Stream,
133 }
134}
135
136/// The profile for an Ollama model: vision when it ships a projector, otherwise
137/// the profile of `architecture`, the one its weight blob's GGUF header names,
138/// if recognized, else the plain chat default.
139pub fn ollama_profile(has_projector: bool, architecture: Option<&str>) -> GgufArchitectureProfile {
140 if has_projector {
141 return ollama_vision_profile();
142 }
143 architecture
144 .and_then(gguf_architecture_profile)
145 .unwrap_or_else(ollama_chat_profile)
146}