Skip to main content

microscope_memory/
config.rs

1use serde::{Deserialize, Serialize};
2use std::fs;
3use std::path::Path;
4
5#[derive(Debug, Serialize, Deserialize, Clone)]
6pub struct Config {
7    pub paths: Paths,
8    pub index: Index,
9    pub search: Search,
10    pub memory_layers: MemoryLayers,
11    pub performance: Performance,
12    pub logging: Logging,
13    #[serde(default)]
14    pub embedding: Embedding,
15    #[serde(default)]
16    pub server: Server,
17    #[serde(default)]
18    pub federation: Federation,
19}
20
21#[derive(Debug, Serialize, Deserialize, Clone)]
22pub struct Paths {
23    pub layers_dir: String,
24    pub output_dir: String,
25    pub temp_dir: String,
26}
27
28#[derive(Debug, Serialize, Deserialize, Clone)]
29pub struct Index {
30    pub block_size: usize,
31    pub max_depth: u8,
32    pub header_size: usize,
33}
34
35#[derive(Debug, Serialize, Deserialize, Clone)]
36pub struct Search {
37    pub default_k: usize,
38    pub zoom_weight: f32,
39    pub keyword_boost: f32,
40    #[serde(default)]
41    pub semantic_weight: f32,
42    #[serde(default)]
43    pub emotional_bias_weight: f32,
44}
45
46#[derive(Debug, Serialize, Deserialize, Clone)]
47pub struct MemoryLayers {
48    pub layers: Vec<String>,
49}
50
51#[derive(Debug, Serialize, Deserialize, Clone)]
52pub struct Performance {
53    pub use_mmap: bool,
54    pub cache_size: usize,
55    pub build_workers: usize,
56    #[serde(default)]
57    pub use_gpu: bool,
58    #[serde(default)]
59    pub compression: bool,
60    #[serde(default = "default_cache_ttl")]
61    pub cache_ttl_secs: u64,
62}
63
64fn default_cache_ttl() -> u64 {
65    300
66}
67
68#[derive(Debug, Serialize, Deserialize, Clone)]
69pub struct Embedding {
70    #[serde(default = "default_provider")]
71    pub provider: String,
72    #[serde(default = "default_model")]
73    pub model: String,
74    #[serde(default = "default_dim")]
75    pub dim: usize,
76    #[serde(default = "default_max_depth")]
77    pub max_depth: u8,
78    #[serde(default)]
79    pub onnx_model_path: Option<String>,
80    #[serde(default)]
81    pub tokenizer_path: Option<String>,
82}
83
84fn default_provider() -> String {
85    "mock".to_string()
86}
87fn default_model() -> String {
88    "sentence-transformers/all-MiniLM-L6-v2".to_string()
89}
90fn default_dim() -> usize {
91    384
92}
93fn default_max_depth() -> u8 {
94    4
95}
96
97impl Default for Embedding {
98    fn default() -> Self {
99        Self {
100            provider: default_provider(),
101            model: default_model(),
102            dim: default_dim(),
103            max_depth: default_max_depth(),
104            onnx_model_path: None,
105            tokenizer_path: None,
106        }
107    }
108}
109
110#[derive(Debug, Serialize, Deserialize, Clone)]
111pub struct Server {
112    #[serde(default = "default_port")]
113    pub port: u16,
114    #[serde(default)]
115    pub cors_origin: Option<String>,
116}
117
118fn default_port() -> u16 {
119    6060
120}
121
122impl Default for Server {
123    fn default() -> Self {
124        Self {
125            port: default_port(),
126            cors_origin: None,
127        }
128    }
129}
130
131#[derive(Debug, Serialize, Deserialize, Clone)]
132pub struct Logging {
133    pub level: String,
134    pub file: Option<String>,
135}
136
137#[derive(Debug, Serialize, Deserialize, Clone, Default)]
138pub struct Federation {
139    #[serde(default)]
140    pub indices: Vec<FederatedIndex>,
141}
142
143#[derive(Debug, Serialize, Deserialize, Clone)]
144pub struct FederatedIndex {
145    pub name: String,
146    pub config_path: String,
147    #[serde(default = "default_weight")]
148    pub weight: f32,
149}
150
151fn default_weight() -> f32 {
152    1.0
153}
154
155impl Config {
156    /// Zero-JSON/Zero-TOML loader: reads config.bin via mmap or returns Default.
157    pub fn load<P: AsRef<Path>>(path: P) -> Result<Self, Box<dyn std::error::Error>> {
158        if !path.as_ref().exists() {
159            return Ok(Self::default());
160        }
161
162        let file = fs::File::open(path)?;
163        // Safety: config.bin is read-only and mapped for the duration of this call
164        let mmap = unsafe { memmap2::Mmap::map(&file)? };
165
166        let config: Config = bincode::deserialize(&mmap)?;
167        Ok(config)
168    }
169
170    /// Saves the current config to a binary mmap-ready format.
171    pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<(), Box<dyn std::error::Error>> {
172        let bytes = bincode::serialize(self)?;
173        fs::write(path, bytes)?;
174        Ok(())
175    }
176}
177
178impl Default for Config {
179    fn default() -> Self {
180        Self {
181            paths: Paths {
182                layers_dir: "./layers".to_string(),
183                output_dir: "./data".to_string(),
184                temp_dir: "./tmp".to_string(),
185            },
186            index: Index {
187                block_size: 256,
188                max_depth: 8,
189                header_size: 32,
190            },
191            search: Search {
192                default_k: 10,
193                zoom_weight: 2.0,
194                keyword_boost: 0.1,
195                semantic_weight: 0.0,
196                emotional_bias_weight: 0.0,
197            },
198            memory_layers: MemoryLayers {
199                layers: vec![
200                    "long_term".to_string(),
201                    "short_term".to_string(),
202                    "associative".to_string(),
203                    "echo_cache".to_string(),
204                ],
205            },
206            performance: Performance {
207                use_mmap: true,
208                cache_size: 64,
209                build_workers: 4,
210                use_gpu: false,
211                compression: false,
212                cache_ttl_secs: default_cache_ttl(),
213            },
214            logging: Logging {
215                level: "info".to_string(),
216                file: Some("microscope.log".to_string()),
217            },
218            embedding: Embedding::default(),
219            server: Server::default(),
220            federation: Federation::default(),
221        }
222    }
223}