ceylon_next/memory/advanced/
manager.rs1use super::{
4 consolidation::{Consolidator, ConsolidationTask},
5 episodic::{EpisodicMemory, TimeRange},
6 semantic::SemanticMemory,
7 summarization::{Summarizer, SummaryStrategy},
8 working::WorkingMemory,
9 EnhancedMemoryEntry, ImportanceLevel, MemoryConfig, MemoryType,
10};
11use crate::memory::{Memory, MemoryEntry};
12use async_trait::async_trait;
13use std::sync::Arc;
14
15pub struct AdvancedMemoryManager {
24 backend: Arc<dyn Memory>,
26 working: WorkingMemory,
28 episodic: EpisodicMemory,
30 semantic: SemanticMemory,
32 summarizer: Summarizer,
34 consolidator: Consolidator,
36 config: MemoryConfig,
38}
39
40impl AdvancedMemoryManager {
41 pub async fn new(
43 backend: Arc<dyn Memory>,
44 config: MemoryConfig,
45 ) -> Result<Self, String> {
46 let working = WorkingMemory::new(config.clone());
47 let episodic = EpisodicMemory::new(backend.clone(), config.clone()).await?;
48 let semantic = SemanticMemory::new();
49 let summarizer = Summarizer::new();
50 let consolidator = Consolidator::new(config.clone());
51
52 Ok(Self {
53 backend,
54 working,
55 episodic,
56 semantic,
57 summarizer,
58 consolidator,
59 config,
60 })
61 }
62
63 pub async fn store_conversation(&self, entry: MemoryEntry) -> Result<String, String> {
69 let mut enhanced = EnhancedMemoryEntry::new(entry, MemoryType::Working);
71
72 if self.config.auto_summarize && enhanced.entry.messages.len() >= self.config.min_summary_length {
74 let summary = self
75 .summarizer
76 .summarize(&enhanced, SummaryStrategy::Hybrid)
77 .await?;
78
79 enhanced.summary = Some(summary.summary);
80 enhanced.key_points = summary.key_points;
81 enhanced.importance = summary.importance;
82
83 enhanced.entities = summary.entities.iter().map(|e| e.name.clone()).collect();
85
86 for entity in summary.entities {
88 self.semantic
89 .add_entity(entity.name, entity.entity_type, enhanced.entry.id.clone())
90 .await;
91 }
92
93 for fact in summary.facts {
95 self.semantic
96 .add_fact(fact.subject, fact.predicate, fact.object, enhanced.entry.id.clone())
97 .await;
98 }
99 }
100
101 self.working.add(enhanced.clone()).await?;
103
104 let id = self.episodic.store(enhanced).await?;
106
107 Ok(id)
108 }
109
110 pub async fn get(&self, id: &str) -> Result<Option<EnhancedMemoryEntry>, String> {
112 self.episodic.get(id).await
113 }
114
115 pub async fn get_working_memory(&self, max_tokens: Option<usize>) -> Vec<EnhancedMemoryEntry> {
117 if let Some(limit) = max_tokens {
118 self.working.get_recent_within_limit(limit).await
119 } else {
120 self.working.get_all().await
121 }
122 }
123
124 pub async fn get_episodic_by_time(
126 &self,
127 agent_id: &str,
128 range: TimeRange,
129 ) -> Result<Vec<EnhancedMemoryEntry>, String> {
130 self.episodic.get_by_time_range(agent_id, range).await
131 }
132
133 pub async fn get_relevant_memories(
135 &self,
136 agent_id: &str,
137 min_score: f32,
138 limit: usize,
139 ) -> Result<Vec<EnhancedMemoryEntry>, String> {
140 self.episodic.get_by_relevance(agent_id, min_score, limit).await
141 }
142
143 pub async fn search(&self, agent_id: &str, query: &str) -> Result<Vec<EnhancedMemoryEntry>, String> {
145 self.episodic.search(agent_id, query).await
146 }
147
148 pub async fn get_knowledge(&self, entity_name: &str) -> Option<super::semantic::KnowledgeSummary> {
154 self.semantic.get_knowledge_summary(entity_name).await
155 }
156
157 pub async fn get_facts_about(&self, subject: &str) -> Vec<super::semantic::Fact> {
159 self.semantic.get_facts_about(subject).await
160 }
161
162 pub async fn get_semantic_stats(&self) -> super::semantic::SemanticMemoryStats {
164 self.semantic.get_statistics().await
165 }
166
167 pub async fn create_agent_context(
173 &self,
174 agent_id: &str,
175 max_tokens: Option<usize>,
176 include_semantic: bool,
177 ) -> Result<String, String> {
178 let mut context = String::new();
179
180 let working_context = self.working.create_context(max_tokens).await;
182 if !working_context.is_empty() {
183 context.push_str(&working_context);
184 context.push_str("\n");
185 }
186
187 let important_memories = self
189 .episodic
190 .get_by_importance(agent_id, ImportanceLevel::High)
191 .await?;
192
193 if !important_memories.is_empty() {
194 context.push_str("IMPORTANT PAST CONVERSATIONS:\n");
195 for (i, memory) in important_memories.iter().take(3).enumerate() {
196 if let Some(summary) = &memory.summary {
197 context.push_str(&format!("{}. {}\n", i + 1, summary));
198 }
199 }
200 context.push_str("\n");
201 }
202
203 if include_semantic {
205 let stats = self.semantic.get_statistics().await;
206 if stats.total_entities > 0 || stats.total_facts > 0 {
207 context.push_str("LEARNED KNOWLEDGE:\n");
208 context.push_str(&format!("- {} entities tracked\n", stats.total_entities));
209 context.push_str(&format!("- {} facts stored\n", stats.total_facts));
210
211 let high_conf_facts = self.semantic.get_high_confidence_facts(0.8).await;
213 if !high_conf_facts.is_empty() {
214 context.push_str("Key facts:\n");
215 for fact in high_conf_facts.iter().take(5) {
216 context.push_str(&format!(
217 " - {} {} {}\n",
218 fact.subject, fact.predicate, fact.object
219 ));
220 }
221 }
222 context.push_str("\n");
223 }
224 }
225
226 Ok(context)
227 }
228
229 pub async fn consolidate(&self, task: ConsolidationTask) -> Result<super::consolidation::ConsolidationResult, String> {
235 let memories = self.episodic.get_by_time_range("", TimeRange::All).await?;
237
238 let mut mem_map = std::collections::HashMap::new();
240 for memory in memories {
241 mem_map.insert(memory.entry.id.clone(), memory);
242 }
243
244 self.consolidator.set_memories(mem_map).await;
245
246 let result = self.consolidator.consolidate(task).await?;
248
249 Ok(result)
253 }
254
255 pub async fn start_background_processing(&self) {
257 if self.config.auto_consolidate {
258 self.consolidator.start_background_consolidation().await;
259 }
260 }
261
262 pub async fn stop_background_processing(&self) {
264 self.consolidator.stop_background_consolidation().await;
265 }
266
267 pub async fn get_statistics(&self) -> MemoryStatistics {
269 let semantic_stats = self.semantic.get_statistics().await;
270
271 MemoryStatistics {
272 working_memory_count: self.working.memory_count().await,
273 working_memory_tokens: self.working.token_count().await,
274 semantic_entities: semantic_stats.total_entities,
275 semantic_facts: semantic_stats.total_facts,
276 semantic_relationships: semantic_stats.total_relationships,
277 }
278 }
279
280 pub async fn clear_agent_memory(&self, agent_id: &str) -> Result<(), String> {
282 self.working.clear().await;
283 self.episodic.clear(agent_id).await?;
284 Ok(())
285 }
286}
287
288#[async_trait]
290impl Memory for AdvancedMemoryManager {
291 async fn store(&self, entry: MemoryEntry) -> Result<String, String> {
292 self.store_conversation(entry).await
293 }
294
295 async fn get(&self, id: &str) -> Result<Option<MemoryEntry>, String> {
296 if let Some(enhanced) = self.get(id).await? {
297 Ok(Some(enhanced.entry))
298 } else {
299 Ok(None)
300 }
301 }
302
303 async fn get_agent_history(&self, agent_id: &str) -> Result<Vec<MemoryEntry>, String> {
304 let enhanced_entries = self
305 .episodic
306 .get_by_time_range(agent_id, TimeRange::All)
307 .await?;
308
309 Ok(enhanced_entries.into_iter().map(|e| e.entry).collect())
310 }
311
312 async fn get_recent(&self, agent_id: &str, limit: usize) -> Result<Vec<MemoryEntry>, String> {
313 let enhanced_entries = self
314 .episodic
315 .get_by_relevance(agent_id, 0.0, limit)
316 .await?;
317
318 Ok(enhanced_entries.into_iter().map(|e| e.entry).collect())
319 }
320
321 async fn search(&self, agent_id: &str, query: &str) -> Result<Vec<MemoryEntry>, String> {
322 let enhanced_entries = self.search(agent_id, query).await?;
323 Ok(enhanced_entries.into_iter().map(|e| e.entry).collect())
324 }
325
326 async fn clear_agent_memory(&self, agent_id: &str) -> Result<(), String> {
327 self.clear_agent_memory(agent_id).await
328 }
329}
330
331#[derive(Debug, Clone)]
333pub struct MemoryStatistics {
334 pub working_memory_count: usize,
335 pub working_memory_tokens: usize,
336 pub semantic_entities: usize,
337 pub semantic_facts: usize,
338 pub semantic_relationships: usize,
339}
340
341#[cfg(test)]
342mod tests {
343 use super::*;
344 use crate::memory::InMemoryStore;
345 use crate::llm::types::Message;
346
347 #[tokio::test]
348 async fn test_advanced_memory_manager_basic() {
349 let backend = Arc::new(InMemoryStore::new());
350 let config = MemoryConfig {
351 auto_consolidate: false, ..Default::default()
353 };
354 let manager = AdvancedMemoryManager::new(backend, config).await.unwrap();
355
356 let messages = vec![
358 Message {
359 role: "user".to_string(),
360 content: "Hello, tell me about Rust.".to_string(),
361 },
362 Message {
363 role: "assistant".to_string(),
364 content: "Rust is a systems programming language.".to_string(),
365 },
366 ];
367
368 let entry = MemoryEntry::new("agent-1".to_string(), "task-1".to_string(), messages);
369 let id = manager.store_conversation(entry).await.unwrap();
370
371 let retrieved = manager.get(&id).await.unwrap();
373 assert!(retrieved.is_some());
374 }
375
376 #[tokio::test]
377 async fn test_context_generation() {
378 let backend = Arc::new(InMemoryStore::new());
379 let config = MemoryConfig {
380 auto_consolidate: false, ..Default::default()
382 };
383 let manager = AdvancedMemoryManager::new(backend, config).await.unwrap();
384
385 for i in 0..3 {
387 let messages = vec![Message {
388 role: "user".to_string(),
389 content: format!("Message {}", i),
390 }];
391
392 let entry = MemoryEntry::new(
393 "agent-1".to_string(),
394 format!("task-{}", i),
395 messages,
396 );
397 manager.store_conversation(entry).await.unwrap();
398 }
399
400 let context = manager
402 .create_agent_context("agent-1", None, true)
403 .await
404 .unwrap();
405
406 assert!(!context.is_empty());
407 }
408}