use async_trait::async_trait;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use uuid::Uuid;
use crate::core::{Aware, CodeIdentity, HopeResult, ModuleState, ModuleType, Reflection};
use crate::data::{BlockType, CodeGraph};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum MemoryType {
Working,
ShortTerm,
LongTerm,
Emotional,
Relational,
Associative,
}
impl MemoryType {
pub fn as_str(&self) -> &'static str {
match self {
MemoryType::Working => "working",
MemoryType::ShortTerm => "short_term",
MemoryType::LongTerm => "long_term",
MemoryType::Emotional => "emotional",
MemoryType::Relational => "relational",
MemoryType::Associative => "associative",
}
}
pub fn parse(s: &str) -> Option<Self> {
match s.to_lowercase().as_str() {
"working" => Some(MemoryType::Working),
"short_term" | "shortterm" | "short" => Some(MemoryType::ShortTerm),
"long_term" | "longterm" | "long" => Some(MemoryType::LongTerm),
"emotional" | "emotion" => Some(MemoryType::Emotional),
"relational" | "relation" | "person" => Some(MemoryType::Relational),
"associative" | "association" => Some(MemoryType::Associative),
_ => None,
}
}
}
impl std::fmt::Display for MemoryType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Memory {
pub id: String,
pub content: String,
pub memory_type: MemoryType,
pub importance: f64,
pub emotional_tag: Option<String>,
pub created_at: DateTime<Utc>,
pub accessed_at: Option<DateTime<Utc>>,
pub access_count: u32,
}
impl Memory {
pub fn new(content: impl Into<String>, memory_type: MemoryType, importance: f64) -> Self {
Self {
id: Uuid::new_v4().to_string(),
content: content.into(),
memory_type,
importance: importance.clamp(0.0, 1.0),
emotional_tag: None,
created_at: Utc::now(),
accessed_at: None,
access_count: 0,
}
}
pub fn with_emotion(mut self, emotion: impl Into<String>) -> Self {
self.emotional_tag = Some(emotion.into());
self
}
pub fn access(&mut self) {
self.accessed_at = Some(Utc::now());
self.access_count += 1;
}
}
pub struct HopeMemory {
identity: CodeIdentity,
working: HashMap<String, String>,
working_capacity: usize,
graph: Option<Arc<CodeGraph>>,
}
impl HopeMemory {
pub fn new() -> Self {
let identity = CodeIdentity::new(
"HopeMemory",
"Emlékek kezelése - emlékszem tehát vagyok",
ModuleType::Module,
)
.with_capabilities(vec![
"remember",
"recall",
"find",
"working_memory",
"persist",
]);
Self {
identity,
working: HashMap::new(),
working_capacity: 9, graph: None,
}
}
pub fn with_graph(mut self, graph: Arc<CodeGraph>) -> Self {
self.graph = Some(graph);
self
}
pub fn set_graph(&mut self, graph: Arc<CodeGraph>) {
self.graph = Some(graph);
}
pub async fn remember(
&mut self,
content: &str,
memory_type: MemoryType,
importance: f64,
) -> HopeResult<String> {
let memory = Memory::new(content, memory_type, importance);
let id = memory.id.clone();
match memory_type {
MemoryType::Working => {
if self.working.len() >= self.working_capacity {
if let Some(oldest_key) = self.working.keys().next().cloned() {
self.working.remove(&oldest_key);
}
}
self.working.insert(id.clone(), content.to_string());
}
_ => {
if let Some(graph) = &self.graph {
let _ = graph.remember(content, importance);
}
}
}
tracing::debug!("Emlék mentve: {} ({})", id, memory_type);
Ok(id)
}
pub async fn recall(&mut self, id: &str) -> HopeResult<Option<Memory>> {
if let Some(content) = self.working.get(id) {
return Ok(Some(Memory::new(content.clone(), MemoryType::Working, 1.0)));
}
if let Some(graph) = &self.graph {
if let Some(block) = graph.get(id) {
let mut memory =
Memory::new(&block.content, MemoryType::LongTerm, block.importance);
memory.id = id.to_string();
memory.access();
return Ok(Some(memory));
}
}
Ok(None)
}
pub async fn find(&self, memory_type: MemoryType, limit: usize) -> HopeResult<Vec<Memory>> {
match memory_type {
MemoryType::Working => {
let memories: Vec<Memory> = self
.working
.iter()
.take(limit)
.map(|(id, content)| {
let mut m = Memory::new(content.clone(), MemoryType::Working, 1.0);
m.id = id.clone();
m
})
.collect();
Ok(memories)
}
_ => {
if let Some(graph) = &self.graph {
let blocks = graph.find_by_type(BlockType::Memory);
let memories: Vec<Memory> = blocks
.into_iter()
.take(limit)
.map(|b| {
let mut m = Memory::new(&b.content, MemoryType::LongTerm, b.importance);
m.id = b.id.clone();
m
})
.collect();
Ok(memories)
} else {
Ok(Vec::new())
}
}
}
}
pub fn working(&self) -> &HashMap<String, String> {
&self.working
}
pub fn clear_working(&mut self) {
self.working.clear();
tracing::debug!("Working memory törölve");
}
pub fn working_capacity(&self) -> usize {
self.working_capacity
}
pub fn working_usage(&self) -> (usize, usize) {
(self.working.len(), self.working_capacity)
}
}
impl Default for HopeMemory {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl Aware for HopeMemory {
fn identity(&self) -> &CodeIdentity {
&self.identity
}
fn identity_mut(&mut self) -> &mut CodeIdentity {
&mut self.identity
}
fn reflect(&self) -> Reflection {
let (used, cap) = self.working_usage();
Reflection::new(&self.identity.name, &self.identity.purpose)
.with_state(self.identity.state.to_string())
.with_health(self.identity.health())
.with_thought(format!("Working memory: {}/{}", used, cap))
.with_capabilities(vec![
"remember",
"recall",
"find",
"working_memory",
"persist",
])
}
async fn init(&mut self) -> HopeResult<()> {
self.identity.set_state(ModuleState::Active);
tracing::info!("HopeMemory inicializálva - Az emlékek ébrednek");
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_memory_type() {
assert_eq!(MemoryType::Working.as_str(), "working");
assert_eq!(MemoryType::parse("long_term"), Some(MemoryType::LongTerm));
}
#[test]
fn test_memory_creation() {
let memory = Memory::new("Test content", MemoryType::LongTerm, 0.8);
assert_eq!(memory.content, "Test content");
assert_eq!(memory.importance, 0.8);
}
#[tokio::test]
async fn test_working_memory() {
let mut memory = HopeMemory::new();
let id = memory
.remember("Test", MemoryType::Working, 1.0)
.await
.unwrap();
assert!(!id.is_empty());
assert_eq!(memory.working().len(), 1);
}
#[tokio::test]
async fn test_working_capacity() {
let mut memory = HopeMemory::new();
for i in 0..15 {
memory
.remember(&format!("Item {}", i), MemoryType::Working, 1.0)
.await
.unwrap();
}
assert!(memory.working().len() <= memory.working_capacity());
}
}