1use std::future::Future;
5use std::pin::Pin;
6
7use crate::error::Result;
8use crate::types::Message;
9
10pub trait Memory: Send + Sync {
12 fn add_message(&self, message: &Message) -> impl Future<Output = Result<()>> + Send;
19
20 fn get_messages(&self) -> impl Future<Output = Result<Vec<Message>>> + Send;
22
23 fn with_messages<R, F>(&self, f: F) -> impl Future<Output = Result<R>> + Send
31 where
32 F: FnOnce(&[Message]) -> R + Send,
33 R: Send,
34 {
35 async move { Ok(f(&self.get_messages().await?)) }
36 }
37
38 fn clear(&self) -> impl Future<Output = Result<()>> + Send;
40}
41
42pub trait ErasedMemory: Send + Sync {
44 fn add_message_erased<'a>(
45 &'a self,
46 message: &'a Message,
47 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>;
48
49 fn get_messages_erased<'a>(
50 &'a self,
51 ) -> Pin<Box<dyn Future<Output = Result<Vec<Message>>> + Send + 'a>>;
52
53 fn with_messages_erased<'a>(
63 &'a self,
64 f: Box<dyn FnOnce(&[Message]) + Send + 'a>,
65 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>> {
66 Box::pin(async move {
67 let messages = self.get_messages_erased().await?;
68 f(&messages);
69 Ok(())
70 })
71 }
72
73 fn clear_erased<'a>(&'a self) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>;
74}
75
76impl<T: Memory> ErasedMemory for T {
77 fn add_message_erased<'a>(
78 &'a self,
79 message: &'a Message,
80 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>> {
81 Box::pin(self.add_message(message))
82 }
83
84 fn get_messages_erased<'a>(
85 &'a self,
86 ) -> Pin<Box<dyn Future<Output = Result<Vec<Message>>> + Send + 'a>> {
87 Box::pin(self.get_messages())
88 }
89
90 fn with_messages_erased<'a>(
91 &'a self,
92 f: Box<dyn FnOnce(&[Message]) + Send + 'a>,
93 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>> {
94 Box::pin(self.with_messages(f))
95 }
96
97 fn clear_erased<'a>(&'a self) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>> {
98 Box::pin(self.clear())
99 }
100}
101
102pub type SharedMemory = std::sync::Arc<dyn ErasedMemory>;
104
105#[cfg(test)]
106mod tests {
107 use super::{ErasedMemory, Memory, SharedMemory};
108 use crate::{Message, Result, Role};
109 use std::future::Future;
110 use std::pin::Pin;
111 use std::sync::{Arc, Mutex};
112
113 struct VecMemory(Mutex<Vec<Message>>);
119
120 impl Memory for VecMemory {
121 async fn add_message(&self, message: &Message) -> Result<()> {
122 self.0.lock().unwrap().push(message.clone());
123 Ok(())
124 }
125
126 async fn get_messages(&self) -> Result<Vec<Message>> {
127 Ok(self.0.lock().unwrap().clone())
128 }
129
130 async fn clear(&self) -> Result<()> {
131 self.0.lock().unwrap().clear();
132 Ok(())
133 }
134 }
135
136 #[tokio::test]
137 async fn memory_is_implementable_from_core_alone() {
138 let mem = VecMemory(Mutex::new(Vec::new()));
139 mem.add_message(&Message::user("hi")).await.unwrap();
140 assert_eq!(mem.get_messages().await.unwrap().len(), 1);
141 assert_eq!(mem.get_messages().await.unwrap()[0].role, Role::User);
142 mem.clear().await.unwrap();
143 assert!(mem.get_messages().await.unwrap().is_empty());
144
145 let shared: SharedMemory = Arc::new(VecMemory(Mutex::new(Vec::new())));
146 shared
147 .add_message_erased(&Message::user("x"))
148 .await
149 .unwrap();
150 assert_eq!(shared.get_messages_erased().await.unwrap().len(), 1);
151 }
152
153 #[tokio::test]
154 async fn with_messages_default_matches_get_messages() {
155 let mem = VecMemory(Mutex::new(Vec::new()));
156 mem.add_message(&Message::user("one")).await.unwrap();
157 mem.add_message(&Message::assistant("two")).await.unwrap();
158
159 let owned = mem.get_messages().await.unwrap();
160 let borrowed = mem
161 .with_messages(|messages| {
162 messages
163 .iter()
164 .map(|m| (m.role.clone(), m.content.clone()))
165 .collect::<Vec<_>>()
166 })
167 .await
168 .unwrap();
169
170 assert_eq!(borrowed.len(), owned.len());
171 for (seen, expected) in borrowed.iter().zip(&owned) {
172 assert_eq!(seen.0, expected.role);
173 assert_eq!(seen.1, expected.content);
174 }
175 }
176
177 #[tokio::test]
178 async fn with_messages_erased_works_through_shared_memory() {
179 let shared: SharedMemory = Arc::new(VecMemory(Mutex::new(Vec::new())));
180 shared
181 .add_message_erased(&Message::user("x"))
182 .await
183 .unwrap();
184 shared
185 .add_message_erased(&Message::assistant("y"))
186 .await
187 .unwrap();
188
189 let mut seen: Vec<Option<String>> = Vec::new();
192 shared
193 .with_messages_erased(Box::new(|messages| {
194 seen.extend(messages.iter().map(|m| m.content.clone()));
195 }))
196 .await
197 .unwrap();
198
199 assert_eq!(seen, vec![Some("x".into()), Some("y".into())]);
200 }
201
202 struct DirectErased(Mutex<Vec<Message>>);
206
207 impl ErasedMemory for DirectErased {
208 fn add_message_erased<'a>(
209 &'a self,
210 message: &'a Message,
211 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>> {
212 Box::pin(async move {
213 self.0.lock().unwrap().push(message.clone());
214 Ok(())
215 })
216 }
217
218 fn get_messages_erased<'a>(
219 &'a self,
220 ) -> Pin<Box<dyn Future<Output = Result<Vec<Message>>> + Send + 'a>> {
221 Box::pin(async move { Ok(self.0.lock().unwrap().clone()) })
222 }
223
224 fn clear_erased<'a>(&'a self) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>> {
225 Box::pin(async move {
226 self.0.lock().unwrap().clear();
227 Ok(())
228 })
229 }
230 }
231
232 #[tokio::test]
233 async fn direct_erased_impl_gets_default_with_messages_erased() {
234 let shared: SharedMemory = Arc::new(DirectErased(Mutex::new(Vec::new())));
235 shared
236 .add_message_erased(&Message::user("hello"))
237 .await
238 .unwrap();
239
240 let mut count = 0usize;
241 shared
242 .with_messages_erased(Box::new(|messages| count = messages.len()))
243 .await
244 .unwrap();
245 assert_eq!(count, 1);
246 }
247}