omega_persistence/
storage.rs

1use rusqlite::{Connection, OptionalExtension, params};
2use serde::{Deserialize, Serialize};
3use thiserror::Error;
4
5use crate::schema;
6
7/// Custom error types for storage operations
8#[derive(Error, Debug)]
9pub enum StorageError {
10    #[error("Database error: {0}")]
11    Database(#[from] rusqlite::Error),
12
13    #[error("Serialization error: {0}")]
14    Serialization(#[from] serde_json::Error),
15
16    #[error("Not found: {0}")]
17    NotFound(String),
18
19    #[error("Invalid data: {0}")]
20    InvalidData(String),
21}
22
23pub type Result<T> = std::result::Result<T, StorageError>;
24
25/// Stored memory record
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct StoredMemory {
28    pub id: String,
29    pub content: String,
30    pub tier: i32,
31    pub importance: f64,
32    pub embedding_blob: Option<Vec<u8>>,
33    pub created_at: i64,
34    pub last_accessed: i64,
35}
36
37/// Stored skill record
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct StoredSkill {
40    pub id: String,
41    pub name: String,
42    pub description: String,
43    pub trigger_pattern: String,
44    pub success_count: i32,
45    pub last_used: Option<i64>,
46    pub created_at: i64,
47}
48
49/// Stored architecture record
50#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct StoredArchitecture {
52    pub id: String,
53    pub name: String,
54    pub paradigm: String,
55    pub substrate: String,
56    pub fitness_json: String,
57    pub lineage_json: String,
58    pub created_at: i64,
59}
60
61/// Stored intelligence record
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct StoredIntelligence {
64    pub id: String,
65    pub name: String,
66    pub arch_id: String,
67    pub maturity: f64,
68    pub capabilities_json: String,
69    pub memories_json: String,
70    pub state_json: String,
71    pub created_at: i64,
72    pub updated_at: i64,
73}
74
75/// Stored vector record
76#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct StoredVector {
78    pub id: String,
79    pub memory_id: String,
80    pub dimensions: i32,
81    pub data_blob: Vec<u8>,
82}
83
84/// Stored reflexion episode
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct StoredReflexionEpisode {
87    pub id: String,
88    pub memory_id: String,
89    pub trigger: String,
90    pub context: String,
91    pub action: String,
92    pub outcome: String,
93    pub created_at: i64,
94}
95
96/// Stored causal edge
97#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct StoredCausalEdge {
99    pub id: String,
100    pub from_memory: String,
101    pub to_memory: String,
102    pub weight: f64,
103    pub edge_type: String,
104    pub created_at: i64,
105}
106
107/// Main storage interface for ExoGenesis Omega
108pub struct OmegaStore {
109    conn: Connection,
110}
111
112impl OmegaStore {
113    /// Create or open a database at the given path
114    ///
115    /// # Arguments
116    /// * `path` - Path to the SQLite database file
117    ///
118    /// # Returns
119    /// * `Result<Self>` - New OmegaStore instance or error
120    pub fn new(path: &str) -> Result<Self> {
121        let conn = Connection::open(path)?;
122
123        // Enable foreign keys
124        conn.execute("PRAGMA foreign_keys = ON;", [])?;
125
126        // Run all schema migrations
127        for schema_sql in schema::ALL_SCHEMAS {
128            conn.execute(schema_sql, [])?;
129        }
130
131        Ok(Self { conn })
132    }
133
134    /// Create an in-memory database (useful for testing)
135    pub fn new_in_memory() -> Result<Self> {
136        let conn = Connection::open_in_memory()?;
137        conn.execute("PRAGMA foreign_keys = ON;", [])?;
138
139        for schema_sql in schema::ALL_SCHEMAS {
140            conn.execute(schema_sql, [])?;
141        }
142
143        Ok(Self { conn })
144    }
145
146    // ===== MEMORY OPERATIONS =====
147
148    /// Store a memory in the database
149    pub fn store_memory(&self, memory: &StoredMemory) -> Result<()> {
150        self.conn.execute(
151            "INSERT OR REPLACE INTO memories
152             (id, content, tier, importance, embedding_blob, created_at, last_accessed)
153             VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
154            params![
155                &memory.id,
156                &memory.content,
157                memory.tier,
158                memory.importance,
159                &memory.embedding_blob,
160                memory.created_at,
161                memory.last_accessed,
162            ],
163        )?;
164        Ok(())
165    }
166
167    /// Retrieve a memory by ID
168    pub fn get_memory(&self, id: &str) -> Result<StoredMemory> {
169        let memory = self.conn.query_row(
170            "SELECT id, content, tier, importance, embedding_blob, created_at, last_accessed
171             FROM memories WHERE id = ?1",
172            params![id],
173            |row| {
174                Ok(StoredMemory {
175                    id: row.get(0)?,
176                    content: row.get(1)?,
177                    tier: row.get(2)?,
178                    importance: row.get(3)?,
179                    embedding_blob: row.get(4)?,
180                    created_at: row.get(5)?,
181                    last_accessed: row.get(6)?,
182                })
183            },
184        ).optional()?
185        .ok_or_else(|| StorageError::NotFound(format!("Memory not found: {}", id)))?;
186
187        Ok(memory)
188    }
189
190    /// Query memories by tier
191    pub fn query_memories_by_tier(&self, tier: i32) -> Result<Vec<StoredMemory>> {
192        let mut stmt = self.conn.prepare(
193            "SELECT id, content, tier, importance, embedding_blob, created_at, last_accessed
194             FROM memories WHERE tier = ?1 ORDER BY importance DESC"
195        )?;
196
197        let memories = stmt.query_map(params![tier], |row| {
198            Ok(StoredMemory {
199                id: row.get(0)?,
200                content: row.get(1)?,
201                tier: row.get(2)?,
202                importance: row.get(3)?,
203                embedding_blob: row.get(4)?,
204                created_at: row.get(5)?,
205                last_accessed: row.get(6)?,
206            })
207        })?
208        .collect::<std::result::Result<Vec<_>, _>>()?;
209
210        Ok(memories)
211    }
212
213    /// Update memory last accessed timestamp
214    pub fn update_memory_access(&self, id: &str, timestamp: i64) -> Result<()> {
215        let rows = self.conn.execute(
216            "UPDATE memories SET last_accessed = ?1 WHERE id = ?2",
217            params![timestamp, id],
218        )?;
219
220        if rows == 0 {
221            return Err(StorageError::NotFound(format!("Memory not found: {}", id)));
222        }
223
224        Ok(())
225    }
226
227    // ===== SKILL OPERATIONS =====
228
229    /// Store a skill in the database
230    pub fn store_skill(&self, skill: &StoredSkill) -> Result<()> {
231        self.conn.execute(
232            "INSERT OR REPLACE INTO skills
233             (id, name, description, trigger_pattern, success_count, last_used, created_at)
234             VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
235            params![
236                &skill.id,
237                &skill.name,
238                &skill.description,
239                &skill.trigger_pattern,
240                skill.success_count,
241                skill.last_used,
242                skill.created_at,
243            ],
244        )?;
245        Ok(())
246    }
247
248    /// Retrieve a skill by ID
249    pub fn get_skill(&self, id: &str) -> Result<StoredSkill> {
250        let skill = self.conn.query_row(
251            "SELECT id, name, description, trigger_pattern, success_count, last_used, created_at
252             FROM skills WHERE id = ?1",
253            params![id],
254            |row| {
255                Ok(StoredSkill {
256                    id: row.get(0)?,
257                    name: row.get(1)?,
258                    description: row.get(2)?,
259                    trigger_pattern: row.get(3)?,
260                    success_count: row.get(4)?,
261                    last_used: row.get(5)?,
262                    created_at: row.get(6)?,
263                })
264            },
265        ).optional()?
266        .ok_or_else(|| StorageError::NotFound(format!("Skill not found: {}", id)))?;
267
268        Ok(skill)
269    }
270
271    /// Get skills matching a trigger pattern
272    pub fn get_skills_by_pattern(&self, pattern: &str) -> Result<Vec<StoredSkill>> {
273        let mut stmt = self.conn.prepare(
274            "SELECT id, name, description, trigger_pattern, success_count, last_used, created_at
275             FROM skills WHERE trigger_pattern LIKE ?1 ORDER BY success_count DESC"
276        )?;
277
278        let pattern_query = format!("%{}%", pattern);
279        let skills = stmt.query_map(params![pattern_query], |row| {
280            Ok(StoredSkill {
281                id: row.get(0)?,
282                name: row.get(1)?,
283                description: row.get(2)?,
284                trigger_pattern: row.get(3)?,
285                success_count: row.get(4)?,
286                last_used: row.get(5)?,
287                created_at: row.get(6)?,
288            })
289        })?
290        .collect::<std::result::Result<Vec<_>, _>>()?;
291
292        Ok(skills)
293    }
294
295    /// Increment skill success count
296    pub fn increment_skill_success(&self, id: &str, timestamp: i64) -> Result<()> {
297        let rows = self.conn.execute(
298            "UPDATE skills SET success_count = success_count + 1, last_used = ?1 WHERE id = ?2",
299            params![timestamp, id],
300        )?;
301
302        if rows == 0 {
303            return Err(StorageError::NotFound(format!("Skill not found: {}", id)));
304        }
305
306        Ok(())
307    }
308
309    // ===== ARCHITECTURE OPERATIONS =====
310
311    /// Store an architecture in the database
312    pub fn store_architecture(&self, arch: &StoredArchitecture) -> Result<()> {
313        self.conn.execute(
314            "INSERT OR REPLACE INTO architectures
315             (id, name, paradigm, substrate, fitness_json, lineage_json, created_at)
316             VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
317            params![
318                &arch.id,
319                &arch.name,
320                &arch.paradigm,
321                &arch.substrate,
322                &arch.fitness_json,
323                &arch.lineage_json,
324                arch.created_at,
325            ],
326        )?;
327        Ok(())
328    }
329
330    /// Retrieve an architecture by ID
331    pub fn get_architecture(&self, id: &str) -> Result<StoredArchitecture> {
332        let arch = self.conn.query_row(
333            "SELECT id, name, paradigm, substrate, fitness_json, lineage_json, created_at
334             FROM architectures WHERE id = ?1",
335            params![id],
336            |row| {
337                Ok(StoredArchitecture {
338                    id: row.get(0)?,
339                    name: row.get(1)?,
340                    paradigm: row.get(2)?,
341                    substrate: row.get(3)?,
342                    fitness_json: row.get(4)?,
343                    lineage_json: row.get(5)?,
344                    created_at: row.get(6)?,
345                })
346            },
347        ).optional()?
348        .ok_or_else(|| StorageError::NotFound(format!("Architecture not found: {}", id)))?;
349
350        Ok(arch)
351    }
352
353    /// Get all architectures by paradigm
354    pub fn get_architectures_by_paradigm(&self, paradigm: &str) -> Result<Vec<StoredArchitecture>> {
355        let mut stmt = self.conn.prepare(
356            "SELECT id, name, paradigm, substrate, fitness_json, lineage_json, created_at
357             FROM architectures WHERE paradigm = ?1 ORDER BY created_at DESC"
358        )?;
359
360        let archs = stmt.query_map(params![paradigm], |row| {
361            Ok(StoredArchitecture {
362                id: row.get(0)?,
363                name: row.get(1)?,
364                paradigm: row.get(2)?,
365                substrate: row.get(3)?,
366                fitness_json: row.get(4)?,
367                lineage_json: row.get(5)?,
368                created_at: row.get(6)?,
369            })
370        })?
371        .collect::<std::result::Result<Vec<_>, _>>()?;
372
373        Ok(archs)
374    }
375
376    // ===== INTELLIGENCE OPERATIONS =====
377
378    /// Store an intelligence in the database
379    pub fn store_intelligence(&self, intel: &StoredIntelligence) -> Result<()> {
380        self.conn.execute(
381            "INSERT OR REPLACE INTO intelligences
382             (id, name, arch_id, maturity, capabilities_json, memories_json, state_json, created_at, updated_at)
383             VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)",
384            params![
385                &intel.id,
386                &intel.name,
387                &intel.arch_id,
388                intel.maturity,
389                &intel.capabilities_json,
390                &intel.memories_json,
391                &intel.state_json,
392                intel.created_at,
393                intel.updated_at,
394            ],
395        )?;
396        Ok(())
397    }
398
399    /// Retrieve an intelligence by ID
400    pub fn get_intelligence(&self, id: &str) -> Result<StoredIntelligence> {
401        let intel = self.conn.query_row(
402            "SELECT id, name, arch_id, maturity, capabilities_json, memories_json, state_json, created_at, updated_at
403             FROM intelligences WHERE id = ?1",
404            params![id],
405            |row| {
406                Ok(StoredIntelligence {
407                    id: row.get(0)?,
408                    name: row.get(1)?,
409                    arch_id: row.get(2)?,
410                    maturity: row.get(3)?,
411                    capabilities_json: row.get(4)?,
412                    memories_json: row.get(5)?,
413                    state_json: row.get(6)?,
414                    created_at: row.get(7)?,
415                    updated_at: row.get(8)?,
416                })
417            },
418        ).optional()?
419        .ok_or_else(|| StorageError::NotFound(format!("Intelligence not found: {}", id)))?;
420
421        Ok(intel)
422    }
423
424    /// Get intelligences by architecture ID
425    pub fn get_intelligences_by_arch(&self, arch_id: &str) -> Result<Vec<StoredIntelligence>> {
426        let mut stmt = self.conn.prepare(
427            "SELECT id, name, arch_id, maturity, capabilities_json, memories_json, state_json, created_at, updated_at
428             FROM intelligences WHERE arch_id = ?1 ORDER BY maturity DESC"
429        )?;
430
431        let intels = stmt.query_map(params![arch_id], |row| {
432            Ok(StoredIntelligence {
433                id: row.get(0)?,
434                name: row.get(1)?,
435                arch_id: row.get(2)?,
436                maturity: row.get(3)?,
437                capabilities_json: row.get(4)?,
438                memories_json: row.get(5)?,
439                state_json: row.get(6)?,
440                created_at: row.get(7)?,
441                updated_at: row.get(8)?,
442            })
443        })?
444        .collect::<std::result::Result<Vec<_>, _>>()?;
445
446        Ok(intels)
447    }
448
449    // ===== VECTOR OPERATIONS =====
450
451    /// Store a vector embedding
452    pub fn store_vector(&self, vector: &StoredVector) -> Result<()> {
453        self.conn.execute(
454            "INSERT OR REPLACE INTO vectors (id, memory_id, dimensions, data_blob)
455             VALUES (?1, ?2, ?3, ?4)",
456            params![
457                &vector.id,
458                &vector.memory_id,
459                vector.dimensions,
460                &vector.data_blob,
461            ],
462        )?;
463        Ok(())
464    }
465
466    /// Get vector by memory ID
467    pub fn get_vector_by_memory(&self, memory_id: &str) -> Result<StoredVector> {
468        let vector = self.conn.query_row(
469            "SELECT id, memory_id, dimensions, data_blob FROM vectors WHERE memory_id = ?1",
470            params![memory_id],
471            |row| {
472                Ok(StoredVector {
473                    id: row.get(0)?,
474                    memory_id: row.get(1)?,
475                    dimensions: row.get(2)?,
476                    data_blob: row.get(3)?,
477                })
478            },
479        ).optional()?
480        .ok_or_else(|| StorageError::NotFound(format!("Vector not found for memory: {}", memory_id)))?;
481
482        Ok(vector)
483    }
484
485    // ===== REFLEXION OPERATIONS =====
486
487    /// Store a reflexion episode
488    pub fn store_reflexion(&self, episode: &StoredReflexionEpisode) -> Result<()> {
489        self.conn.execute(
490            "INSERT OR REPLACE INTO reflexion_episodes
491             (id, memory_id, trigger, context, action, outcome, created_at)
492             VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
493            params![
494                &episode.id,
495                &episode.memory_id,
496                &episode.trigger,
497                &episode.context,
498                &episode.action,
499                &episode.outcome,
500                episode.created_at,
501            ],
502        )?;
503        Ok(())
504    }
505
506    /// Get reflexion episodes by memory ID
507    pub fn get_reflexions_by_memory(&self, memory_id: &str) -> Result<Vec<StoredReflexionEpisode>> {
508        let mut stmt = self.conn.prepare(
509            "SELECT id, memory_id, trigger, context, action, outcome, created_at
510             FROM reflexion_episodes WHERE memory_id = ?1 ORDER BY created_at DESC"
511        )?;
512
513        let episodes = stmt.query_map(params![memory_id], |row| {
514            Ok(StoredReflexionEpisode {
515                id: row.get(0)?,
516                memory_id: row.get(1)?,
517                trigger: row.get(2)?,
518                context: row.get(3)?,
519                action: row.get(4)?,
520                outcome: row.get(5)?,
521                created_at: row.get(6)?,
522            })
523        })?
524        .collect::<std::result::Result<Vec<_>, _>>()?;
525
526        Ok(episodes)
527    }
528
529    // ===== CAUSAL EDGE OPERATIONS =====
530
531    /// Store a causal edge
532    pub fn store_causal_edge(&self, edge: &StoredCausalEdge) -> Result<()> {
533        self.conn.execute(
534            "INSERT OR REPLACE INTO causal_edges
535             (id, from_memory, to_memory, weight, edge_type, created_at)
536             VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
537            params![
538                &edge.id,
539                &edge.from_memory,
540                &edge.to_memory,
541                edge.weight,
542                &edge.edge_type,
543                edge.created_at,
544            ],
545        )?;
546        Ok(())
547    }
548
549    /// Get outgoing causal edges from a memory
550    pub fn get_causal_edges_from(&self, memory_id: &str) -> Result<Vec<StoredCausalEdge>> {
551        let mut stmt = self.conn.prepare(
552            "SELECT id, from_memory, to_memory, weight, edge_type, created_at
553             FROM causal_edges WHERE from_memory = ?1 ORDER BY weight DESC"
554        )?;
555
556        let edges = stmt.query_map(params![memory_id], |row| {
557            Ok(StoredCausalEdge {
558                id: row.get(0)?,
559                from_memory: row.get(1)?,
560                to_memory: row.get(2)?,
561                weight: row.get(3)?,
562                edge_type: row.get(4)?,
563                created_at: row.get(5)?,
564            })
565        })?
566        .collect::<std::result::Result<Vec<_>, _>>()?;
567
568        Ok(edges)
569    }
570
571    // ===== BACKUP OPERATIONS =====
572
573    /// Backup the database to a specified path
574    pub fn backup(&self, backup_path: &str) -> Result<()> {
575        let mut backup_conn = Connection::open(backup_path)?;
576        let backup = rusqlite::backup::Backup::new(&self.conn, &mut backup_conn)?;
577        backup.run_to_completion(5, std::time::Duration::from_millis(250), None)?;
578        Ok(())
579    }
580
581    /// Get database statistics
582    pub fn get_statistics(&self) -> Result<DatabaseStatistics> {
583        let memory_count: i64 = self.conn.query_row(
584            "SELECT COUNT(*) FROM memories",
585            [],
586            |row| row.get(0),
587        )?;
588
589        let skill_count: i64 = self.conn.query_row(
590            "SELECT COUNT(*) FROM skills",
591            [],
592            |row| row.get(0),
593        )?;
594
595        let architecture_count: i64 = self.conn.query_row(
596            "SELECT COUNT(*) FROM architectures",
597            [],
598            |row| row.get(0),
599        )?;
600
601        let intelligence_count: i64 = self.conn.query_row(
602            "SELECT COUNT(*) FROM intelligences",
603            [],
604            |row| row.get(0),
605        )?;
606
607        let causal_edge_count: i64 = self.conn.query_row(
608            "SELECT COUNT(*) FROM causal_edges",
609            [],
610            |row| row.get(0),
611        )?;
612
613        Ok(DatabaseStatistics {
614            memory_count,
615            skill_count,
616            architecture_count,
617            intelligence_count,
618            causal_edge_count,
619        })
620    }
621}
622
623/// Database statistics
624#[derive(Debug, Clone, Serialize, Deserialize)]
625pub struct DatabaseStatistics {
626    pub memory_count: i64,
627    pub skill_count: i64,
628    pub architecture_count: i64,
629    pub intelligence_count: i64,
630    pub causal_edge_count: i64,
631}