rexis_rag/agent/memory/
working.rs1use crate::error::RragResult;
8use crate::storage::{Memory, MemoryValue};
9use std::sync::Arc;
10
11pub struct WorkingMemory {
13 storage: Arc<dyn Memory>,
15
16 namespace: String,
18
19 auto_clear: bool,
21}
22
23impl WorkingMemory {
24 pub fn new(storage: Arc<dyn Memory>, session_id: String) -> Self {
26 let namespace = format!("session::{}::working", session_id);
27
28 Self {
29 storage,
30 namespace,
31 auto_clear: true,
32 }
33 }
34
35 pub fn new_persistent(storage: Arc<dyn Memory>, session_id: String) -> Self {
37 let namespace = format!("session::{}::working", session_id);
38
39 Self {
40 storage,
41 namespace,
42 auto_clear: false,
43 }
44 }
45
46 pub async fn set(&self, key: &str, value: impl Into<MemoryValue>) -> RragResult<()> {
48 let full_key = self.make_key(key);
49 self.storage.set(&full_key, value.into()).await
50 }
51
52 pub async fn get(&self, key: &str) -> RragResult<Option<MemoryValue>> {
54 let full_key = self.make_key(key);
55 self.storage.get(&full_key).await
56 }
57
58 pub async fn delete(&self, key: &str) -> RragResult<bool> {
60 let full_key = self.make_key(key);
61 self.storage.delete(&full_key).await
62 }
63
64 pub async fn exists(&self, key: &str) -> RragResult<bool> {
66 let full_key = self.make_key(key);
67 self.storage.exists(&full_key).await
68 }
69
70 pub async fn clear(&self) -> RragResult<()> {
72 self.storage.clear(Some(&self.namespace)).await
73 }
74
75 pub async fn keys(&self) -> RragResult<Vec<String>> {
77 use crate::storage::MemoryQuery;
78
79 let query = MemoryQuery::new().with_namespace(self.namespace.clone());
80 let all_keys = self.storage.keys(&query).await?;
81
82 let prefix = format!("{}::", self.namespace);
84 let keys = all_keys
85 .into_iter()
86 .filter_map(|k| k.strip_prefix(&prefix).map(String::from))
87 .collect();
88
89 Ok(keys)
90 }
91
92 pub async fn set_many(&self, pairs: &[(&str, MemoryValue)]) -> RragResult<()> {
94 let full_pairs: Vec<(String, MemoryValue)> = pairs
95 .iter()
96 .map(|(k, v)| (self.make_key(k), v.clone()))
97 .collect();
98
99 self.storage.mset(&full_pairs).await
100 }
101
102 pub async fn get_many(&self, keys: &[&str]) -> RragResult<Vec<Option<MemoryValue>>> {
104 let full_keys: Vec<String> = keys.iter().map(|k| self.make_key(k)).collect();
105 self.storage.mget(&full_keys).await
106 }
107
108 pub async fn count(&self) -> RragResult<usize> {
110 self.storage.count(Some(&self.namespace)).await
111 }
112
113 fn make_key(&self, key: &str) -> String {
115 format!("{}::{}", self.namespace, key)
116 }
117
118 pub fn disable_auto_clear(&mut self) {
120 self.auto_clear = false;
121 }
122
123 pub fn enable_auto_clear(&mut self) {
125 self.auto_clear = true;
126 }
127}
128
129impl Drop for WorkingMemory {
130 fn drop(&mut self) {
131 if self.auto_clear {
132 tracing::debug!(
135 namespace = %self.namespace,
136 "WorkingMemory dropped with auto_clear enabled - cleanup deferred"
137 );
138 }
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145 use crate::storage::InMemoryStorage;
146
147 #[tokio::test]
148 async fn test_working_memory_basic_operations() {
149 let storage = Arc::new(InMemoryStorage::new());
150 let working = WorkingMemory::new(storage, "test-session".to_string());
151
152 working
154 .set("temp_result", MemoryValue::from(42i64))
155 .await
156 .unwrap();
157 let value = working.get("temp_result").await.unwrap();
158 assert_eq!(value.unwrap().as_integer(), Some(42));
159
160 assert!(working.exists("temp_result").await.unwrap());
162 assert!(!working.exists("nonexistent").await.unwrap());
163
164 assert!(working.delete("temp_result").await.unwrap());
166 assert!(!working.exists("temp_result").await.unwrap());
167 }
168
169 #[tokio::test]
170 async fn test_working_memory_multiple_operations() {
171 let storage = Arc::new(InMemoryStorage::new());
172 let working = WorkingMemory::new(storage, "test-session".to_string());
173
174 let pairs = [
176 ("key1", MemoryValue::from("value1")),
177 ("key2", MemoryValue::from(100i64)),
178 ("key3", MemoryValue::from(true)),
179 ];
180 working.set_many(&pairs).await.unwrap();
181
182 let keys = ["key1", "key2", "key3"];
184 let values = working.get_many(&keys).await.unwrap();
185
186 assert_eq!(values[0].as_ref().unwrap().as_string(), Some("value1"));
187 assert_eq!(values[1].as_ref().unwrap().as_integer(), Some(100));
188 assert_eq!(values[2].as_ref().unwrap().as_boolean(), Some(true));
189
190 assert_eq!(working.count().await.unwrap(), 3);
192
193 let all_keys = working.keys().await.unwrap();
195 assert_eq!(all_keys.len(), 3);
196 }
197
198 #[tokio::test]
199 async fn test_working_memory_clear() {
200 let storage = Arc::new(InMemoryStorage::new());
201 let working = WorkingMemory::new(storage, "test-session".to_string());
202
203 working
205 .set("key1", MemoryValue::from("value1"))
206 .await
207 .unwrap();
208 working
209 .set("key2", MemoryValue::from("value2"))
210 .await
211 .unwrap();
212
213 assert_eq!(working.count().await.unwrap(), 2);
214
215 working.clear().await.unwrap();
217 assert_eq!(working.count().await.unwrap(), 0);
218 }
219
220 #[tokio::test]
221 async fn test_working_memory_namespace_isolation() {
222 let storage = Arc::new(InMemoryStorage::new());
223 let working1 = WorkingMemory::new(storage.clone(), "session1".to_string());
224 let working2 = WorkingMemory::new(storage.clone(), "session2".to_string());
225
226 working1
228 .set("data", MemoryValue::from("session1-data"))
229 .await
230 .unwrap();
231 working2
232 .set("data", MemoryValue::from("session2-data"))
233 .await
234 .unwrap();
235
236 let value1 = working1.get("data").await.unwrap();
238 let value2 = working2.get("data").await.unwrap();
239
240 assert_eq!(value1.unwrap().as_string(), Some("session1-data"));
241 assert_eq!(value2.unwrap().as_string(), Some("session2-data"));
242 }
243}