1use crate::consolidation::{ConsolidationConfig, ConsolidationEngine, ConsolidationResult};
8use crate::context::{ContextBlock, ContextBuilder, ContextConfig};
9use crate::embedding::EmbeddingProvider;
10use crate::extract::{ExtractionConfig, Message};
11use crate::fact::{Entity, Fact, FactFilter, FactId, Relationship};
12use crate::graph::GraphStore;
13use crate::graph_sqlite::SqliteGraphStore;
14use crate::llm::LlmClient;
15use crate::pipeline::ExtractionPipeline;
16use crate::scope::Scope;
17use crate::store::{FactStore, MemoryError, StoreStats};
18use crate::store_sqlite::SqliteFactStore;
19use crate::vector::{VectorFilter, VectorStore};
20use crate::vector_embedded::EmbeddedVectorStore;
21use chrono::{DateTime, Utc};
22use std::collections::HashMap;
23use std::sync::Arc;
24
25#[derive(Debug, Clone, Default)]
31pub struct RecallQuery {
32 pub query: String,
34 pub scope: Option<Scope>,
36 pub max_results: usize,
38 pub as_of: Option<DateTime<Utc>>,
40 pub min_score: Option<f32>,
42}
43
44pub struct Memory {
54 fact_store: Arc<dyn FactStore>,
55 vector_store: Arc<dyn VectorStore>,
56 graph_store: Arc<dyn GraphStore>,
57 embedding: Arc<dyn EmbeddingProvider>,
58}
59
60impl Memory {
61 pub fn new(
67 fact_store: Arc<dyn FactStore>,
68 vector_store: Arc<dyn VectorStore>,
69 graph_store: Arc<dyn GraphStore>,
70 embedding: Arc<dyn EmbeddingProvider>,
71 ) -> Self {
72 Self {
73 fact_store,
74 vector_store,
75 graph_store,
76 embedding,
77 }
78 }
79
80 pub async fn in_memory(embedding: Box<dyn EmbeddingProvider>) -> Result<Self, MemoryError> {
85 let dims = embedding.dimensions();
86 let embedding = Arc::from(embedding);
87
88 let fact_store = SqliteFactStore::open("sqlite::memory:")
89 .await
90 .map_err(|e| MemoryError::Database(format!("failed to open in-memory SQLite: {e}")))?;
91 fact_store
92 .migrate()
93 .await
94 .map_err(|e| MemoryError::Database(format!("fact store migration failed: {e}")))?;
95
96 let graph_store = SqliteGraphStore::open("sqlite::memory:")
97 .await
98 .map_err(|e| MemoryError::Database(format!("failed to open in-memory graph: {e}")))?;
99 graph_store
100 .migrate()
101 .await
102 .map_err(|e| MemoryError::Database(format!("graph store migration failed: {e}")))?;
103
104 let vector_store = EmbeddedVectorStore::new(dims);
105
106 Ok(Self {
107 fact_store: Arc::new(fact_store),
108 vector_store: Arc::new(vector_store),
109 graph_store: Arc::new(graph_store),
110 embedding,
111 })
112 }
113
114 pub async fn open(
120 database_url: &str,
121 embedding: Box<dyn EmbeddingProvider>,
122 ) -> Result<Self, MemoryError> {
123 let dims = embedding.dimensions();
124 let embedding = Arc::from(embedding);
125
126 let fact_store = SqliteFactStore::open(database_url)
127 .await
128 .map_err(|e| MemoryError::Database(format!("failed to open SQLite: {e}")))?;
129 fact_store
130 .migrate()
131 .await
132 .map_err(|e| MemoryError::Database(format!("fact store migration failed: {e}")))?;
133
134 let graph_store = SqliteGraphStore::open(database_url)
135 .await
136 .map_err(|e| MemoryError::Database(format!("failed to open graph SQLite: {e}")))?;
137 graph_store
138 .migrate()
139 .await
140 .map_err(|e| MemoryError::Database(format!("graph store migration failed: {e}")))?;
141
142 let vector_store = EmbeddedVectorStore::new(dims);
143
144 Ok(Self {
145 fact_store: Arc::new(fact_store),
146 vector_store: Arc::new(vector_store),
147 graph_store: Arc::new(graph_store),
148 embedding,
149 })
150 }
151
152 pub async fn add_fact(&self, text: &str, scope: Scope) -> Result<FactId, MemoryError> {
161 let mut embeddings = self.embedding.embed(&[text]).await?;
163 let embedding = embeddings.pop().ok_or_else(|| {
164 MemoryError::Embedding("provider returned empty embeddings".to_string())
165 })?;
166
167 let mut fact = Fact::new(text, scope);
169 fact.embedding = embedding.clone();
170 let id = self.fact_store.insert_fact(fact).await?;
171
172 let metadata = serde_json::json!({ "fact_id": id.to_string() });
174 self.vector_store.upsert(id, embedding, metadata).await?;
175
176 Ok(id)
177 }
178
179 pub async fn recall(&self, query: &RecallQuery) -> Result<Vec<Fact>, MemoryError> {
185 let max_results = if query.max_results == 0 {
186 10
187 } else {
188 query.max_results
189 };
190
191 let mut embeddings = self.embedding.embed(&[query.query.as_str()]).await?;
193 let query_vec = embeddings.pop().ok_or_else(|| {
194 MemoryError::Embedding("provider returned empty embeddings".to_string())
195 })?;
196
197 let filter = VectorFilter {
199 scope: query.scope.clone(),
200 min_score: query.min_score,
201 };
202 let matches = self
203 .vector_store
204 .search(&query_vec, &filter, max_results)
205 .await?;
206
207 let mut facts = Vec::with_capacity(matches.len());
209 for vm in matches {
210 match self.fact_store.get_fact(vm.id).await {
211 Ok(fact) => {
212 let valid = match query.as_of {
214 Some(t) => fact.is_valid_at(t),
215 None => fact.is_valid(),
216 };
217 if !valid {
218 continue;
219 }
220 if let Some(ref scope) = query.scope {
222 if !scope.contains(&fact.scope) {
223 continue;
224 }
225 }
226 let _ = self.fact_store.record_access(fact.id).await;
228 facts.push(fact);
229 }
230 Err(MemoryError::NotFound(_)) => {
231 }
233 Err(e) => return Err(e),
234 }
235 }
236
237 Ok(facts)
238 }
239
240 pub async fn list_facts(&self, scope: Option<Scope>) -> Result<Vec<Fact>, MemoryError> {
246 let filter = match scope {
247 Some(s) => FactFilter::new().with_scope(s),
248 None => FactFilter::new(),
249 };
250 self.fact_store.list_facts(&filter).await
251 }
252
253 pub async fn forget(&self, id: FactId, _reason: Option<&str>) -> Result<(), MemoryError> {
262 self.fact_store.invalidate_fact(id).await?;
263 self.vector_store.delete(id).await?;
264 Ok(())
265 }
266
267 pub async fn delete_user_data(&self, scope: Scope) -> Result<u64, MemoryError> {
272 let fact_count = self.fact_store.delete_scope_data(&scope).await?;
273 self.vector_store.delete_by_scope(&scope).await?;
274 self.graph_store.delete_by_scope(&scope).await?;
275 Ok(fact_count)
276 }
277
278 pub async fn stats(&self, _scope: Option<Scope>) -> Result<StoreStats, MemoryError> {
284 self.fact_store.stats().await
285 }
286
287 pub async fn export(&self, scope: Option<Scope>) -> Result<Vec<Fact>, MemoryError> {
289 let filter = match scope {
290 Some(s) => FactFilter::new().with_scope(s),
291 None => FactFilter::new(),
292 };
293 self.fact_store.export(&filter).await
294 }
295
296 pub async fn import(&self, facts: Vec<Fact>) -> Result<u64, MemoryError> {
300 let mut imported: u64 = 0;
301 for mut fact in facts {
302 let mut embeddings = self.embedding.embed(&[fact.text.as_str()]).await?;
304 let embedding = embeddings.pop().ok_or_else(|| {
305 MemoryError::Embedding("provider returned empty embeddings".to_string())
306 })?;
307 fact.embedding = embedding.clone();
308
309 let fact_id = fact.id;
310 self.fact_store.insert_fact(fact).await?;
311
312 let metadata = serde_json::json!({ "fact_id": fact_id.to_string() });
313 self.vector_store
314 .upsert(fact_id, embedding, metadata)
315 .await?;
316 imported += 1;
317 }
318 Ok(imported)
319 }
320
321 pub async fn consolidate(
331 &self,
332 scope: &Scope,
333 llm: Option<&dyn LlmClient>,
334 config: ConsolidationConfig,
335 ) -> Result<ConsolidationResult, MemoryError> {
336 let engine = ConsolidationEngine::new(
337 self.fact_store.clone(),
338 self.vector_store.clone(),
339 self.embedding.clone(),
340 config,
341 );
342 engine.run(scope, llm).await
343 }
344
345 pub async fn context(
355 &self,
356 query: &str,
357 scope: &Scope,
358 config: ContextConfig,
359 ) -> Result<ContextBlock, MemoryError> {
360 let builder = ContextBuilder::new(
361 self.fact_store.clone(),
362 self.vector_store.clone(),
363 self.graph_store.clone(),
364 self.embedding.clone(),
365 config,
366 );
367 builder.build(query, scope).await
368 }
369
370 pub async fn add_messages(
379 &self,
380 messages: &[Message],
381 scope: Scope,
382 llm: Box<dyn LlmClient>,
383 config: ExtractionConfig,
384 ) -> Result<Vec<FactId>, MemoryError> {
385 let pipeline = ExtractionPipeline::new(llm, config);
386 let extraction = pipeline.extract(messages).await?;
387
388 let mut fact_ids = Vec::new();
389
390 for extracted in extraction.facts {
391 let mut embeddings = self.embedding.embed(&[extracted.text.as_str()]).await?;
393 let embedding = embeddings
394 .pop()
395 .ok_or_else(|| MemoryError::Embedding("empty embedding".to_string()))?;
396
397 let mut fact = Fact::new(&extracted.text, scope.clone());
398 fact.confidence = Some(extracted.confidence as f32);
399 fact.category = extracted.category;
400 fact.embedding = embedding.clone();
401
402 let id = self.fact_store.insert_fact(fact).await?;
403 self.vector_store
404 .upsert(id, embedding, serde_json::json!({}))
405 .await?;
406
407 let mut entity_map: HashMap<String, uuid::Uuid> = HashMap::new();
409 for ext_entity in &extracted.entities {
410 let entity = Entity::new(&ext_entity.name, scope.clone())
411 .with_type(ext_entity.entity_type.as_deref().unwrap_or("unknown"));
412 entity_map.insert(ext_entity.name.clone(), entity.id);
413 self.graph_store.upsert_entity(&entity).await?;
414 }
415
416 for ext_rel in &extracted.relationships {
418 if let (Some(&src_id), Some(&tgt_id)) = (
419 entity_map.get(&ext_rel.source),
420 entity_map.get(&ext_rel.target),
421 ) {
422 let rel = Relationship::new(src_id, &ext_rel.relation, tgt_id, scope.clone());
423 self.graph_store.upsert_relationship(&rel).await?;
424 }
425 }
426
427 fact_ids.push(id);
428 }
429
430 Ok(fact_ids)
431 }
432
433 pub fn fact_store(&self) -> &Arc<dyn FactStore> {
439 &self.fact_store
440 }
441
442 pub fn vector_store(&self) -> &Arc<dyn VectorStore> {
444 &self.vector_store
445 }
446
447 pub fn graph_store(&self) -> &Arc<dyn GraphStore> {
449 &self.graph_store
450 }
451}