1use async_trait::async_trait;
7use serde_json::Value;
8use std::collections::HashMap;
9
10use super::base::{BaseChatMemory, BaseMemory, ChatMessageHistory, MemoryError};
11use lc_schema::Message;
12
13#[derive(Debug)]
33pub struct ConversationBufferMemory {
34 chat_memory: ChatMessageHistory,
36
37 input_key: String,
39
40 output_key: String,
42
43 memory_key: String,
45
46 return_messages: bool,
48}
49
50impl ConversationBufferMemory {
51 pub fn new() -> Self {
53 Self {
54 chat_memory: ChatMessageHistory::new(),
55 input_key: "input".to_string(),
56 output_key: "output".to_string(),
57 memory_key: "history".to_string(),
58 return_messages: false,
59 }
60 }
61
62 pub fn with_input_key(mut self, key: String) -> Self {
64 self.input_key = key;
65 self
66 }
67
68 pub fn with_output_key(mut self, key: String) -> Self {
70 self.output_key = key;
71 self
72 }
73
74 pub fn with_memory_key(mut self, key: String) -> Self {
76 self.memory_key = key;
77 self
78 }
79
80 pub fn with_return_messages(mut self, return_messages: bool) -> Self {
82 self.return_messages = return_messages;
83 self
84 }
85
86 pub fn from_chat_memory(chat_memory: ChatMessageHistory) -> Self {
88 Self {
89 chat_memory,
90 ..Self::new()
91 }
92 }
93
94 pub fn chat_memory(&self) -> &ChatMessageHistory {
96 &self.chat_memory
97 }
98
99 pub fn chat_memory_mut(&mut self) -> &mut ChatMessageHistory {
101 &mut self.chat_memory
102 }
103
104 fn buffer_as_string(&self) -> String {
106 self.chat_memory.to_string()
107 }
108
109 fn buffer_as_messages(&self) -> Vec<Message> {
111 self.chat_memory.messages().to_vec()
112 }
113}
114
115impl Default for ConversationBufferMemory {
116 fn default() -> Self {
117 Self::new()
118 }
119}
120
121impl BaseChatMemory for ConversationBufferMemory {
123 fn messages(&self) -> &[Message] {
124 self.chat_memory.messages()
125 }
126
127 fn add_message(&mut self, message: Message) {
128 self.chat_memory.add_message(message);
129 }
130}
131
132#[async_trait]
133impl BaseMemory for ConversationBufferMemory {
134 fn memory_variables(&self) -> Vec<&str> {
135 vec![&self.memory_key]
136 }
137
138 async fn load_memory_variables(
139 &self,
140 _inputs: &HashMap<String, String>,
141 ) -> Result<HashMap<String, Value>, MemoryError> {
142 let mut result = HashMap::new();
143
144 if self.return_messages {
145 let messages: Vec<Value> = self
147 .buffer_as_messages()
148 .into_iter()
149 .map(|msg| serde_json::to_value(&msg).unwrap_or(Value::Null))
150 .collect();
151 result.insert(self.memory_key.clone(), Value::Array(messages));
152 } else {
153 result.insert(
155 self.memory_key.clone(),
156 Value::String(self.buffer_as_string()),
157 );
158 }
159
160 Ok(result)
161 }
162
163 async fn save_context(
164 &mut self,
165 inputs: &HashMap<String, String>,
166 outputs: &HashMap<String, String>,
167 ) -> Result<(), MemoryError> {
168 let input = inputs.get(&self.input_key).ok_or_else(|| {
170 MemoryError::SaveError(format!("Missing input key '{}'", self.input_key))
171 })?;
172 self.chat_memory.add_user_message(input);
173
174 let output = outputs.get(&self.output_key).ok_or_else(|| {
175 MemoryError::SaveError(format!("Missing output key '{}'", self.output_key))
176 })?;
177 self.chat_memory.add_ai_message(output);
178
179 Ok(())
180 }
181
182 async fn clear(&mut self) -> Result<(), MemoryError> {
183 self.chat_memory.clear();
184 Ok(())
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 #[tokio::test]
193 async fn test_conversation_buffer_memory() {
194 let mut memory = ConversationBufferMemory::new();
195
196 let inputs = HashMap::from([("input".to_string(), "Hello".to_string())]);
198 let outputs = HashMap::from([(
199 "output".to_string(),
200 "Hello! How can I help you?".to_string(),
201 )]);
202
203 memory.save_context(&inputs, &outputs).await.unwrap();
204
205 let memory_vars = memory.load_memory_variables(&HashMap::new()).await.unwrap();
207
208 assert!(memory_vars.contains_key("history"));
209 let history = memory_vars.get("history").unwrap();
210 assert!(history.as_str().unwrap().contains("Human: Hello"));
211 assert!(history.as_str().unwrap().contains("AI: Hello"));
212 }
213
214 #[tokio::test]
215 async fn test_conversation_buffer_memory_multiple() {
216 let mut memory = ConversationBufferMemory::new();
217
218 let inputs1 = HashMap::from([("input".to_string(), "My name is Zhang San".to_string())]);
220 let outputs1 = HashMap::from([("output".to_string(), "Hello Zhang San!".to_string())]);
221 memory.save_context(&inputs1, &outputs1).await.unwrap();
222
223 let inputs2 = HashMap::from([("input".to_string(), "What is my name?".to_string())]);
225 let outputs2 =
226 HashMap::from([("output".to_string(), "Your name is Zhang San".to_string())]);
227 memory.save_context(&inputs2, &outputs2).await.unwrap();
228
229 let memory_vars = memory.load_memory_variables(&HashMap::new()).await.unwrap();
231 let history = memory_vars.get("history").unwrap().as_str().unwrap();
232
233 assert!(history.contains("Zhang San"));
234 assert!(memory.chat_memory().len() == 4); }
236
237 #[tokio::test]
238 async fn test_conversation_buffer_memory_clear() {
239 let mut memory = ConversationBufferMemory::new();
240
241 let inputs = HashMap::from([("input".to_string(), "test".to_string())]);
243 let outputs = HashMap::from([("output".to_string(), "received".to_string())]);
244 memory.save_context(&inputs, &outputs).await.unwrap();
245
246 assert_eq!(memory.chat_memory().len(), 2);
247
248 memory.clear().await.unwrap();
250 assert_eq!(memory.chat_memory().len(), 0);
251 }
252
253 #[tokio::test]
256 async fn test_base_chat_memory_generic_function() {
257 use crate::window::ConversationBufferWindowMemory;
258
259 fn count_messages<T: BaseChatMemory>(memory: &T) -> usize {
261 memory.messages().len()
262 }
263
264 let mut buffer = ConversationBufferMemory::new();
266 buffer.add_user_message("Hello");
267 buffer.add_ai_message("Hi!");
268 assert_eq!(count_messages(&buffer), 2);
269
270 let mut window = ConversationBufferWindowMemory::new(2);
272 window.add_user_message("Q1");
273 window.add_ai_message("A1");
274 assert_eq!(count_messages(&window), 2);
275
276 let mut dyn_mem: Box<dyn BaseChatMemory> = Box::new(ConversationBufferWindowMemory::new(2));
278 dyn_mem.add_user_message("q");
279 dyn_mem.add_ai_message("a");
280 assert_eq!(dyn_mem.messages().len(), 2);
281 }
282
283 #[tokio::test]
284 async fn test_conversation_buffer_memory_return_messages() {
285 let mut memory = ConversationBufferMemory::new().with_return_messages(true);
286
287 let inputs = HashMap::from([("input".to_string(), "Hello".to_string())]);
288 let outputs = HashMap::from([("output".to_string(), "Hello!".to_string())]);
289 memory.save_context(&inputs, &outputs).await.unwrap();
290
291 let memory_vars = memory.load_memory_variables(&HashMap::new()).await.unwrap();
292 let history = memory_vars.get("history").unwrap();
293
294 assert!(history.is_array());
296 let messages = history.as_array().unwrap();
297 assert_eq!(messages.len(), 2);
298 }
299}