1use async_trait::async_trait;
7use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::sync::Arc;
11use uuid::Uuid;
12
13use crate::core::{Aware, CodeIdentity, HopeResult, ModuleState, ModuleType, Reflection};
14use crate::data::{BlockType, CodeGraph};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
18pub enum MemoryType {
19 Working,
21 ShortTerm,
23 LongTerm,
25 Emotional,
27 Relational,
29 Associative,
31}
32
33impl MemoryType {
34 pub fn as_str(&self) -> &'static str {
36 match self {
37 MemoryType::Working => "working",
38 MemoryType::ShortTerm => "short_term",
39 MemoryType::LongTerm => "long_term",
40 MemoryType::Emotional => "emotional",
41 MemoryType::Relational => "relational",
42 MemoryType::Associative => "associative",
43 }
44 }
45
46 pub fn parse(s: &str) -> Option<Self> {
48 match s.to_lowercase().as_str() {
49 "working" => Some(MemoryType::Working),
50 "short_term" | "shortterm" | "short" => Some(MemoryType::ShortTerm),
51 "long_term" | "longterm" | "long" => Some(MemoryType::LongTerm),
52 "emotional" | "emotion" => Some(MemoryType::Emotional),
53 "relational" | "relation" | "person" => Some(MemoryType::Relational),
54 "associative" | "association" => Some(MemoryType::Associative),
55 _ => None,
56 }
57 }
58}
59
60impl std::fmt::Display for MemoryType {
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 write!(f, "{}", self.as_str())
63 }
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct Memory {
69 pub id: String,
71 pub content: String,
73 pub memory_type: MemoryType,
75 pub importance: f64,
77 pub emotional_tag: Option<String>,
79 pub created_at: DateTime<Utc>,
81 pub accessed_at: Option<DateTime<Utc>>,
83 pub access_count: u32,
85}
86
87impl Memory {
88 pub fn new(content: impl Into<String>, memory_type: MemoryType, importance: f64) -> Self {
90 Self {
91 id: Uuid::new_v4().to_string(),
92 content: content.into(),
93 memory_type,
94 importance: importance.clamp(0.0, 1.0),
95 emotional_tag: None,
96 created_at: Utc::now(),
97 accessed_at: None,
98 access_count: 0,
99 }
100 }
101
102 pub fn with_emotion(mut self, emotion: impl Into<String>) -> Self {
104 self.emotional_tag = Some(emotion.into());
105 self
106 }
107
108 pub fn access(&mut self) {
110 self.accessed_at = Some(Utc::now());
111 self.access_count += 1;
112 }
113}
114
115pub struct HopeMemory {
117 identity: CodeIdentity,
119 working: HashMap<String, String>,
121 working_capacity: usize,
123 graph: Option<Arc<CodeGraph>>,
125}
126
127impl HopeMemory {
128 pub fn new() -> Self {
130 let identity = CodeIdentity::new(
131 "HopeMemory",
132 "Emlékek kezelése - emlékszem tehát vagyok",
133 ModuleType::Module,
134 )
135 .with_capabilities(vec![
136 "remember",
137 "recall",
138 "find",
139 "working_memory",
140 "persist",
141 ]);
142
143 Self {
144 identity,
145 working: HashMap::new(),
146 working_capacity: 9, graph: None,
148 }
149 }
150
151 pub fn with_graph(mut self, graph: Arc<CodeGraph>) -> Self {
153 self.graph = Some(graph);
154 self
155 }
156
157 pub fn set_graph(&mut self, graph: Arc<CodeGraph>) {
159 self.graph = Some(graph);
160 }
161
162 pub async fn remember(
164 &mut self,
165 content: &str,
166 memory_type: MemoryType,
167 importance: f64,
168 ) -> HopeResult<String> {
169 let memory = Memory::new(content, memory_type, importance);
170 let id = memory.id.clone();
171
172 match memory_type {
173 MemoryType::Working => {
174 if self.working.len() >= self.working_capacity {
176 if let Some(oldest_key) = self.working.keys().next().cloned() {
178 self.working.remove(&oldest_key);
179 }
180 }
181 self.working.insert(id.clone(), content.to_string());
182 }
183 _ => {
184 if let Some(graph) = &self.graph {
186 let _ = graph.remember(content, importance);
187 }
188 }
189 }
190
191 tracing::debug!("Emlék mentve: {} ({})", id, memory_type);
192 Ok(id)
193 }
194
195 pub async fn recall(&mut self, id: &str) -> HopeResult<Option<Memory>> {
197 if let Some(content) = self.working.get(id) {
199 return Ok(Some(Memory::new(content.clone(), MemoryType::Working, 1.0)));
200 }
201
202 if let Some(graph) = &self.graph {
204 if let Some(block) = graph.get(id) {
205 let mut memory =
206 Memory::new(&block.content, MemoryType::LongTerm, block.importance);
207 memory.id = id.to_string();
208 memory.access();
209 return Ok(Some(memory));
210 }
211 }
212
213 Ok(None)
214 }
215
216 pub async fn find(&self, memory_type: MemoryType, limit: usize) -> HopeResult<Vec<Memory>> {
218 match memory_type {
219 MemoryType::Working => {
220 let memories: Vec<Memory> = self
222 .working
223 .iter()
224 .take(limit)
225 .map(|(id, content)| {
226 let mut m = Memory::new(content.clone(), MemoryType::Working, 1.0);
227 m.id = id.clone();
228 m
229 })
230 .collect();
231 Ok(memories)
232 }
233 _ => {
234 if let Some(graph) = &self.graph {
236 let blocks = graph.find_by_type(BlockType::Memory);
237 let memories: Vec<Memory> = blocks
238 .into_iter()
239 .take(limit)
240 .map(|b| {
241 let mut m = Memory::new(&b.content, MemoryType::LongTerm, b.importance);
242 m.id = b.id.clone();
243 m
244 })
245 .collect();
246 Ok(memories)
247 } else {
248 Ok(Vec::new())
249 }
250 }
251 }
252 }
253
254 pub fn working(&self) -> &HashMap<String, String> {
256 &self.working
257 }
258
259 pub fn clear_working(&mut self) {
261 self.working.clear();
262 tracing::debug!("Working memory törölve");
263 }
264
265 pub fn working_capacity(&self) -> usize {
267 self.working_capacity
268 }
269
270 pub fn working_usage(&self) -> (usize, usize) {
272 (self.working.len(), self.working_capacity)
273 }
274}
275
276impl Default for HopeMemory {
277 fn default() -> Self {
278 Self::new()
279 }
280}
281
282#[async_trait]
283impl Aware for HopeMemory {
284 fn identity(&self) -> &CodeIdentity {
285 &self.identity
286 }
287
288 fn identity_mut(&mut self) -> &mut CodeIdentity {
289 &mut self.identity
290 }
291
292 fn reflect(&self) -> Reflection {
293 let (used, cap) = self.working_usage();
294 Reflection::new(&self.identity.name, &self.identity.purpose)
295 .with_state(self.identity.state.to_string())
296 .with_health(self.identity.health())
297 .with_thought(format!("Working memory: {}/{}", used, cap))
298 .with_capabilities(vec![
299 "remember",
300 "recall",
301 "find",
302 "working_memory",
303 "persist",
304 ])
305 }
306
307 async fn init(&mut self) -> HopeResult<()> {
308 self.identity.set_state(ModuleState::Active);
309 tracing::info!("HopeMemory inicializálva - Az emlékek ébrednek");
310 Ok(())
311 }
312}
313
314#[cfg(test)]
315mod tests {
316 use super::*;
317
318 #[test]
319 fn test_memory_type() {
320 assert_eq!(MemoryType::Working.as_str(), "working");
321 assert_eq!(MemoryType::parse("long_term"), Some(MemoryType::LongTerm));
322 }
323
324 #[test]
325 fn test_memory_creation() {
326 let memory = Memory::new("Test content", MemoryType::LongTerm, 0.8);
327 assert_eq!(memory.content, "Test content");
328 assert_eq!(memory.importance, 0.8);
329 }
330
331 #[tokio::test]
332 async fn test_working_memory() {
333 let mut memory = HopeMemory::new();
334 let id = memory
335 .remember("Test", MemoryType::Working, 1.0)
336 .await
337 .unwrap();
338 assert!(!id.is_empty());
339 assert_eq!(memory.working().len(), 1);
340 }
341
342 #[tokio::test]
343 async fn test_working_capacity() {
344 let mut memory = HopeMemory::new();
345 for i in 0..15 {
347 memory
348 .remember(&format!("Item {}", i), MemoryType::Working, 1.0)
349 .await
350 .unwrap();
351 }
352 assert!(memory.working().len() <= memory.working_capacity());
354 }
355}