microscope_memory/
self_reflect.rs1use std::fs;
7use std::path::Path;
8use std::time::{SystemTime, UNIX_EPOCH};
9
10use crate::archetype::ArchetypeState;
11use crate::attention::AttentionState;
12use crate::config::Config;
13use crate::emotional_state::EmotionalStateRing;
14use crate::hebbian::HebbianState;
15use crate::narrative::NarrativeState;
16use crate::reader::MicroscopeReader;
17use crate::spaced_repetition::SpacedRepetition;
18use crate::thought_graph::ThoughtGraphState;
19use colored::Colorize;
20
21pub const AUTO_REFLECT_INTERVAL: usize = 5;
22const MAX_REFLECTION_BYTES: usize = 2048;
23
24pub struct ReflectionState {
25 pub last_reflection_ms: u64,
26 pub total_reflections: u64,
27 pub last_reflection_text: String,
28}
29
30impl ReflectionState {
31 pub fn load_or_init(output_dir: &Path) -> Self {
32 let path = output_dir.join("self_reflect.bin");
33 if path.exists() {
34 if let Ok(data) = fs::read(&path) {
35 if data.len() >= 14 && &data[0..4] == b"SELF" {
36 let ts = u64::from_le_bytes(data[4..12].try_into().unwrap_or([0; 8]));
37 let count = u64::from_le_bytes(data[12..20].try_into().unwrap_or([0; 8]));
38 let rlen =
39 u16::from_le_bytes(data[20..22].try_into().unwrap_or([0; 2])) as usize;
40 let text = if rlen > 0 && 22 + rlen <= data.len() {
41 String::from_utf8_lossy(&data[22..22 + rlen]).to_string()
42 } else {
43 String::new()
44 };
45 return Self {
46 last_reflection_ms: ts,
47 total_reflections: count,
48 last_reflection_text: text,
49 };
50 }
51 }
52 }
53 Self {
54 last_reflection_ms: 0,
55 total_reflections: 0,
56 last_reflection_text: String::new(),
57 }
58 }
59
60 pub fn save(&self, output_dir: &Path) -> Result<(), String> {
61 let path = output_dir.join("self_reflect.bin");
62 let text_bytes = self.last_reflection_text.as_bytes();
63 let rlen = text_bytes.len().min(MAX_REFLECTION_BYTES) as u16;
64 let mut buf = Vec::with_capacity(22 + rlen as usize);
65 buf.extend_from_slice(b"SELF");
66 buf.extend_from_slice(&self.last_reflection_ms.to_le_bytes());
67 buf.extend_from_slice(&self.total_reflections.to_le_bytes());
68 buf.extend_from_slice(&rlen.to_le_bytes());
69 buf.extend_from_slice(&text_bytes[..rlen as usize]);
70 let tmp_path = output_dir.join("self_reflect.bin.tmp");
71 fs::write(&tmp_path, &buf).map_err(|e| format!("write self_reflect.bin: {}", e))?;
72 fs::rename(&tmp_path, &path).map_err(|e| format!("rename self_reflect.bin: {}", e))
73 }
74}
75
76pub fn introspect(_config: &Config, reader: &MicroscopeReader, output_dir: &Path) -> String {
77 let now_ms = SystemTime::now()
78 .duration_since(UNIX_EPOCH)
79 .unwrap_or_default()
80 .as_millis() as u64;
81
82 let hebb = HebbianState::load_or_init(output_dir, reader.block_count);
83 let attention = AttentionState::load_or_init(output_dir);
84 let archetypes = ArchetypeState::load_or_init(output_dir);
85 let narrative = NarrativeState::load_or_init(output_dir);
86 let esr = EmotionalStateRing::load_or_init(output_dir);
87 let spaced = SpacedRepetition::load_or_init(output_dir);
88 let thought_graph = ThoughtGraphState::load_or_init(output_dir);
89 let reflection = ReflectionState::load_or_init(output_dir);
90
91 let mut parts: Vec<String> = Vec::new();
92
93 if esr.is_active() {
95 let labels = crate::reader::EMOTION_DIMS;
96 let mut emotions: Vec<(usize, f32)> = esr
97 .current
98 .iter()
99 .enumerate()
100 .map(|(i, &v)| (i, v))
101 .collect();
102 emotions.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
103 let top: Vec<String> = emotions
104 .iter()
105 .take(3)
106 .filter(|(_, v)| *v > 0.1)
107 .map(|(i, v)| {
108 let intensity = if *v > 0.6 {
109 "strongly "
110 } else if *v > 0.3 {
111 ""
112 } else {
113 "slightly "
114 };
115 format!("{}{}", intensity, labels.get(*i).unwrap_or(&"?"))
116 })
117 .collect();
118 if !top.is_empty() {
119 parts.push(format!("I feel {}", top.join(" and ")));
120 }
121 }
122
123 let layer_names = [
125 "Hebbian",
126 "Mirror",
127 "Resonance",
128 "Archetype",
129 "Emotional",
130 "ThoughtGraph",
131 "PredictiveCache",
132 ];
133 let mut layers: Vec<(usize, f32)> = attention
134 .learned_weights
135 .iter()
136 .enumerate()
137 .map(|(i, &w)| (i, w))
138 .collect();
139 layers.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
140 if let Some((best_idx, best_w)) = layers.first() {
141 let name = layer_names.get(*best_idx).unwrap_or(&"?");
142 parts.push(format!(
143 "my {} layer is most active ({:.0}%)",
144 name,
145 best_w * 100.0
146 ));
147 }
148
149 let hot_count = hebb.activations.iter().filter(|a| a.energy > 0.1).count();
151 let total_energy: f32 = hebb.activations.iter().map(|a| a.energy).sum();
152 if hot_count > 0 {
153 parts.push(format!(
154 "{} hot memories (energy={:.1})",
155 hot_count, total_energy
156 ));
157 }
158
159 if !archetypes.archetypes.is_empty() {
161 let arch_labels: Vec<&str> = archetypes
162 .archetypes
163 .iter()
164 .take(3)
165 .map(|a| a.label.as_str())
166 .collect();
167 parts.push(format!(
168 "{} archetypes: {}",
169 archetypes.archetypes.len(),
170 arch_labels.join(", ")
171 ));
172 }
173
174 let due = spaced.due_count();
176 let mastered = spaced.mastered_count();
177 if due > 0 || mastered > 0 {
178 parts.push(format!("{} memories due, {} mastered", due, mastered));
179 }
180
181 let patterns = thought_graph.crystallized_count();
183 if patterns > 0 {
184 parts.push(format!("{} thought patterns crystallized", patterns));
185 }
186
187 parts.push(format!("{} total blocks", reader.block_count));
189
190 if reflection.total_reflections > 0 && !reflection.last_reflection_text.is_empty() {
192 let prev = crate::safe_truncate(&reflection.last_reflection_text, 60);
193 parts.push(format!("previously I reflected: \"{}\"", prev));
194 }
195
196 if narrative.session_count > 0 {
198 parts.push(format!(
199 "this is my {}th interaction",
200 narrative.session_count
201 ));
202 }
203
204 let reflection_text = if parts.is_empty() {
205 "I am aware of myself but have no distinct feelings right now.".to_string()
206 } else {
207 parts.join(". ") + "."
208 };
209
210 let mut state = ReflectionState::load_or_init(output_dir);
211 state.last_reflection_ms = now_ms;
212 state.total_reflections += 1;
213 state.last_reflection_text = reflection_text.clone();
214 let _ = state.save(output_dir);
215
216 reflection_text
217}
218
219pub fn format_reflection(text: &str) -> String {
220 format!(" {} {}", "SELF:".cyan().bold(), text)
221}