ri_agent_graph/
checkpointer.rs1use crate::checkpoint::Checkpoint;
2use crate::error::Result;
3use async_trait::async_trait;
4use std::collections::HashMap;
5use std::sync::Arc;
6use tokio::sync::RwLock;
7
8#[async_trait]
10pub trait CheckpointSaver: Send + Sync {
11 async fn save(&self, checkpoint: &Checkpoint) -> Result<()>;
13 async fn load(&self, thread_id: &str) -> Result<Option<Checkpoint>>;
15 async fn load_history(&self, thread_id: &str) -> Result<Vec<Checkpoint>>;
17 async fn clear(&self, thread_id: &str) -> Result<()>;
19}
20
21pub struct MemorySaver {
23 checkpoints: Arc<RwLock<HashMap<String, Vec<Checkpoint>>>>,
24}
25
26impl MemorySaver {
27 pub fn new() -> Self {
28 Self {
29 checkpoints: Arc::new(RwLock::new(HashMap::new())),
30 }
31 }
32}
33
34impl Default for MemorySaver {
35 fn default() -> Self {
36 Self::new()
37 }
38}
39
40#[async_trait]
41impl CheckpointSaver for MemorySaver {
42 async fn save(&self, checkpoint: &Checkpoint) -> Result<()> {
43 let mut store = self.checkpoints.write().await;
44 store
45 .entry(checkpoint.execution_id.clone())
46 .or_default()
47 .push(checkpoint.clone());
48 Ok(())
49 }
50
51 async fn load(&self, thread_id: &str) -> Result<Option<Checkpoint>> {
52 let store = self.checkpoints.read().await;
53 Ok(store.get(thread_id).and_then(|v| v.last()).cloned())
54 }
55
56 async fn load_history(&self, thread_id: &str) -> Result<Vec<Checkpoint>> {
57 let store = self.checkpoints.read().await;
58 Ok(store.get(thread_id).cloned().unwrap_or_default())
59 }
60
61 async fn clear(&self, thread_id: &str) -> Result<()> {
62 let mut store = self.checkpoints.write().await;
63 store.remove(thread_id);
64 Ok(())
65 }
66}
67
68#[cfg(feature = "checkpointing")]
70pub struct SqliteSaver {
71 manager: std::sync::Mutex<crate::checkpoint::CheckpointManager>,
72}
73
74#[cfg(feature = "checkpointing")]
75impl SqliteSaver {
76 pub fn new(db_path: &str) -> Result<Self> {
77 Ok(Self {
78 manager: std::sync::Mutex::new(crate::checkpoint::CheckpointManager::new(db_path)?),
79 })
80 }
81}
82
83#[cfg(feature = "checkpointing")]
84#[async_trait]
85impl CheckpointSaver for SqliteSaver {
86 async fn save(&self, checkpoint: &Checkpoint) -> Result<()> {
87 let mgr = self
88 .manager
89 .lock()
90 .map_err(|e| crate::error::AgentGraphError::CheckpointError(e.to_string()))?;
91 mgr.save(checkpoint)
92 }
93
94 async fn load(&self, thread_id: &str) -> Result<Option<Checkpoint>> {
95 let mgr = self
96 .manager
97 .lock()
98 .map_err(|e| crate::error::AgentGraphError::CheckpointError(e.to_string()))?;
99 mgr.load(thread_id)
100 }
101
102 async fn load_history(&self, thread_id: &str) -> Result<Vec<Checkpoint>> {
103 let mgr = self
104 .manager
105 .lock()
106 .map_err(|e| crate::error::AgentGraphError::CheckpointError(e.to_string()))?;
107 mgr.load_all(thread_id)
108 }
109
110 async fn clear(&self, thread_id: &str) -> Result<()> {
111 let mgr = self
112 .manager
113 .lock()
114 .map_err(|e| crate::error::AgentGraphError::CheckpointError(e.to_string()))?;
115 mgr.clear(thread_id)
116 }
117}