1use crate::types::ProjectId;
2use serde::{Deserialize, Serialize};
3use std::fs;
4use std::path::Path;
5
6#[derive(Debug, Serialize, Deserialize, Clone)]
7pub struct HooksConfig {
8 pub read_only: bool,
9 pub write_enabled: bool,
10}
11
12impl Default for HooksConfig {
13 fn default() -> Self {
14 Self {
15 read_only: true,
16 write_enabled: false,
17 }
18 }
19}
20
21#[derive(Debug, Serialize, Deserialize, Clone)]
22pub struct Config {
23 pub paths: Paths,
24 pub index: Index,
25 pub search: Search,
26 pub memory_layers: MemoryLayers,
27 pub performance: Performance,
28 pub logging: Logging,
29 #[serde(default)]
30 pub embedding: Embedding,
31 #[serde(default)]
32 pub server: Server,
33 #[serde(default)]
34 pub federation: Federation,
35 #[serde(default)]
36 pub hooks: HooksConfig,
37 #[serde(default = "default_project_id")]
38 pub project_id: ProjectId,
39 #[serde(default)]
40 pub epistemic: EpistemicConfig,
41}
42
43#[derive(Debug, Serialize, Deserialize, Clone)]
44pub struct Paths {
45 pub layers_dir: String,
46 pub output_dir: String,
47 pub temp_dir: String,
48}
49
50#[derive(Debug, Serialize, Deserialize, Clone)]
51pub struct Index {
52 pub max_depth: u8,
53 pub header_size: usize,
54 #[serde(default = "default_auto_rebuild")]
55 pub auto_rebuild: bool,
56 #[serde(default = "default_auto_rebuild_entries")]
57 pub auto_rebuild_entries: usize,
58 #[serde(default = "default_layer_retention_entries")]
60 pub layer_retention_entries: usize,
61 #[serde(default = "default_max_blocks")]
64 pub max_blocks: usize,
65 #[serde(default = "default_protect_min_importance")]
67 pub protect_min_importance: u8,
68 #[serde(default = "default_promote_energy_threshold")]
70 pub promote_energy_threshold: f32,
71}
72
73fn default_auto_rebuild() -> bool {
74 true
75}
76
77fn default_layer_retention_entries() -> usize {
78 2000
79}
80
81fn default_max_blocks() -> usize {
82 0
83}
84
85fn default_protect_min_importance() -> u8 {
86 8
87}
88
89fn default_promote_energy_threshold() -> f32 {
90 0.35
91}
92
93fn default_auto_rebuild_entries() -> usize {
94 25
95}
96
97#[derive(Debug, Serialize, Deserialize, Clone)]
98pub struct Search {
99 pub default_k: usize,
100 pub zoom_weight: f32,
101 pub keyword_boost: f32,
102 #[serde(default)]
103 pub semantic_weight: f32,
104 #[serde(default = "default_semantic_rank_gain")]
117 pub semantic_rank_gain: f32,
118 #[serde(default)]
119 pub emotional_bias_weight: f32,
120 #[serde(default)]
121 pub emotion_21d_weight: f32,
122}
123
124#[derive(Debug, Serialize, Deserialize, Clone)]
125pub struct MemoryLayers {
126 pub layers: Vec<String>,
127}
128
129#[derive(Debug, Serialize, Deserialize, Clone)]
130pub struct Performance {
131 pub use_mmap: bool,
132 pub cache_size: usize,
133 pub build_workers: usize,
134 #[serde(default)]
135 pub use_gpu: bool,
136 #[serde(default)]
137 pub compression: bool,
138 #[serde(default = "default_cache_ttl")]
139 pub cache_ttl_secs: u64,
140}
141
142fn default_cache_ttl() -> u64 {
143 300
144}
145
146#[derive(Debug, Serialize, Deserialize, Clone)]
147pub struct Embedding {
148 #[serde(default = "default_provider")]
149 pub provider: String,
150 #[serde(default = "default_model")]
151 pub model: String,
152 #[serde(default = "default_dim")]
153 pub dim: usize,
154 #[serde(default = "default_max_depth")]
155 pub max_depth: u8,
156 #[serde(default)]
157 pub onnx_model_path: Option<String>,
158 #[serde(default)]
159 pub tokenizer_path: Option<String>,
160 #[serde(default)]
161 pub use_gpu: bool,
162}
163
164fn default_provider() -> String {
165 "mock".to_string()
166}
167fn default_model() -> String {
168 "sentence-transformers/all-MiniLM-L6-v2".to_string()
169}
170fn default_dim() -> usize {
171 384
172}
173fn default_max_depth() -> u8 {
174 5
178}
179fn default_semantic_rank_gain() -> f32 {
180 2.0
184}
185
186impl Default for Embedding {
187 fn default() -> Self {
188 Self {
189 provider: default_provider(),
190 model: default_model(),
191 dim: default_dim(),
192 max_depth: default_max_depth(),
193 onnx_model_path: None,
194 tokenizer_path: None,
195 use_gpu: false,
196 }
197 }
198}
199
200#[derive(Debug, Serialize, Deserialize, Clone)]
201pub struct Server {
202 #[serde(default = "default_port")]
203 pub port: u16,
204 #[serde(default)]
205 pub cors_origin: Option<String>,
206 #[serde(default)]
207 pub openai_api_key: Option<String>,
208 #[serde(default)]
209 pub gemini_api_key: Option<String>,
210 #[serde(default)]
214 pub api_key: Option<String>,
215}
216
217fn default_port() -> u16 {
218 6060
219}
220
221impl Default for Server {
222 fn default() -> Self {
223 Self {
224 port: default_port(),
225 cors_origin: None,
226 openai_api_key: None,
227 gemini_api_key: None,
228 api_key: None,
229 }
230 }
231}
232
233#[derive(Debug, Serialize, Deserialize, Clone)]
234pub struct Logging {
235 pub level: String,
236 pub file: Option<String>,
237}
238
239#[derive(Debug, Serialize, Deserialize, Clone, Default)]
240pub struct Federation {
241 #[serde(default)]
242 pub indices: Vec<FederatedIndex>,
243}
244
245#[derive(Debug, Serialize, Deserialize, Clone, Default)]
246pub struct EpistemicConfig {
247 #[serde(default = "default_true")]
249 pub gate_promotion: bool,
250 #[serde(default = "default_min_sources")]
252 pub min_independent_sources: u32,
253 #[serde(default = "default_sim_threshold")]
255 pub sim_threshold: f32,
256 #[serde(default = "default_search_k")]
258 pub search_k: usize,
259 #[serde(default)]
261 pub confidence_weights: ConfidenceWeights,
262 #[serde(default)]
264 pub eviction_evidence_bias: f32,
265}
266
267fn default_true() -> bool {
268 true
269}
270fn default_min_sources() -> u32 {
271 1
272}
273fn default_sim_threshold() -> f32 {
274 0.85
275}
276fn default_search_k() -> usize {
277 8
278}
279
280#[derive(Debug, Serialize, Deserialize, Clone)]
281pub struct ConfidenceWeights {
282 #[serde(default = "default_obs_w")]
283 pub observation_w: i32,
284 #[serde(default = "default_src_w")]
285 pub source_w: i32,
286 #[serde(default = "default_refute_w")]
287 pub refute_w: i32,
288 #[serde(default = "default_age_pen")]
289 pub age_penalty: i32,
290}
291
292impl Default for ConfidenceWeights {
293 fn default() -> Self {
294 Self {
295 observation_w: 30,
296 source_w: 18,
297 refute_w: 25,
298 age_penalty: 5,
299 }
300 }
301}
302
303fn default_obs_w() -> i32 {
304 30
305}
306fn default_src_w() -> i32 {
307 18
308}
309fn default_refute_w() -> i32 {
310 25
311}
312fn default_age_pen() -> i32 {
313 5
314}
315
316#[derive(Debug, Serialize, Deserialize, Clone)]
317pub struct FederatedIndex {
318 pub name: String,
319 pub config_path: String,
320 #[serde(default = "default_weight")]
321 pub weight: f32,
322}
323
324fn default_weight() -> f32 {
325 1.0
326}
327
328fn default_project_id() -> ProjectId {
329 ProjectId::GLOBAL
330}
331
332impl Config {
333 pub fn load<P: AsRef<Path>>(path: P) -> Result<Self, Box<dyn std::error::Error>> {
335 if !path.as_ref().exists() {
336 return Ok(Self::default());
337 }
338
339 let content = fs::read_to_string(path)?;
340 let config: Config = toml::from_str(&content)?;
341 Ok(config)
342 }
343
344 pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<(), Box<dyn std::error::Error>> {
346 let content = toml::to_string_pretty(self)?;
347 let tmp_path = path.as_ref().with_extension("toml.tmp");
348 fs::write(&tmp_path, &content)?;
349 fs::rename(&tmp_path, path.as_ref())?;
350 Ok(())
351 }
352}
353
354impl Default for Config {
355 fn default() -> Self {
356 Self {
357 paths: Paths {
358 layers_dir: "./layers".to_string(),
359 output_dir: "./output".to_string(),
360 temp_dir: "./tmp".to_string(),
361 },
362 index: Index {
363 max_depth: 8,
364 header_size: 50,
365 auto_rebuild: default_auto_rebuild(),
366 auto_rebuild_entries: default_auto_rebuild_entries(),
367 layer_retention_entries: default_layer_retention_entries(),
368 max_blocks: default_max_blocks(),
369 protect_min_importance: default_protect_min_importance(),
370 promote_energy_threshold: default_promote_energy_threshold(),
371 },
372 search: Search {
373 default_k: 10,
374 zoom_weight: 3.0, keyword_boost: 0.4, semantic_weight: 0.0,
377 semantic_rank_gain: default_semantic_rank_gain(),
378 emotional_bias_weight: 0.0,
379 emotion_21d_weight: 0.0,
380 },
381 memory_layers: MemoryLayers {
382 layers: vec![
383 "long_term".to_string(),
384 "short_term".to_string(),
385 "session".to_string(),
386 "associative".to_string(),
387 "echo_cache".to_string(),
388 ],
389 },
390 performance: Performance {
391 use_mmap: true,
392 cache_size: 64,
393 build_workers: 4,
394 use_gpu: false,
395 compression: false,
396 cache_ttl_secs: default_cache_ttl(),
397 },
398 logging: Logging {
399 level: "info".to_string(),
400 file: Some("microscope.log".to_string()),
401 },
402 embedding: Embedding::default(),
403 server: Server::default(),
404 federation: Federation::default(),
405 hooks: HooksConfig::default(),
406 project_id: ProjectId::GLOBAL,
407 epistemic: EpistemicConfig::default(),
408 }
409 }
410}