Skip to main content

microscope_memory/
explicit_memory.rs

1//! Explicit memory — declarative knowledge (facts, events, concepts)
2//! Facts, events, concepts that can be consciously recalled and reported
3
4use std::collections::HashMap;
5use std::fs;
6use std::path::Path;
7
8#[derive(Clone, Debug)]
9pub struct Fact {
10    pub statement: String,
11    pub confidence: f32, // 0.0-1.0
12    pub source: String,
13    pub timestamp_ms: u64,
14    pub access_count: u32,
15    pub verified: bool,
16}
17
18#[derive(Clone, Debug)]
19pub struct Concept {
20    pub name: String,
21    pub definition: String,
22    pub examples: Vec<String>,
23    pub related_concepts: Vec<String>,
24    pub abstraction_level: f32, // 0.0 = concrete, 1.0 = abstract
25}
26
27#[derive(Clone, Debug)]
28pub struct Event {
29    pub description: String,
30    pub timestamp_ms: u64,
31    pub participants: Vec<String>,
32    pub location: String,
33    pub emotional_significance: f32,
34    pub detail_level: f32, // 0.0 = vague, 1.0 = detailed
35}
36
37pub struct ExplicitMemory {
38    pub facts: HashMap<String, Fact>,
39    pub concepts: HashMap<String, Concept>,
40    pub events: Vec<Event>,
41    pub relationships: HashMap<String, Vec<String>>, // concept -> related concepts
42}
43
44impl Default for ExplicitMemory {
45    fn default() -> Self {
46        Self::new()
47    }
48}
49
50impl ExplicitMemory {
51    pub fn new() -> Self {
52        Self {
53            facts: HashMap::new(),
54            concepts: HashMap::new(),
55            events: Vec::new(),
56            relationships: HashMap::new(),
57        }
58    }
59
60    /// Store a declarative fact
61    pub fn store_fact(&mut self, statement: &str, source: &str, confidence: f32) {
62        let key = statement.to_lowercase();
63        self.facts
64            .entry(key.clone())
65            .and_modify(|f| {
66                f.access_count += 1;
67                f.confidence = (f.confidence + confidence) / 2.0;
68            })
69            .or_insert_with(|| Fact {
70                statement: statement.to_string(),
71                confidence: confidence.clamp(0.0, 1.0),
72                source: source.to_string(),
73                timestamp_ms: Self::now_ms(),
74                access_count: 1,
75                verified: confidence > 0.8,
76            });
77    }
78
79    /// Define a concept
80    pub fn define_concept(&mut self, name: &str, definition: &str, abstraction: f32) {
81        self.concepts.insert(
82            name.to_lowercase(),
83            Concept {
84                name: name.to_string(),
85                definition: definition.to_string(),
86                examples: Vec::new(),
87                related_concepts: Vec::new(),
88                abstraction_level: abstraction.clamp(0.0, 1.0),
89            },
90        );
91    }
92
93    /// Add example to concept
94    pub fn add_example(&mut self, concept: &str, example: &str) {
95        if let Some(c) = self.concepts.get_mut(&concept.to_lowercase()) {
96            c.examples.push(example.to_string());
97        }
98    }
99
100    /// Record an event
101    pub fn record_event(&mut self, description: &str, location: &str, emotional_sig: f32) {
102        self.events.push(Event {
103            description: description.to_string(),
104            timestamp_ms: Self::now_ms(),
105            participants: Vec::new(),
106            location: location.to_string(),
107            emotional_significance: emotional_sig.clamp(0.0, 1.0),
108            detail_level: 0.5,
109        });
110    }
111
112    /// Link concepts
113    pub fn relate_concepts(&mut self, concept1: &str, concept2: &str) {
114        let key1 = concept1.to_lowercase();
115        let key2 = concept2.to_lowercase();
116
117        self.relationships
118            .entry(key1.clone())
119            .or_default()
120            .push(key2.clone());
121
122        self.relationships.entry(key2).or_default().push(key1);
123    }
124
125    /// Retrieve fact with confidence
126    pub fn recall_fact(&mut self, statement: &str) -> Option<(String, f32)> {
127        let key = statement.to_lowercase();
128        if let Some(fact) = self.facts.get_mut(&key) {
129            fact.access_count += 1;
130            return Some((fact.statement.clone(), fact.confidence));
131        }
132        None
133    }
134
135    /// Get concept definition
136    pub fn get_concept(&self, name: &str) -> Option<&Concept> {
137        self.concepts.get(&name.to_lowercase())
138    }
139
140    /// Get all facts sorted by confidence
141    pub fn high_confidence_facts(&self, min_confidence: f32) -> Vec<&Fact> {
142        let mut facts: Vec<_> = self
143            .facts
144            .values()
145            .filter(|f| f.confidence >= min_confidence)
146            .collect();
147        facts.sort_by(|a, b| b.confidence.total_cmp(&a.confidence));
148        facts
149    }
150
151    /// Get recent events
152    pub fn recent_events(&self, count: usize) -> Vec<&Event> {
153        let mut events = self.events.iter().collect::<Vec<_>>();
154        events.sort_by_key(|b| std::cmp::Reverse(b.timestamp_ms));
155        events.into_iter().take(count).collect()
156    }
157
158    pub fn save(&self, dir: &Path) -> Result<(), String> {
159        let path = dir.join("explicit_memory.bin");
160        let mut data = Vec::new();
161
162        data.extend_from_slice(b"EXPL");
163        data.push(1);
164
165        // Facts
166        let fact_count = self.facts.len() as u32;
167        data.extend_from_slice(&fact_count.to_le_bytes());
168        for fact in self.facts.values() {
169            let stmt_bytes = fact.statement.as_bytes();
170            data.extend_from_slice(&(stmt_bytes.len() as u16).to_le_bytes());
171            data.extend_from_slice(stmt_bytes);
172            data.extend_from_slice(&fact.confidence.to_le_bytes());
173            let src_bytes = fact.source.as_bytes();
174            data.push(src_bytes.len() as u8);
175            data.extend_from_slice(src_bytes);
176            data.extend_from_slice(&fact.timestamp_ms.to_le_bytes());
177            data.extend_from_slice(&fact.access_count.to_le_bytes());
178            data.push(fact.verified as u8);
179        }
180
181        // Concepts
182        let concept_count = self.concepts.len() as u32;
183        data.extend_from_slice(&concept_count.to_le_bytes());
184        for concept in self.concepts.values() {
185            let name_bytes = concept.name.as_bytes();
186            data.push(name_bytes.len() as u8);
187            data.extend_from_slice(name_bytes);
188            let def_bytes = concept.definition.as_bytes();
189            data.extend_from_slice(&(def_bytes.len() as u16).to_le_bytes());
190            data.extend_from_slice(def_bytes);
191            data.extend_from_slice(&concept.abstraction_level.to_le_bytes());
192
193            let ex_count = concept.examples.len() as u8;
194            data.push(ex_count);
195            for example in &concept.examples {
196                let ex_bytes = example.as_bytes();
197                data.extend_from_slice(&(ex_bytes.len() as u16).to_le_bytes());
198                data.extend_from_slice(ex_bytes);
199            }
200        }
201
202        // Events
203        let event_count = self.events.len() as u32;
204        data.extend_from_slice(&event_count.to_le_bytes());
205        for event in &self.events {
206            let desc_bytes = event.description.as_bytes();
207            data.extend_from_slice(&(desc_bytes.len() as u16).to_le_bytes());
208            data.extend_from_slice(desc_bytes);
209            data.extend_from_slice(&event.timestamp_ms.to_le_bytes());
210            let loc_bytes = event.location.as_bytes();
211            data.push(loc_bytes.len() as u8);
212            data.extend_from_slice(loc_bytes);
213            data.extend_from_slice(&event.emotional_significance.to_le_bytes());
214            data.extend_from_slice(&event.detail_level.to_le_bytes());
215        }
216
217        let tmp_path = dir.join("explicit_memory.bin.tmp");
218        fs::write(&tmp_path, data).map_err(|e| e.to_string())?;
219        fs::rename(&tmp_path, &path).map_err(|e| e.to_string())
220    }
221
222    pub fn load(dir: &Path) -> Result<Self, String> {
223        let path = dir.join("explicit_memory.bin");
224        if !path.exists() {
225            return Ok(Self::new());
226        }
227
228        let data = fs::read(&path).map_err(|e| e.to_string())?;
229        if data.len() < 5 || &data[0..4] != b"EXPL" {
230            return Ok(Self::new());
231        }
232
233        let mut idx = 5;
234        let mut facts = HashMap::new();
235        let mut concepts = HashMap::new();
236        let events = Vec::new();
237
238        // Read facts
239        if idx + 4 <= data.len() {
240            let fact_count =
241                u32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]])
242                    as usize;
243            idx += 4;
244
245            for _ in 0..fact_count {
246                if idx + 2 > data.len() {
247                    break;
248                }
249                let stmt_len = u16::from_le_bytes([data[idx], data[idx + 1]]) as usize;
250                idx += 2;
251
252                if idx + stmt_len > data.len() {
253                    break;
254                }
255                let statement = String::from_utf8_lossy(&data[idx..idx + stmt_len]).to_string();
256                idx += stmt_len;
257
258                if idx + 4 > data.len() {
259                    break;
260                }
261                let confidence =
262                    f32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
263                idx += 4;
264
265                if idx >= data.len() {
266                    break;
267                }
268                let src_len = data[idx] as usize;
269                idx += 1;
270
271                if idx + src_len > data.len() {
272                    break;
273                }
274                let source = String::from_utf8_lossy(&data[idx..idx + src_len]).to_string();
275                idx += src_len;
276
277                if idx + 13 > data.len() {
278                    break;
279                }
280                let timestamp_ms = u64::from_le_bytes([
281                    data[idx],
282                    data[idx + 1],
283                    data[idx + 2],
284                    data[idx + 3],
285                    data[idx + 4],
286                    data[idx + 5],
287                    data[idx + 6],
288                    data[idx + 7],
289                ]);
290                idx += 8;
291
292                let access_count =
293                    u32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
294                idx += 4;
295
296                let verified = data[idx] != 0;
297                idx += 1;
298
299                facts.insert(
300                    statement.to_lowercase(),
301                    Fact {
302                        statement,
303                        confidence,
304                        source,
305                        timestamp_ms,
306                        access_count,
307                        verified,
308                    },
309                );
310            }
311        }
312
313        // Read concepts (simplified)
314        if idx + 4 <= data.len() {
315            let concept_count =
316                u32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]])
317                    as usize;
318            idx += 4;
319
320            for _ in 0..concept_count {
321                if idx >= data.len() {
322                    break;
323                }
324                let name_len = data[idx] as usize;
325                idx += 1;
326
327                if idx + name_len + 6 > data.len() {
328                    break;
329                }
330                let name = String::from_utf8_lossy(&data[idx..idx + name_len]).to_string();
331                idx += name_len;
332
333                let def_len = u16::from_le_bytes([data[idx], data[idx + 1]]) as usize;
334                idx += 2;
335
336                if idx + def_len > data.len() {
337                    break;
338                }
339                let definition = String::from_utf8_lossy(&data[idx..idx + def_len]).to_string();
340                idx += def_len;
341
342                if idx + 4 > data.len() {
343                    break;
344                }
345                let abstraction_level =
346                    f32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
347                idx += 4;
348
349                concepts.insert(
350                    name.clone(),
351                    Concept {
352                        name,
353                        definition,
354                        examples: Vec::new(),
355                        related_concepts: Vec::new(),
356                        abstraction_level,
357                    },
358                );
359            }
360        }
361
362        Ok(Self {
363            facts,
364            concepts,
365            events,
366            relationships: HashMap::new(),
367        })
368    }
369
370    pub fn load_or_init(dir: &Path) -> Self {
371        Self::load(dir).unwrap_or_else(|_| Self::new())
372    }
373
374    fn now_ms() -> u64 {
375        std::time::SystemTime::now()
376            .duration_since(std::time::UNIX_EPOCH)
377            .unwrap_or_default()
378            .as_millis() as u64
379    }
380}