luft_core/
in_memory_backend.rs1use crate::contract::event::AgentEvent;
6use crate::contract::finding::Finding;
7use crate::contract::ids::RunId;
8use crate::state::{
9 AgentResultCache, AgentSessionCheckpoint, CheckpointBackend, CheckpointStatus, RunCheckpoint,
10};
11use std::collections::HashMap;
12use std::sync::RwLock;
13
14#[derive(Debug, Default)]
15pub struct InMemoryBackend {
16 checkpoint: RwLock<Option<RunCheckpoint>>,
17 events: RwLock<Vec<AgentEvent>>,
18}
19
20impl InMemoryBackend {
21 pub fn new() -> Self {
22 Self::default()
23 }
24}
25
26impl CheckpointBackend for InMemoryBackend {
27 fn init_run(&self, run_id: RunId, task: &str, _run_dir: &str) -> anyhow::Result<()> {
28 let now = crate::state::current_timestamp();
29 let cp = RunCheckpoint {
30 run_id,
31 task: task.to_string(),
32 status: CheckpointStatus::Running,
33 current_phase: 0,
34 completed_phases: vec![],
35 agent_results: HashMap::new(),
36 agent_sessions: HashMap::new(),
37 findings: vec![],
38 total_tokens: 0,
39 created_at: now,
40 updated_at: now,
41 workflow_meta: None,
42 started_agent_ids: vec![],
43 };
44 *self.checkpoint.write().unwrap() = Some(cp);
45 Ok(())
46 }
47
48 fn init_run_with_meta(
49 &self,
50 run_id: RunId,
51 task: &str,
52 _run_dir: &str,
53 workflow_meta: serde_json::Value,
54 ) -> anyhow::Result<()> {
55 self.init_run(run_id, task, "")?;
56 if let Some(ref mut cp) = *self.checkpoint.write().unwrap() {
57 cp.workflow_meta = Some(workflow_meta);
58 }
59 Ok(())
60 }
61
62 fn open_run(&self, run_id: RunId) -> anyhow::Result<Option<RunCheckpoint>> {
63 let cp = self.checkpoint.read().unwrap().clone();
64 Ok(cp.filter(|c| c.run_id == run_id))
65 }
66
67 fn append_event(&self, event: &AgentEvent) -> anyhow::Result<()> {
68 self.events.write().unwrap().push(event.clone());
69 Ok(())
70 }
71
72 fn upsert_agent_result(&self, cache: &AgentResultCache) -> anyhow::Result<()> {
73 if let Some(ref mut cp) = *self.checkpoint.write().unwrap() {
74 cp.agent_results.insert(cache.agent_id, cache.clone());
75 }
76 Ok(())
77 }
78
79 fn upsert_agent_session(&self, session: &AgentSessionCheckpoint) -> anyhow::Result<()> {
80 if let Some(ref mut cp) = *self.checkpoint.write().unwrap() {
81 cp.agent_sessions.insert(session.agent_id, session.clone());
82 }
83 Ok(())
84 }
85
86 fn get_checkpoint(&self) -> Option<RunCheckpoint> {
87 self.checkpoint.read().unwrap().clone()
88 }
89
90 fn get_findings(&self) -> Vec<Finding> {
91 self.checkpoint
92 .read()
93 .unwrap()
94 .as_ref()
95 .map(|cp| cp.findings.clone())
96 .unwrap_or_default()
97 }
98
99 fn get_event_log(&self) -> anyhow::Result<Vec<AgentEvent>> {
100 Ok(self.events.read().unwrap().clone())
101 }
102
103 fn can_resume(&self) -> bool {
104 self.checkpoint
105 .read()
106 .unwrap()
107 .as_ref()
108 .map(|cp| {
109 matches!(
110 cp.status,
111 CheckpointStatus::Running
112 | CheckpointStatus::Failed
113 | CheckpointStatus::Cancelled
114 )
115 })
116 .unwrap_or(false)
117 }
118
119 fn reset_status_to_running(&self) -> anyhow::Result<()> {
120 if let Some(ref mut cp) = *self.checkpoint.write().unwrap() {
121 cp.status = CheckpointStatus::Running;
122 }
123 Ok(())
124 }
125
126 fn cancel(&self) -> anyhow::Result<()> {
127 if let Some(ref mut cp) = *self.checkpoint.write().unwrap() {
128 cp.status = CheckpointStatus::Cancelled;
129 }
130 Ok(())
131 }
132
133 fn save_checkpoint(&self, checkpoint: &RunCheckpoint) -> anyhow::Result<()> {
134 *self.checkpoint.write().unwrap() = Some(checkpoint.clone());
135 Ok(())
136 }
137}