Skip to main content

lc_memory/
persistent.rs

1// lc-memory/src/persistent.rs
2//! Memory Persistence Trait
3//!
4//! Defines the interface for persistent memory storage.
5//! Allows memory to be saved/loaded from external storage (MongoDB, Redis, etc.)
6
7use super::base::{BaseMemory, MemoryError};
8use async_trait::async_trait;
9
10/// Persistent Memory Trait
11///
12/// Extends BaseMemory with persistence capabilities.
13/// Implementations can save/load memory state to external storage.
14///
15/// # Design
16/// - Framework provides trait and algorithms
17/// - Business layer provides storage implementation
18///
19/// # Example
20/// ```ignore
21/// use lc_memory::{MongoPersistentMemory, PersistentMemory};
22///
23/// let mut memory = MongoPersistentMemory::new(config);
24/// memory.load_from_store("session_123").await?;
25/// memory.save_context(&inputs, &outputs).await?;
26/// memory.save_to_store("session_123").await?;
27/// ```
28#[async_trait]
29pub trait PersistentMemory: BaseMemory {
30    /// Load memory state from persistent storage
31    ///
32    /// # Arguments
33    /// * `session_id` - Unique identifier for the conversation session
34    ///
35    /// # Returns
36    /// Ok(()) if successful, MemoryError if failed
37    async fn load_from_store(&mut self, session_id: &str) -> Result<(), MemoryError>;
38
39    /// Save current memory state to persistent storage
40    ///
41    /// Called after each conversation turn to persist the updated state.
42    ///
43    /// # Arguments
44    /// * `session_id` - Unique identifier for the conversation session
45    async fn save_to_store(&mut self, session_id: &str) -> Result<(), MemoryError>;
46
47    /// Delete a session's memory from storage
48    ///
49    /// # Arguments
50    /// * `session_id` - Session to delete
51    async fn delete_session(&self, session_id: &str) -> Result<(), MemoryError>;
52
53    /// Check if a session exists in storage
54    ///
55    /// # Arguments
56    /// * `session_id` - Session to check
57    async fn session_exists(&self, session_id: &str) -> Result<bool, MemoryError>;
58
59    /// Get current session ID
60    fn current_session_id(&self) -> Option<&str>;
61
62    /// Set session ID
63    fn set_session_id(&mut self, session_id: String);
64}
65
66/// Memory persistence configuration
67#[derive(Debug, Clone)]
68pub struct PersistenceConfig {
69    /// Auto-save after each save_context call
70    pub auto_save: bool,
71
72    /// Auto-load on first access
73    pub auto_load: bool,
74
75    /// Maximum messages to keep in memory before compression
76    pub max_messages: usize,
77
78    /// Token limit for summary buffer memory
79    pub token_limit: usize,
80}
81
82impl Default for PersistenceConfig {
83    fn default() -> Self {
84        Self {
85            auto_save: true,
86            auto_load: true,
87            max_messages: 100,
88            token_limit: 4000,
89        }
90    }
91}
92
93impl PersistenceConfig {
94    pub fn new() -> Self {
95        Self::default()
96    }
97
98    pub fn with_auto_save(mut self, auto_save: bool) -> Self {
99        self.auto_save = auto_save;
100        self
101    }
102
103    pub fn with_auto_load(mut self, auto_load: bool) -> Self {
104        self.auto_load = auto_load;
105        self
106    }
107
108    pub fn with_max_messages(mut self, max_messages: usize) -> Self {
109        self.max_messages = max_messages;
110        self
111    }
112
113    pub fn with_token_limit(mut self, token_limit: usize) -> Self {
114        self.token_limit = token_limit;
115        self
116    }
117}
118
119/// Memory data structure for serialization
120#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
121pub struct MemoryData {
122    /// Session ID
123    pub session_id: String,
124
125    /// Chat messages (serialized Message objects)
126    pub messages: Vec<lc_schema::Message>,
127
128    /// Current summary (for summary-based memory)
129    pub summary: Option<String>,
130
131    /// Memory metadata
132    pub metadata: std::collections::HashMap<String, String>,
133
134    /// Created timestamp
135    pub created_at: String,
136
137    /// Last updated timestamp
138    pub updated_at: String,
139}
140
141impl MemoryData {
142    pub fn new(session_id: String) -> Self {
143        let now = chrono::Utc::now().to_rfc3339();
144        Self {
145            session_id,
146            messages: Vec::new(),
147            summary: None,
148            metadata: std::collections::HashMap::new(),
149            created_at: now.clone(),
150            updated_at: now,
151        }
152    }
153
154    pub fn with_messages(mut self, messages: Vec<lc_schema::Message>) -> Self {
155        self.messages = messages;
156        self.updated_at = chrono::Utc::now().to_rfc3339();
157        self
158    }
159
160    pub fn with_summary(mut self, summary: String) -> Self {
161        self.summary = Some(summary);
162        self.updated_at = chrono::Utc::now().to_rfc3339();
163        self
164    }
165
166    pub fn add_message(&mut self, message: lc_schema::Message) {
167        self.messages.push(message);
168        self.updated_at = chrono::Utc::now().to_rfc3339();
169    }
170
171    pub fn set_summary(&mut self, summary: String) {
172        self.summary = Some(summary);
173        self.updated_at = chrono::Utc::now().to_rfc3339();
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use super::*;
180
181    #[test]
182    fn test_persistence_config_default() {
183        let config = PersistenceConfig::default();
184        assert!(config.auto_save);
185        assert!(config.auto_load);
186        assert_eq!(config.max_messages, 100);
187        assert_eq!(config.token_limit, 4000);
188    }
189
190    #[test]
191    fn test_persistence_config_custom() {
192        let config = PersistenceConfig::new()
193            .with_auto_save(false)
194            .with_max_messages(50)
195            .with_token_limit(2000);
196
197        assert!(!config.auto_save);
198        assert_eq!(config.max_messages, 50);
199        assert_eq!(config.token_limit, 2000);
200    }
201
202    #[test]
203    fn test_memory_data_new() {
204        let data = MemoryData::new("session_123".to_string());
205        assert_eq!(data.session_id, "session_123");
206        assert!(data.messages.is_empty());
207        assert!(data.summary.is_none());
208    }
209
210    #[test]
211    fn test_memory_data_with_messages() {
212        let messages = vec![
213            lc_schema::Message::human("Hello"),
214            lc_schema::Message::ai("Hi!"),
215        ];
216
217        let data = MemoryData::new("session_123".to_string()).with_messages(messages);
218
219        assert_eq!(data.messages.len(), 2);
220    }
221}