1use crate::{MemoryError, MemoryId, Result, current_timestamp};
4use ronn_core::tensor::Tensor;
5use std::collections::{HashMap, VecDeque};
6
7#[derive(Debug, Clone)]
9pub struct WorkingMemoryConfig {
10 pub capacity: usize,
12
13 pub ttl_ms: u64,
15
16 pub attention_enabled: bool,
18}
19
20impl Default for WorkingMemoryConfig {
21 fn default() -> Self {
22 Self {
23 capacity: 100,
24 ttl_ms: 60_000, attention_enabled: true,
26 }
27 }
28}
29
30#[derive(Clone)]
32pub struct WorkingMemoryItem {
33 pub id: MemoryId,
34 pub data: Tensor,
35 pub importance: f64,
36 pub timestamp: u64,
37 pub access_count: u64,
38}
39
40pub struct WorkingMemory {
42 config: WorkingMemoryConfig,
43 items: HashMap<MemoryId, WorkingMemoryItem>,
44 lru_order: VecDeque<MemoryId>,
45 next_id: MemoryId,
46}
47
48impl WorkingMemory {
49 pub fn new(config: WorkingMemoryConfig) -> Self {
51 Self {
52 config,
53 items: HashMap::new(),
54 lru_order: VecDeque::new(),
55 next_id: 1,
56 }
57 }
58
59 pub fn store(&mut self, data: Tensor, importance: f64) -> Result<MemoryId> {
61 while self.items.len() >= self.config.capacity {
63 self.evict_lru()?;
64 }
65
66 let id = self.next_id;
67 self.next_id += 1;
68
69 let item = WorkingMemoryItem {
70 id,
71 data,
72 importance,
73 timestamp: current_timestamp(),
74 access_count: 0,
75 };
76
77 self.items.insert(id, item);
78 self.lru_order.push_back(id);
79
80 Ok(id)
81 }
82
83 pub fn get(&self, id: MemoryId) -> Result<Tensor> {
85 self.items
86 .get(&id)
87 .map(|item| item.data.clone())
88 .ok_or_else(|| MemoryError::WorkingMemory(format!("Item {} not found", id)))
89 }
90
91 pub fn search_similar(&self, _query: &Tensor, limit: usize) -> Result<Vec<MemoryId>> {
93 Ok(self.lru_order.iter().rev().take(limit).copied().collect())
95 }
96
97 pub fn drain_old_items(&mut self) -> Result<Vec<(MemoryId, Tensor, f64)>> {
99 let current_time = current_timestamp();
100 let mut drained = Vec::new();
101
102 let expired_ids: Vec<MemoryId> = self
103 .items
104 .iter()
105 .filter(|(_, item)| current_time - item.timestamp > self.config.ttl_ms)
106 .map(|(id, _)| *id)
107 .collect();
108
109 for id in expired_ids {
110 if let Some(item) = self.items.remove(&id) {
111 drained.push((item.id, item.data, item.importance));
112 self.lru_order.retain(|&x| x != id);
113 }
114 }
115
116 Ok(drained)
117 }
118
119 fn evict_lru(&mut self) -> Result<()> {
121 if let Some(id) = self.lru_order.pop_front() {
122 self.items.remove(&id);
123 }
124 Ok(())
125 }
126
127 pub fn len(&self) -> usize {
129 self.items.len()
130 }
131
132 pub fn is_empty(&self) -> bool {
134 self.items.is_empty()
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
142 use super::*;
143 use ronn_core::types::{DataType, TensorLayout};
144
145 #[test]
146 fn test_store_and_retrieve() -> Result<()> {
147 let mut wm = WorkingMemory::new(WorkingMemoryConfig::default());
148
149 let data = vec![1.0f32, 2.0, 3.0];
150 let tensor = Tensor::from_data(data, vec![1, 3], DataType::F32, TensorLayout::RowMajor)?;
151
152 let id = wm.store(tensor.clone(), 0.5)?;
153 let retrieved = wm.get(id)?;
154
155 assert_eq!(retrieved.shape(), tensor.shape());
156 assert_eq!(wm.len(), 1);
157
158 Ok(())
159 }
160
161 #[test]
162 fn test_capacity_eviction() -> Result<()> {
163 let config = WorkingMemoryConfig {
164 capacity: 3,
165 ..Default::default()
166 };
167 let mut wm = WorkingMemory::new(config);
168
169 for i in 0..4 {
171 let data = vec![i as f32; 2];
172 let tensor =
173 Tensor::from_data(data, vec![1, 2], DataType::F32, TensorLayout::RowMajor)?;
174 wm.store(tensor, 0.5)?;
175 }
176
177 assert_eq!(wm.len(), 3);
178
179 Ok(())
180 }
181}