Thanks to visit codestin.com
Credit goes to docs.rs

Skip to main content

mimirs_sleeptime/
reflection.rs

1//! Sleep-time agent for memory reflection
2//!
3//! Implements Letta-inspired sleep-time agents that reflect on episodic history
4//! and produce distilled memory blocks.
5
6use mimirs_core::{Goal, MemoryAgent, MimirError, RecallQuery};
7use mimirs_oracle::{Forseti, Ginnunga};
8use std::sync::Weak;
9use std::time::Duration;
10
11/// Sleep-time agent configuration
12#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
13pub struct SleepConfig {
14    /// Minimum idle time before triggering sleep-time reflection
15    pub min_idle_duration: Duration,
16    /// Maximum number of memories to reflect on per session
17    pub max_memories_per_session: usize,
18    /// Whether to automatically consolidate after reflection
19    pub auto_consolidate: bool,
20}
21
22impl Default for SleepConfig {
23    fn default() -> Self {
24        Self {
25            min_idle_duration: Duration::from_secs(300), // 5 minutes
26            max_memories_per_session: 100,
27            auto_consolidate: true,
28        }
29    }
30}
31
32/// GoalSystem - Autonomous Goal Persistence (L6).
33#[derive(Debug, Clone, Default)]
34pub struct GoalSystem {
35    /// Active cognitive attractors.
36    pub active_goals: Vec<Goal>,
37}
38
39impl GoalSystem {
40    /// SelectFocus - Select the highest priority goal based on persistence and weight.
41    pub fn select_focus(&self) -> Option<&Goal> {
42        self.active_goals.iter().max_by(|a, b| {
43            let sa = a.priority * a.persistence;
44            let sb = b.priority * b.persistence;
45            debug_assert!(sa.is_finite() && sb.is_finite());
46            sa.partial_cmp(&sb).unwrap_or(std::cmp::Ordering::Equal)
47        })
48    }
49}
50
51/// Nott - The reflection engine.
52pub struct Nott {
53    /// The memory agent to operate on (Weak to prevent cycles)
54    well_agent: Weak<dyn MemoryAgent>,
55    /// Autonomous goal manager.
56    pub goals: GoalSystem,
57    /// Contradiction detector.
58    pub ginnunga: Ginnunga,
59    /// Verifiability oracle.
60    pub forseti: Forseti,
61    /// Configuration for sleep-time behavior
62    _config: SleepConfig,
63    /// Event bus for signaling (optional, can be attached later)
64    pub bus: Option<mimirs_ha::EventBus>,
65    /// Agent identifier for signal attribution
66    pub agent_id: Option<mimirs_core::AgentId>,
67    /// Distillation configuration
68    pub distillation_config: crate::distillation::DistillationConfig,
69}
70
71impl Nott {
72    /// Opna - Creates a new Nott sleep-time agent.
73    pub fn new(agent: Weak<dyn MemoryAgent>, config: SleepConfig) -> Self {
74        Self {
75            well_agent: agent,
76            goals: GoalSystem::default(),
77            ginnunga: Ginnunga::default(),
78            forseti: Forseti::default(),
79            _config: config,
80            bus: None,
81            agent_id: None,
82            distillation_config: crate::distillation::DistillationConfig::default(),
83        }
84    }
85
86    /// Attaches an event bus for real-time signaling.
87    pub fn with_bus(mut self, bus: mimirs_ha::EventBus) -> Self {
88        self.bus = Some(bus);
89        self
90    }
91
92    /// Attaches an agent ID for signal attribution.
93    pub fn with_agent_id(mut self, id: mimirs_core::AgentId) -> Self {
94        self.agent_id = Some(id);
95        self
96    }
97
98    /// Drauma - Start the reflection loop (dreaming).
99    /// Minimized L = αE + βC + δH (Entropy, Contradiction, Hallucination/Incoherence)
100    pub async fn drauma(&self) -> Result<(), MimirError> {
101        let agent = self
102            .well_agent
103            .upgrade()
104            .ok_or_else(|| MimirError::Uninitialized("Agent dropped".to_string()))?;
105
106        let agent_id = self.agent_id.unwrap_or_default();
107
108        tracing::info!("Nott is starting the reflection loop (Drauma)");
109
110        // 1. Fetch Episodic history
111        let query = RecallQuery::new("")
112            .with_limit(self._config.max_memories_per_session)
113            .with_memory_class(mimirs_core::MemoryClass::Episodic);
114
115        let results = agent.recall(query).await?;
116        if results.is_empty() {
117            tracing::debug!("No episodic memories to reflect on");
118            return Ok(());
119        }
120
121        // 2. Identify Contradictions (C) and evaluate Verifiability
122        for (i, res_a) in results.iter().enumerate() {
123            for res_b in results.iter().skip(i + 1) {
124                let contradiction_score = self.ginnunga.greina(&res_a.memory, &res_b.memory);
125                if contradiction_score > self.ginnunga.contradiction_threshold {
126                    tracing::warn!(
127                        "Contradiction detected between {} and {}: score {}",
128                        res_a.id,
129                        res_b.id,
130                        contradiction_score
131                    );
132
133                    // Emit conflict signal
134                    if let Some(ref bus) = self.bus {
135                        let _ = bus.broadcast(mimirs_ha::MemorySignal::ConflictDetected {
136                            agent_id,
137                            memory_a: res_a.id,
138                            memory_b: res_b.id,
139                            score: contradiction_score,
140                        });
141                    }
142                }
143            }
144
145            // 3. Evaluate and potentially promote verifiability stage
146            let mut current_memory = agent.get_memory(res_a.id).await?;
147            let new_stage = if let Some(ref rho) = current_memory.rho {
148                self.forseti.domur(rho)
149            } else {
150                current_memory.verifiability
151            };
152
153            if new_stage > current_memory.verifiability {
154                tracing::info!(
155                    "Promoting memory {} from {:?} to {:?}",
156                    res_a.id,
157                    current_memory.verifiability,
158                    new_stage
159                );
160
161                // Update memory in the well
162                current_memory.verifiability = new_stage;
163                agent.update_memory(current_memory).await?;
164
165                // Emit verifiability change signal
166                if let Some(ref bus) = self.bus {
167                    let _ = bus.broadcast(mimirs_ha::MemorySignal::VerifiabilityChanged {
168                        agent_id,
169                        memory_id: res_a.id,
170                        new_stage,
171                    });
172                }
173            }
174        }
175
176        // 4. Perform Manifold Distillation (Phase 9)
177        tracing::info!("Nott is distilling episodic manifold into Drauma blocks");
178        let mut full_memories = Vec::new();
179        for r in results.iter() {
180            if let Ok(m) = agent.get_memory(r.id).await {
181                full_memories.push(m);
182            }
183        }
184
185        match crate::distillation::Drauma::distill(&full_memories, &self.distillation_config) {
186            Ok(drauma) => {
187                tracing::info!(
188                    "Successfully distilled {} memories into block {}",
189                    full_memories.len(),
190                    drauma.id
191                );
192
193                // Save the distilled block to the agent's context
194                agent.attach_block(drauma.block).await?;
195
196                // Emit distillation signal
197                if let Some(ref bus) = self.bus {
198                    let _ = bus.broadcast(mimirs_ha::MemorySignal::ConsolidationCompleted {
199                        agent_id,
200                        memories_processed: full_memories.len(),
201                    });
202                }
203            }
204            Err(e) => tracing::error!("Distillation failed: {}", e),
205        }
206
207        // 5. Update Goal weights (simulated based on reflection results)
208        if let Some(focus) = self.goals.select_focus() {
209            tracing::info!("Current cognitive focus: {}", focus.id);
210        }
211
212        Ok(())
213    }
214}