omega_persistence/
lib.rs

1//! # omega-persistence
2//!
3//! SQLite-backed persistence layer for ExoGenesis Omega.
4//!
5//! This crate provides durable storage for:
6//! - Hierarchical memory tiers
7//! - Learned skills with usage tracking
8//! - Evolved architectures with lineage
9//! - Intelligence instances with state
10//! - Causal graphs and reflexion episodes
11//! - Vector embeddings for similarity search
12//!
13//! ## Example
14//!
15//! ```
16//! use omega_persistence::{OmegaStore, StoredMemory};
17//! use chrono::Utc;
18//!
19//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
20//! // Create an in-memory database for testing
21//! let store = OmegaStore::new_in_memory()?;
22//!
23//! // Store a memory
24//! let memory = StoredMemory {
25//!     id: "mem-001".to_string(),
26//!     content: "First memory in the system".to_string(),
27//!     tier: 1,
28//!     importance: 0.95,
29//!     embedding_blob: None,
30//!     created_at: Utc::now().timestamp(),
31//!     last_accessed: Utc::now().timestamp(),
32//! };
33//!
34//! store.store_memory(&memory)?;
35//!
36//! // Retrieve it
37//! let retrieved = store.get_memory("mem-001")?;
38//! assert_eq!(retrieved.content, "First memory in the system");
39//! assert_eq!(retrieved.tier, 1);
40//! # Ok(())
41//! # }
42//! ```
43
44pub mod schema;
45pub mod storage;
46
47pub use storage::{
48    OmegaStore, StorageError, Result,
49    StoredMemory, StoredSkill, StoredArchitecture,
50    StoredIntelligence, StoredVector, StoredReflexionEpisode,
51    StoredCausalEdge, DatabaseStatistics,
52};
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57    use chrono::Utc;
58
59    fn create_test_store() -> OmegaStore {
60        OmegaStore::new_in_memory().expect("Failed to create test store")
61    }
62
63    #[test]
64    fn test_store_and_retrieve_memory() {
65        let store = create_test_store();
66        let now = Utc::now().timestamp();
67
68        let memory = StoredMemory {
69            id: "mem-test-001".to_string(),
70            content: "Test memory content".to_string(),
71            tier: 2,
72            importance: 0.85,
73            embedding_blob: Some(vec![1, 2, 3, 4]),
74            created_at: now,
75            last_accessed: now,
76        };
77
78        store.store_memory(&memory).unwrap();
79        let retrieved = store.get_memory("mem-test-001").unwrap();
80
81        assert_eq!(retrieved.id, "mem-test-001");
82        assert_eq!(retrieved.content, "Test memory content");
83        assert_eq!(retrieved.tier, 2);
84        assert_eq!(retrieved.importance, 0.85);
85        assert_eq!(retrieved.embedding_blob, Some(vec![1, 2, 3, 4]));
86    }
87
88    #[test]
89    fn test_query_memories_by_tier() {
90        let store = create_test_store();
91        let now = Utc::now().timestamp();
92
93        // Store multiple memories in different tiers
94        for i in 0..5 {
95            let memory = StoredMemory {
96                id: format!("mem-tier-{}", i),
97                content: format!("Memory {}", i),
98                tier: if i < 3 { 1 } else { 2 },
99                importance: 0.5 + (i as f64 * 0.1),
100                embedding_blob: None,
101                created_at: now,
102                last_accessed: now,
103            };
104            store.store_memory(&memory).unwrap();
105        }
106
107        let tier1_memories = store.query_memories_by_tier(1).unwrap();
108        assert_eq!(tier1_memories.len(), 3);
109
110        let tier2_memories = store.query_memories_by_tier(2).unwrap();
111        assert_eq!(tier2_memories.len(), 2);
112
113        // Verify ordering by importance (descending)
114        assert!(tier1_memories[0].importance >= tier1_memories[1].importance);
115    }
116
117    #[test]
118    fn test_update_memory_access() {
119        let store = create_test_store();
120        let now = Utc::now().timestamp();
121
122        let memory = StoredMemory {
123            id: "mem-access-001".to_string(),
124            content: "Access test".to_string(),
125            tier: 1,
126            importance: 0.7,
127            embedding_blob: None,
128            created_at: now,
129            last_accessed: now,
130        };
131
132        store.store_memory(&memory).unwrap();
133
134        let new_access_time = now + 1000;
135        store.update_memory_access("mem-access-001", new_access_time).unwrap();
136
137        let retrieved = store.get_memory("mem-access-001").unwrap();
138        assert_eq!(retrieved.last_accessed, new_access_time);
139    }
140
141    #[test]
142    fn test_store_and_retrieve_skill() {
143        let store = create_test_store();
144        let now = Utc::now().timestamp();
145
146        let skill = StoredSkill {
147            id: "skill-001".to_string(),
148            name: "code_review".to_string(),
149            description: "Perform code review with best practices".to_string(),
150            trigger_pattern: "review.*code".to_string(),
151            success_count: 5,
152            last_used: Some(now),
153            created_at: now,
154        };
155
156        store.store_skill(&skill).unwrap();
157        let retrieved = store.get_skill("skill-001").unwrap();
158
159        assert_eq!(retrieved.name, "code_review");
160        assert_eq!(retrieved.success_count, 5);
161    }
162
163    #[test]
164    fn test_get_skills_by_pattern() {
165        let store = create_test_store();
166        let now = Utc::now().timestamp();
167
168        let skills = vec![
169            StoredSkill {
170                id: "skill-code-1".to_string(),
171                name: "code_analysis".to_string(),
172                description: "Analyze code quality".to_string(),
173                trigger_pattern: "analyze_code".to_string(),
174                success_count: 10,
175                last_used: Some(now),
176                created_at: now,
177            },
178            StoredSkill {
179                id: "skill-code-2".to_string(),
180                name: "code_generation".to_string(),
181                description: "Generate code from spec".to_string(),
182                trigger_pattern: "generate_code".to_string(),
183                success_count: 7,
184                last_used: Some(now),
185                created_at: now,
186            },
187            StoredSkill {
188                id: "skill-test-1".to_string(),
189                name: "test_writing".to_string(),
190                description: "Write unit tests".to_string(),
191                trigger_pattern: "write_tests".to_string(),
192                success_count: 3,
193                last_used: Some(now),
194                created_at: now,
195            },
196        ];
197
198        for skill in &skills {
199            store.store_skill(skill).unwrap();
200        }
201
202        let code_skills = store.get_skills_by_pattern("code").unwrap();
203        assert_eq!(code_skills.len(), 2);
204
205        // Verify ordering by success count (descending)
206        assert_eq!(code_skills[0].success_count, 10);
207        assert_eq!(code_skills[1].success_count, 7);
208    }
209
210    #[test]
211    fn test_increment_skill_success() {
212        let store = create_test_store();
213        let now = Utc::now().timestamp();
214
215        let skill = StoredSkill {
216            id: "skill-inc-001".to_string(),
217            name: "test_skill".to_string(),
218            description: "Test".to_string(),
219            trigger_pattern: "test".to_string(),
220            success_count: 5,
221            last_used: None,
222            created_at: now,
223        };
224
225        store.store_skill(&skill).unwrap();
226
227        let new_time = now + 500;
228        store.increment_skill_success("skill-inc-001", new_time).unwrap();
229
230        let retrieved = store.get_skill("skill-inc-001").unwrap();
231        assert_eq!(retrieved.success_count, 6);
232        assert_eq!(retrieved.last_used, Some(new_time));
233    }
234
235    #[test]
236    fn test_store_and_retrieve_architecture() {
237        let store = create_test_store();
238        let now = Utc::now().timestamp();
239
240        let arch = StoredArchitecture {
241            id: "arch-001".to_string(),
242            name: "HybridTransformer".to_string(),
243            paradigm: "neural".to_string(),
244            substrate: "pytorch".to_string(),
245            fitness_json: r#"{"accuracy": 0.95, "speed": 0.8}"#.to_string(),
246            lineage_json: r#"{"parent": null, "generation": 1}"#.to_string(),
247            created_at: now,
248        };
249
250        store.store_architecture(&arch).unwrap();
251        let retrieved = store.get_architecture("arch-001").unwrap();
252
253        assert_eq!(retrieved.name, "HybridTransformer");
254        assert_eq!(retrieved.paradigm, "neural");
255    }
256
257    #[test]
258    fn test_get_architectures_by_paradigm() {
259        let store = create_test_store();
260        let now = Utc::now().timestamp();
261
262        let archs = vec![
263            StoredArchitecture {
264                id: "arch-neural-1".to_string(),
265                name: "Transformer".to_string(),
266                paradigm: "neural".to_string(),
267                substrate: "pytorch".to_string(),
268                fitness_json: "{}".to_string(),
269                lineage_json: "{}".to_string(),
270                created_at: now,
271            },
272            StoredArchitecture {
273                id: "arch-neural-2".to_string(),
274                name: "CNN".to_string(),
275                paradigm: "neural".to_string(),
276                substrate: "tensorflow".to_string(),
277                fitness_json: "{}".to_string(),
278                lineage_json: "{}".to_string(),
279                created_at: now + 100,
280            },
281            StoredArchitecture {
282                id: "arch-symbolic-1".to_string(),
283                name: "LogicNet".to_string(),
284                paradigm: "symbolic".to_string(),
285                substrate: "prolog".to_string(),
286                fitness_json: "{}".to_string(),
287                lineage_json: "{}".to_string(),
288                created_at: now + 200,
289            },
290        ];
291
292        for arch in &archs {
293            store.store_architecture(arch).unwrap();
294        }
295
296        let neural_archs = store.get_architectures_by_paradigm("neural").unwrap();
297        assert_eq!(neural_archs.len(), 2);
298
299        // Verify ordering by created_at (descending)
300        assert_eq!(neural_archs[0].name, "CNN");
301        assert_eq!(neural_archs[1].name, "Transformer");
302    }
303
304    #[test]
305    fn test_store_and_retrieve_intelligence() {
306        let store = create_test_store();
307        let now = Utc::now().timestamp();
308
309        // First create an architecture
310        let arch = StoredArchitecture {
311            id: "arch-intel-001".to_string(),
312            name: "TestArch".to_string(),
313            paradigm: "hybrid".to_string(),
314            substrate: "rust".to_string(),
315            fitness_json: "{}".to_string(),
316            lineage_json: "{}".to_string(),
317            created_at: now,
318        };
319        store.store_architecture(&arch).unwrap();
320
321        let intel = StoredIntelligence {
322            id: "intel-001".to_string(),
323            name: "Alpha".to_string(),
324            arch_id: "arch-intel-001".to_string(),
325            maturity: 0.75,
326            capabilities_json: r#"["reasoning", "learning"]"#.to_string(),
327            memories_json: r#"["mem-001", "mem-002"]"#.to_string(),
328            state_json: r#"{"active": true}"#.to_string(),
329            created_at: now,
330            updated_at: now,
331        };
332
333        store.store_intelligence(&intel).unwrap();
334        let retrieved = store.get_intelligence("intel-001").unwrap();
335
336        assert_eq!(retrieved.name, "Alpha");
337        assert_eq!(retrieved.maturity, 0.75);
338        assert_eq!(retrieved.arch_id, "arch-intel-001");
339    }
340
341    #[test]
342    fn test_get_intelligences_by_arch() {
343        let store = create_test_store();
344        let now = Utc::now().timestamp();
345
346        // Create architecture
347        let arch = StoredArchitecture {
348            id: "arch-multi-001".to_string(),
349            name: "MultiArch".to_string(),
350            paradigm: "neural".to_string(),
351            substrate: "pytorch".to_string(),
352            fitness_json: "{}".to_string(),
353            lineage_json: "{}".to_string(),
354            created_at: now,
355        };
356        store.store_architecture(&arch).unwrap();
357
358        // Create multiple intelligences
359        for i in 0..3 {
360            let intel = StoredIntelligence {
361                id: format!("intel-multi-{}", i),
362                name: format!("Intel-{}", i),
363                arch_id: "arch-multi-001".to_string(),
364                maturity: 0.5 + (i as f64 * 0.2),
365                capabilities_json: "[]".to_string(),
366                memories_json: "[]".to_string(),
367                state_json: "{}".to_string(),
368                created_at: now,
369                updated_at: now,
370            };
371            store.store_intelligence(&intel).unwrap();
372        }
373
374        let intels = store.get_intelligences_by_arch("arch-multi-001").unwrap();
375        assert_eq!(intels.len(), 3);
376
377        // Verify ordering by maturity (descending)
378        assert!(intels[0].maturity >= intels[1].maturity);
379        assert!(intels[1].maturity >= intels[2].maturity);
380    }
381
382    #[test]
383    fn test_store_and_retrieve_vector() {
384        let store = create_test_store();
385        let now = Utc::now().timestamp();
386
387        // Create memory first
388        let memory = StoredMemory {
389            id: "mem-vec-001".to_string(),
390            content: "Vector test".to_string(),
391            tier: 1,
392            importance: 0.8,
393            embedding_blob: None,
394            created_at: now,
395            last_accessed: now,
396        };
397        store.store_memory(&memory).unwrap();
398
399        // Create vector
400        let vector = StoredVector {
401            id: "vec-001".to_string(),
402            memory_id: "mem-vec-001".to_string(),
403            dimensions: 768,
404            data_blob: vec![0u8; 768 * 4], // 768 floats as bytes
405        };
406
407        store.store_vector(&vector).unwrap();
408        let retrieved = store.get_vector_by_memory("mem-vec-001").unwrap();
409
410        assert_eq!(retrieved.dimensions, 768);
411        assert_eq!(retrieved.data_blob.len(), 768 * 4);
412    }
413
414    #[test]
415    fn test_store_and_retrieve_reflexion() {
416        let store = create_test_store();
417        let now = Utc::now().timestamp();
418
419        // Create memory first
420        let memory = StoredMemory {
421            id: "mem-reflex-001".to_string(),
422            content: "Reflexion test".to_string(),
423            tier: 1,
424            importance: 0.9,
425            embedding_blob: None,
426            created_at: now,
427            last_accessed: now,
428        };
429        store.store_memory(&memory).unwrap();
430
431        // Create reflexion episode
432        let episode = StoredReflexionEpisode {
433            id: "reflex-001".to_string(),
434            memory_id: "mem-reflex-001".to_string(),
435            trigger: "code_error".to_string(),
436            context: "Syntax error in function".to_string(),
437            action: "Fixed missing semicolon".to_string(),
438            outcome: "Code compiled successfully".to_string(),
439            created_at: now,
440        };
441
442        store.store_reflexion(&episode).unwrap();
443        let episodes = store.get_reflexions_by_memory("mem-reflex-001").unwrap();
444
445        assert_eq!(episodes.len(), 1);
446        assert_eq!(episodes[0].trigger, "code_error");
447        assert_eq!(episodes[0].outcome, "Code compiled successfully");
448    }
449
450    #[test]
451    fn test_store_and_retrieve_causal_edge() {
452        let store = create_test_store();
453        let now = Utc::now().timestamp();
454
455        // Create two memories
456        let mem1 = StoredMemory {
457            id: "mem-cause-001".to_string(),
458            content: "Cause".to_string(),
459            tier: 1,
460            importance: 0.8,
461            embedding_blob: None,
462            created_at: now,
463            last_accessed: now,
464        };
465        let mem2 = StoredMemory {
466            id: "mem-effect-001".to_string(),
467            content: "Effect".to_string(),
468            tier: 1,
469            importance: 0.7,
470            embedding_blob: None,
471            created_at: now,
472            last_accessed: now,
473        };
474        store.store_memory(&mem1).unwrap();
475        store.store_memory(&mem2).unwrap();
476
477        // Create causal edge
478        let edge = StoredCausalEdge {
479            id: "edge-001".to_string(),
480            from_memory: "mem-cause-001".to_string(),
481            to_memory: "mem-effect-001".to_string(),
482            weight: 0.9,
483            edge_type: "causal".to_string(),
484            created_at: now,
485        };
486
487        store.store_causal_edge(&edge).unwrap();
488        let edges = store.get_causal_edges_from("mem-cause-001").unwrap();
489
490        assert_eq!(edges.len(), 1);
491        assert_eq!(edges[0].to_memory, "mem-effect-001");
492        assert_eq!(edges[0].weight, 0.9);
493    }
494
495    #[test]
496    fn test_database_statistics() {
497        let store = create_test_store();
498        let now = Utc::now().timestamp();
499
500        // Add some data
501        let memory = StoredMemory {
502            id: "mem-stats-001".to_string(),
503            content: "Stats test".to_string(),
504            tier: 1,
505            importance: 0.5,
506            embedding_blob: None,
507            created_at: now,
508            last_accessed: now,
509        };
510        store.store_memory(&memory).unwrap();
511
512        let skill = StoredSkill {
513            id: "skill-stats-001".to_string(),
514            name: "test".to_string(),
515            description: "test".to_string(),
516            trigger_pattern: "test".to_string(),
517            success_count: 0,
518            last_used: None,
519            created_at: now,
520        };
521        store.store_skill(&skill).unwrap();
522
523        let stats = store.get_statistics().unwrap();
524        assert_eq!(stats.memory_count, 1);
525        assert_eq!(stats.skill_count, 1);
526        assert_eq!(stats.architecture_count, 0);
527        assert_eq!(stats.intelligence_count, 0);
528        assert_eq!(stats.causal_edge_count, 0);
529    }
530
531    #[test]
532    fn test_backup() {
533        use tempfile::NamedTempFile;
534
535        let store = create_test_store();
536        let now = Utc::now().timestamp();
537
538        // Add some data
539        let memory = StoredMemory {
540            id: "mem-backup-001".to_string(),
541            content: "Backup test".to_string(),
542            tier: 1,
543            importance: 0.8,
544            embedding_blob: None,
545            created_at: now,
546            last_accessed: now,
547        };
548        store.store_memory(&memory).unwrap();
549
550        // Create backup
551        let backup_file = NamedTempFile::new().unwrap();
552        let backup_path = backup_file.path().to_str().unwrap();
553        store.backup(backup_path).unwrap();
554
555        // Open backup and verify
556        let backup_store = OmegaStore::new(backup_path).unwrap();
557        let retrieved = backup_store.get_memory("mem-backup-001").unwrap();
558        assert_eq!(retrieved.content, "Backup test");
559    }
560
561    #[test]
562    fn test_not_found_errors() {
563        let store = create_test_store();
564
565        // Test memory not found
566        let result = store.get_memory("nonexistent");
567        assert!(result.is_err());
568        assert!(matches!(result.unwrap_err(), StorageError::NotFound(_)));
569
570        // Test skill not found
571        let result = store.get_skill("nonexistent");
572        assert!(result.is_err());
573        assert!(matches!(result.unwrap_err(), StorageError::NotFound(_)));
574
575        // Test architecture not found
576        let result = store.get_architecture("nonexistent");
577        assert!(result.is_err());
578        assert!(matches!(result.unwrap_err(), StorageError::NotFound(_)));
579
580        // Test intelligence not found
581        let result = store.get_intelligence("nonexistent");
582        assert!(result.is_err());
583        assert!(matches!(result.unwrap_err(), StorageError::NotFound(_)));
584    }
585}