Skip to main content

sc/embeddings/
types.rs

1//! Embedding types and configuration.
2//!
3//! Mirrors the TypeScript `EmbeddingSettings` and `EmbeddingProvider` interfaces
4//! to maintain config compatibility with `~/.savecontext/config.json`.
5
6use serde::{Deserialize, Serialize};
7
8/// Embedding provider types.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(rename_all = "lowercase")]
11pub enum EmbeddingProviderType {
12    Ollama,
13    Huggingface,
14    Transformers,
15    /// Model2Vec - fast static embeddings for 2-tier architecture
16    Model2vec,
17}
18
19impl std::fmt::Display for EmbeddingProviderType {
20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21        match self {
22            Self::Ollama => write!(f, "ollama"),
23            Self::Huggingface => write!(f, "huggingface"),
24            Self::Transformers => write!(f, "transformers"),
25            Self::Model2vec => write!(f, "model2vec"),
26        }
27    }
28}
29
30/// Embedding settings stored in `~/.savecontext/config.json`.
31///
32/// Field names match TypeScript implementation for compatibility.
33#[derive(Debug, Clone, Default, Serialize, Deserialize)]
34#[allow(non_snake_case)]
35pub struct EmbeddingSettings {
36    pub enabled: Option<bool>,
37    pub provider: Option<EmbeddingProviderType>,
38    pub HF_TOKEN: Option<String>,
39    pub HF_MODEL: Option<String>,
40    pub HF_ENDPOINT: Option<String>,
41    pub OLLAMA_ENDPOINT: Option<String>,
42    pub OLLAMA_MODEL: Option<String>,
43    pub TRANSFORMERS_MODEL: Option<String>,
44}
45
46/// SaveContext local configuration file structure.
47///
48/// Stored at `~/.savecontext/config.json`.
49#[derive(Debug, Clone, Default, Serialize, Deserialize)]
50pub struct SaveContextConfig {
51    pub embeddings: Option<EmbeddingSettings>,
52}
53
54/// Result from embedding generation.
55#[derive(Debug, Clone)]
56pub struct EmbeddingResult {
57    pub embedding: Vec<f32>,
58    pub model: String,
59    pub dimensions: usize,
60    pub provider: String,
61}
62
63/// Provider metadata returned from availability check.
64#[derive(Debug, Clone)]
65pub struct ProviderInfo {
66    pub name: String,
67    pub model: String,
68    pub dimensions: usize,
69    pub max_chars: usize,
70    pub available: bool,
71}
72
73/// Model configuration with dimensions and max chars.
74#[derive(Debug, Clone)]
75pub struct ModelConfig {
76    pub name: String,
77    pub dimensions: usize,
78    pub max_chars: usize,
79}
80
81/// Ollama model configurations.
82pub mod ollama_models {
83    use super::ModelConfig;
84
85    pub fn nomic_embed_text() -> ModelConfig {
86        ModelConfig {
87            name: "nomic-embed-text".to_string(),
88            dimensions: 768,
89            max_chars: 5000,
90        }
91    }
92
93    pub fn mxbai_embed_large() -> ModelConfig {
94        ModelConfig {
95            name: "mxbai-embed-large".to_string(),
96            dimensions: 1024,
97            max_chars: 1500,
98        }
99    }
100
101    pub fn all_minilm() -> ModelConfig {
102        ModelConfig {
103            name: "all-minilm".to_string(),
104            dimensions: 384,
105            max_chars: 800,
106        }
107    }
108
109    pub fn default_config() -> ModelConfig {
110        nomic_embed_text()
111    }
112
113    pub fn get_config(model: &str) -> ModelConfig {
114        match model {
115            "nomic-embed-text" => nomic_embed_text(),
116            "mxbai-embed-large" => mxbai_embed_large(),
117            "all-minilm" => all_minilm(),
118            _ => ModelConfig {
119                name: model.to_string(),
120                dimensions: 768, // Default assumption
121                max_chars: 5000,
122            },
123        }
124    }
125}
126
127/// HuggingFace model configurations.
128pub mod huggingface_models {
129    use super::ModelConfig;
130
131    pub fn all_minilm_l6_v2() -> ModelConfig {
132        ModelConfig {
133            name: "sentence-transformers/all-MiniLM-L6-v2".to_string(),
134            dimensions: 384,
135            max_chars: 800,
136        }
137    }
138
139    pub fn all_mpnet_base_v2() -> ModelConfig {
140        ModelConfig {
141            name: "sentence-transformers/all-mpnet-base-v2".to_string(),
142            dimensions: 768,
143            max_chars: 1500,
144        }
145    }
146
147    pub fn default_config() -> ModelConfig {
148        all_minilm_l6_v2()
149    }
150
151    pub fn get_config(model: &str) -> ModelConfig {
152        match model {
153            "sentence-transformers/all-MiniLM-L6-v2" => all_minilm_l6_v2(),
154            "sentence-transformers/all-mpnet-base-v2" => all_mpnet_base_v2(),
155            _ => ModelConfig {
156                name: model.to_string(),
157                dimensions: 384, // Default assumption
158                max_chars: 800,
159            },
160        }
161    }
162}
163
164/// Model2Vec model configurations (fast tier - static embeddings).
165pub mod model2vec_models {
166    use super::ModelConfig;
167
168    /// potion-base-8M - fast 256d embeddings
169    pub fn potion_base_8m() -> ModelConfig {
170        ModelConfig {
171            name: "minishlab/potion-base-8M".to_string(),
172            dimensions: 256,
173            max_chars: 2048, // Model2Vec handles longer contexts
174        }
175    }
176
177    /// potion-base-32M - larger 256d embeddings
178    pub fn potion_base_32m() -> ModelConfig {
179        ModelConfig {
180            name: "minishlab/potion-base-32M".to_string(),
181            dimensions: 256,
182            max_chars: 2048,
183        }
184    }
185
186    /// potion-multilingual-128M - multilingual 256d embeddings
187    pub fn potion_multilingual_128m() -> ModelConfig {
188        ModelConfig {
189            name: "minishlab/potion-multilingual-128M".to_string(),
190            dimensions: 256,
191            max_chars: 2048,
192        }
193    }
194
195    pub fn default_config() -> ModelConfig {
196        potion_base_8m()
197    }
198
199    pub fn get_config(model: &str) -> ModelConfig {
200        match model {
201            "minishlab/potion-base-8M" | "potion-base-8M" => potion_base_8m(),
202            "minishlab/potion-base-32M" | "potion-base-32M" => potion_base_32m(),
203            "minishlab/potion-multilingual-128M" | "potion-multilingual-128M" => potion_multilingual_128m(),
204            _ => ModelConfig {
205                name: model.to_string(),
206                dimensions: 256, // Model2Vec default
207                max_chars: 2048,
208            },
209        }
210    }
211}
212
213/// Search modes for tiered embedding system.
214#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
215#[serde(rename_all = "lowercase")]
216pub enum SearchMode {
217    /// Fast only - instant results using Model2Vec, lower quality
218    Fast,
219    /// Quality only - slower, uses Ollama/HuggingFace, better accuracy
220    Quality,
221    /// Tiered (default) - fast candidates, quality re-ranking
222    #[default]
223    Tiered,
224}
225
226impl std::fmt::Display for SearchMode {
227    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
228        match self {
229            Self::Fast => write!(f, "fast"),
230            Self::Quality => write!(f, "quality"),
231            Self::Tiered => write!(f, "tiered"),
232        }
233    }
234}
235
236impl std::str::FromStr for SearchMode {
237    type Err = String;
238
239    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
240        match s.to_lowercase().as_str() {
241            "fast" => Ok(Self::Fast),
242            "quality" => Ok(Self::Quality),
243            "tiered" => Ok(Self::Tiered),
244            _ => Err(format!("Unknown search mode: {s}")),
245        }
246    }
247}
248
249/// Tiered embedding settings for 2-tier architecture.
250#[derive(Debug, Clone, Default, Serialize, Deserialize)]
251pub struct TieredEmbeddingSettings {
252    /// Enable tiered embeddings
253    pub enabled: Option<bool>,
254    /// Fast tier provider (default: model2vec)
255    pub fast_provider: Option<EmbeddingProviderType>,
256    /// Fast tier model (default: minishlab/potion-base-8M)
257    pub fast_model: Option<String>,
258    /// Quality tier provider (default: ollama)
259    pub quality_provider: Option<EmbeddingProviderType>,
260    /// Quality tier model (default: nomic-embed-text)
261    pub quality_model: Option<String>,
262    /// Default search mode
263    pub search_mode: Option<SearchMode>,
264}