Skip to main content

synaptic_memory/
store_memory.rs

1use std::sync::Arc;
2
3use async_trait::async_trait;
4use synaptic_core::{MemoryStore, Message, Store, SynapticError};
5
6/// `MemoryStore` implementation backed by any [`Store`].
7///
8/// Messages are stored under namespace `["memory", "{session_id}"]` with key `"messages"`.
9/// Summaries (used by summary strategies) are stored under the same namespace with key `"summary"`.
10///
11/// This replaces the old `InMemoryStore` (in-memory only) and `FileChatMessageHistory`
12/// (file-only) with a single implementation that works with *any* Store backend
13/// (InMemoryStore, FileStore, RedisStore, etc.).
14pub struct ChatMessageHistory {
15    store: Arc<dyn Store>,
16}
17
18impl ChatMessageHistory {
19    /// Create a new `ChatMessageHistory` backed by the given store.
20    pub fn new(store: Arc<dyn Store>) -> Self {
21        Self { store }
22    }
23
24    /// Get the summary for a session (used by summary memory strategies).
25    pub async fn get_summary(&self, session_id: &str) -> Result<Option<String>, SynapticError> {
26        let item = self.store.get(&["memory", session_id], "summary").await?;
27        Ok(item.and_then(|i| i.value.as_str().map(String::from)))
28    }
29
30    /// Set the summary for a session (used by summary memory strategies).
31    pub async fn set_summary(&self, session_id: &str, summary: &str) -> Result<(), SynapticError> {
32        self.store
33            .put(
34                &["memory", session_id],
35                "summary",
36                serde_json::Value::String(summary.to_string()),
37            )
38            .await
39    }
40
41    /// Return a reference to the underlying store.
42    pub fn store(&self) -> &Arc<dyn Store> {
43        &self.store
44    }
45}
46
47#[async_trait]
48impl MemoryStore for ChatMessageHistory {
49    async fn append(&self, session_id: &str, message: Message) -> Result<(), SynapticError> {
50        let mut messages = self.load(session_id).await?;
51        messages.push(message);
52        let value = serde_json::to_value(&messages)
53            .map_err(|e| SynapticError::Memory(format!("failed to serialize messages: {e}")))?;
54        self.store
55            .put(&["memory", session_id], "messages", value)
56            .await
57    }
58
59    async fn load(&self, session_id: &str) -> Result<Vec<Message>, SynapticError> {
60        let item = self.store.get(&["memory", session_id], "messages").await?;
61        match item {
62            Some(item) => {
63                let messages: Vec<Message> = serde_json::from_value(item.value).map_err(|e| {
64                    SynapticError::Memory(format!("failed to deserialize messages: {e}"))
65                })?;
66                Ok(messages)
67            }
68            None => Ok(Vec::new()),
69        }
70    }
71
72    async fn clear(&self, session_id: &str) -> Result<(), SynapticError> {
73        self.store
74            .delete(&["memory", session_id], "messages")
75            .await?;
76        self.store
77            .delete(&["memory", session_id], "summary")
78            .await?;
79        Ok(())
80    }
81}