Skip to main content

synaptic_session/
store_session.rs

1use std::sync::Arc;
2
3use serde::{Deserialize, Serialize};
4use synaptic_core::{now_iso, Store, SynapticError};
5use synaptic_graph::StoreCheckpointer;
6use synaptic_memory::ChatMessageHistory;
7
8/// Metadata about a session.
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct SessionInfo {
11    pub id: String,
12    pub created_at: String,
13}
14
15/// Store-backed session manager.
16///
17/// Session metadata is stored under namespace `["sessions"]`, key = session_id.
18/// Messages are accessed through [`ChatMessageHistory`] (same store).
19/// Checkpoints are accessed through [`StoreCheckpointer`] (same store).
20pub struct SessionManager {
21    store: Arc<dyn Store>,
22}
23
24impl SessionManager {
25    /// Create a new session manager backed by the given store.
26    pub fn new(store: Arc<dyn Store>) -> Self {
27        Self { store }
28    }
29
30    /// Create a new session with a unique ID.
31    pub async fn create_session(&self) -> Result<String, SynapticError> {
32        let id = uuid::Uuid::new_v4().to_string();
33        let info = SessionInfo {
34            id: id.clone(),
35            created_at: now_iso(),
36        };
37        let value = serde_json::to_value(&info)
38            .map_err(|e| SynapticError::Store(format!("failed to serialize session info: {e}")))?;
39        self.store.put(&["sessions"], &id, value).await?;
40        Ok(id)
41    }
42
43    /// List all sessions.
44    pub async fn list_sessions(&self) -> Result<Vec<SessionInfo>, SynapticError> {
45        let items = self.store.search(&["sessions"], None, 10_000).await?;
46        let mut sessions: Vec<SessionInfo> = items
47            .into_iter()
48            .filter_map(|item| serde_json::from_value(item.value).ok())
49            .collect();
50        sessions.sort_by(|a, b| a.created_at.cmp(&b.created_at));
51        Ok(sessions)
52    }
53
54    /// Get session info by ID.
55    pub async fn get_session(&self, id: &str) -> Result<Option<SessionInfo>, SynapticError> {
56        let item = self.store.get(&["sessions"], id).await?;
57        match item {
58            Some(item) => {
59                let info: SessionInfo = serde_json::from_value(item.value).map_err(|e| {
60                    SynapticError::Store(format!("failed to deserialize session info: {e}"))
61                })?;
62                Ok(Some(info))
63            }
64            None => Ok(None),
65        }
66    }
67
68    /// Delete a session and all its associated data (messages, summaries, checkpoints).
69    pub async fn delete_session(&self, id: &str) -> Result<(), SynapticError> {
70        // Delete session metadata
71        self.store.delete(&["sessions"], id).await?;
72
73        // Delete messages and summary
74        self.store.delete(&["memory", id], "messages").await?;
75        self.store.delete(&["memory", id], "summary").await?;
76
77        // Delete checkpoints — search and delete each one
78        let checkpoints = self
79            .store
80            .search(&["checkpoints", id], None, 10_000)
81            .await?;
82        for ckpt in checkpoints {
83            self.store.delete(&["checkpoints", id], &ckpt.key).await?;
84        }
85
86        Ok(())
87    }
88
89    /// Get a `ChatMessageHistory` that shares the same store.
90    pub fn memory(&self) -> ChatMessageHistory {
91        ChatMessageHistory::new(self.store.clone())
92    }
93
94    /// Get a `StoreCheckpointer` that shares the same store.
95    pub fn checkpointer(&self) -> StoreCheckpointer {
96        StoreCheckpointer::new(self.store.clone())
97    }
98
99    /// Get a reference to the underlying store.
100    pub fn store(&self) -> &Arc<dyn Store> {
101        &self.store
102    }
103}