forge_reasoning/hypothesis/
storage.rs1use async_trait::async_trait;
7use std::collections::HashMap;
8use std::sync::Arc;
9use tokio::sync::RwLock;
10use crate::hypothesis::types::{Hypothesis, HypothesisId, HypothesisStatus};
11use crate::hypothesis::confidence::Confidence;
12use crate::hypothesis::evidence::{Evidence, EvidenceId};
13use crate::errors::{ReasoningError, Result};
14
15#[async_trait]
17pub trait HypothesisStorage: Send + Sync {
18 async fn create_hypothesis(&self, hypothesis: &Hypothesis) -> Result<HypothesisId>;
20
21 async fn get_hypothesis(&self, id: HypothesisId) -> Result<Option<Hypothesis>>;
23
24 async fn update_confidence(
26 &self,
27 id: HypothesisId,
28 posterior: Confidence,
29 ) -> Result<()>;
30
31 async fn set_status(&self, id: HypothesisId, status: HypothesisStatus) -> Result<()>;
33
34 async fn list_hypotheses(&self) -> Result<Vec<Hypothesis>>;
36
37 async fn delete_hypothesis(&self, id: HypothesisId) -> Result<bool>;
39
40 async fn attach_evidence(&self, evidence: &Evidence) -> Result<EvidenceId>;
42
43 async fn get_evidence(&self, id: EvidenceId) -> Result<Option<Evidence>>;
45
46 async fn list_evidence_for_hypothesis(&self, hypothesis_id: HypothesisId) -> Result<Vec<Evidence>>;
48
49 async fn list_all_evidence(&self) -> Result<Vec<Evidence>>;
51
52 async fn delete_evidence(&self, id: EvidenceId) -> Result<bool>;
54}
55
56pub struct InMemoryHypothesisStorage {
58 hypotheses: Arc<RwLock<HashMap<HypothesisId, Hypothesis>>>,
59 evidence: Arc<RwLock<HashMap<EvidenceId, Evidence>>>,
60 hypothesis_evidence_index: Arc<RwLock<HashMap<HypothesisId, Vec<EvidenceId>>>>,
61}
62
63impl InMemoryHypothesisStorage {
64 pub fn new() -> Self {
65 Self {
66 hypotheses: Arc::new(RwLock::new(HashMap::new())),
67 evidence: Arc::new(RwLock::new(HashMap::new())),
68 hypothesis_evidence_index: Arc::new(RwLock::new(HashMap::new())),
69 }
70 }
71}
72
73impl Default for InMemoryHypothesisStorage {
74 fn default() -> Self {
75 Self::new()
76 }
77}
78
79#[async_trait]
80impl HypothesisStorage for InMemoryHypothesisStorage {
81 async fn create_hypothesis(&self, hypothesis: &Hypothesis) -> Result<HypothesisId> {
82 let mut store = self.hypotheses.write().await;
83 let id = hypothesis.id();
84 store.insert(id, hypothesis.clone());
85 Ok(id)
86 }
87
88 async fn get_hypothesis(&self, id: HypothesisId) -> Result<Option<Hypothesis>> {
89 let store = self.hypotheses.read().await;
90 Ok(store.get(&id).cloned())
91 }
92
93 async fn update_confidence(&self, id: HypothesisId, posterior: Confidence) -> Result<()> {
94 let mut store = self.hypotheses.write().await;
95 if let Some(h) = store.get_mut(&id) {
96 h.update_posterior(posterior).map_err(|e| ReasoningError::InvalidState(e))?;
97 Ok(())
98 } else {
99 Err(ReasoningError::NotFound(format!("Hypothesis {} not found", id)))
100 }
101 }
102
103 async fn set_status(&self, id: HypothesisId, status: HypothesisStatus) -> Result<()> {
104 let mut store = self.hypotheses.write().await;
105 if let Some(h) = store.get_mut(&id) {
106 h.set_status(status).map_err(|e| ReasoningError::InvalidState(e))?;
107 Ok(())
108 } else {
109 Err(ReasoningError::NotFound(format!("Hypothesis {} not found", id)))
110 }
111 }
112
113 async fn list_hypotheses(&self) -> Result<Vec<Hypothesis>> {
114 let store = self.hypotheses.read().await;
115 Ok(store.values().cloned().collect())
116 }
117
118 async fn delete_hypothesis(&self, id: HypothesisId) -> Result<bool> {
119 let mut store = self.hypotheses.write().await;
120
121 let mut evidence_store = self.evidence.write().await;
123 let mut index = self.hypothesis_evidence_index.write().await;
124
125 if let Some(evidence_ids) = index.remove(&id) {
126 for evidence_id in evidence_ids {
127 evidence_store.remove(&evidence_id);
128 }
129 }
130
131 Ok(store.remove(&id).is_some())
132 }
133
134 async fn attach_evidence(&self, evidence: &Evidence) -> Result<EvidenceId> {
135 let id = evidence.id();
136 let hypothesis_id = evidence.hypothesis_id();
137
138 let mut evidence_store = self.evidence.write().await;
140 evidence_store.insert(id, evidence.clone());
141
142 let mut index = self.hypothesis_evidence_index.write().await;
144 index.entry(hypothesis_id).or_insert_with(Vec::new).push(id);
145
146 Ok(id)
147 }
148
149 async fn get_evidence(&self, id: EvidenceId) -> Result<Option<Evidence>> {
150 let store = self.evidence.read().await;
151 Ok(store.get(&id).cloned())
152 }
153
154 async fn list_evidence_for_hypothesis(&self, hypothesis_id: HypothesisId) -> Result<Vec<Evidence>> {
155 let index = self.hypothesis_evidence_index.read().await;
156 let evidence_store = self.evidence.read().await;
157
158 if let Some(evidence_ids) = index.get(&hypothesis_id) {
159 let mut result = Vec::new();
160 for evidence_id in evidence_ids {
161 if let Some(evidence) = evidence_store.get(evidence_id) {
162 result.push(evidence.clone());
163 }
164 }
165 Ok(result)
166 } else {
167 Ok(Vec::new())
168 }
169 }
170
171 async fn list_all_evidence(&self) -> Result<Vec<Evidence>> {
172 let store = self.evidence.read().await;
173 Ok(store.values().cloned().collect())
174 }
175
176 async fn delete_evidence(&self, id: EvidenceId) -> Result<bool> {
177 let evidence_opt = {
179 let store = self.evidence.read().await;
180 store.get(&id).cloned()
181 };
182
183 if let Some(evidence) = evidence_opt {
184 let hypothesis_id = evidence.hypothesis_id();
185
186 let mut evidence_store = self.evidence.write().await;
188 evidence_store.remove(&id);
189
190 let mut index = self.hypothesis_evidence_index.write().await;
192 if let Some(evidence_ids) = index.get_mut(&hypothesis_id) {
193 evidence_ids.retain(|e_id| *e_id != id);
194 if evidence_ids.is_empty() {
195 index.remove(&hypothesis_id);
196 }
197 }
198
199 Ok(true)
200 } else {
201 Ok(false)
202 }
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209 use crate::hypothesis::types::Hypothesis;
210 use crate::hypothesis::evidence::{EvidenceType, EvidenceMetadata};
211
212 #[tokio::test]
213 async fn test_in_memory_create_and_get() {
214 let storage = InMemoryHypothesisStorage::new();
215 let prior = Confidence::new(0.5).unwrap();
216 let h = Hypothesis::new("Test hypothesis", prior);
217 let id = h.id();
218
219 let created_id = storage.create_hypothesis(&h).await.unwrap();
220 assert_eq!(created_id, id);
221
222 let retrieved = storage.get_hypothesis(id).await.unwrap();
223 assert!(retrieved.is_some());
224 assert_eq!(retrieved.unwrap().statement(), "Test hypothesis");
225 }
226
227 #[tokio::test]
228 async fn test_in_memory_get_not_found() {
229 let storage = InMemoryHypothesisStorage::new();
230 let id = HypothesisId::new();
231
232 let result = storage.get_hypothesis(id).await.unwrap();
233 assert!(result.is_none());
234 }
235
236 #[tokio::test]
237 async fn test_in_memory_update_confidence() {
238 let storage = InMemoryHypothesisStorage::new();
239 let prior = Confidence::new(0.5).unwrap();
240 let h = Hypothesis::new("Test", prior);
241 let id = h.id();
242
243 storage.create_hypothesis(&h).await.unwrap();
244
245 let new_posterior = Confidence::new(0.8).unwrap();
246 storage.update_confidence(id, new_posterior).await.unwrap();
247
248 let retrieved = storage.get_hypothesis(id).await.unwrap().unwrap();
249 assert_eq!(retrieved.posterior(), new_posterior);
250 }
251
252 #[tokio::test]
253 async fn test_in_memory_update_confidence_not_found() {
254 let storage = InMemoryHypothesisStorage::new();
255 let id = HypothesisId::new();
256 let posterior = Confidence::new(0.8).unwrap();
257
258 let result = storage.update_confidence(id, posterior).await;
259 assert!(result.is_err());
260 }
261
262 #[tokio::test]
263 async fn test_in_memory_set_status() {
264 let storage = InMemoryHypothesisStorage::new();
265 let prior = Confidence::new(0.5).unwrap();
266 let h = Hypothesis::new("Test", prior);
267 let id = h.id();
268
269 storage.create_hypothesis(&h).await.unwrap();
270 storage.set_status(id, HypothesisStatus::UnderTest).await.unwrap();
271
272 let retrieved = storage.get_hypothesis(id).await.unwrap().unwrap();
273 assert_eq!(retrieved.status(), HypothesisStatus::UnderTest);
274 }
275
276 #[tokio::test]
277 async fn test_in_memory_set_status_not_found() {
278 let storage = InMemoryHypothesisStorage::new();
279 let id = HypothesisId::new();
280
281 let result = storage.set_status(id, HypothesisStatus::UnderTest).await;
282 assert!(result.is_err());
283 }
284
285 #[tokio::test]
286 async fn test_in_memory_list_empty() {
287 let storage = InMemoryHypothesisStorage::new();
288 let list = storage.list_hypotheses().await.unwrap();
289 assert!(list.is_empty());
290 }
291
292 #[tokio::test]
293 async fn test_in_memory_list_multiple() {
294 let storage = InMemoryHypothesisStorage::new();
295
296 let h1 = Hypothesis::new("Hypothesis 1", Confidence::new(0.5).unwrap());
297 let h2 = Hypothesis::new("Hypothesis 2", Confidence::new(0.7).unwrap());
298
299 storage.create_hypothesis(&h1).await.unwrap();
300 storage.create_hypothesis(&h2).await.unwrap();
301
302 let list = storage.list_hypotheses().await.unwrap();
303 assert_eq!(list.len(), 2);
304 }
305
306 #[tokio::test]
307 async fn test_in_memory_delete() {
308 let storage = InMemoryHypothesisStorage::new();
309 let prior = Confidence::new(0.5).unwrap();
310 let h = Hypothesis::new("Test", prior);
311 let id = h.id();
312
313 storage.create_hypothesis(&h).await.unwrap();
314
315 let deleted = storage.delete_hypothesis(id).await.unwrap();
316 assert!(deleted);
317
318 let deleted_again = storage.delete_hypothesis(id).await.unwrap();
319 assert!(!deleted_again);
320
321 let retrieved = storage.get_hypothesis(id).await.unwrap();
322 assert!(retrieved.is_none());
323 }
324
325 #[tokio::test]
326 async fn test_attach_evidence() {
327 let storage = InMemoryHypothesisStorage::new();
328 let prior = Confidence::new(0.5).unwrap();
329 let h = Hypothesis::new("Test", prior);
330 let id = h.id();
331
332 storage.create_hypothesis(&h).await.unwrap();
333
334 let metadata = EvidenceMetadata::Observation {
335 description: "Test observation".to_string(),
336 source_path: None,
337 };
338
339 let evidence = Evidence::new(
340 id,
341 EvidenceType::Observation,
342 0.3,
343 metadata,
344 );
345
346 let evidence_id: EvidenceId = storage.attach_evidence(&evidence).await.unwrap();
347 assert_eq!(evidence_id, evidence.id());
348
349 let retrieved: Option<Evidence> = storage.get_evidence(evidence_id).await.unwrap();
350 assert!(retrieved.is_some());
351 assert_eq!(retrieved.unwrap().hypothesis_id(), id);
352 }
353
354 #[tokio::test]
355 async fn test_list_evidence_for_hypothesis() {
356 let storage = InMemoryHypothesisStorage::new();
357 let prior = Confidence::new(0.5).unwrap();
358 let h = Hypothesis::new("Test", prior);
359 let id = h.id();
360
361 storage.create_hypothesis(&h).await.unwrap();
362
363 for i in 0..3 {
365 let metadata = EvidenceMetadata::Observation {
366 description: format!("Observation {}", i),
367 source_path: None,
368 };
369 let evidence = Evidence::new(id, EvidenceType::Observation, 0.3, metadata);
370 storage.attach_evidence(&evidence).await.unwrap();
371 }
372
373 let evidence_list = storage.list_evidence_for_hypothesis(id).await.unwrap();
374 assert_eq!(evidence_list.len(), 3);
375 }
376
377 #[tokio::test]
378 async fn test_delete_evidence() {
379 let storage = InMemoryHypothesisStorage::new();
380 let prior = Confidence::new(0.5).unwrap();
381 let h = Hypothesis::new("Test", prior);
382 let id = h.id();
383
384 storage.create_hypothesis(&h).await.unwrap();
385
386 let metadata = EvidenceMetadata::Observation {
387 description: "Test".to_string(),
388 source_path: None,
389 };
390
391 let evidence = Evidence::new(id, EvidenceType::Observation, 0.3, metadata);
392 let evidence_id: EvidenceId = storage.attach_evidence(&evidence).await.unwrap();
393
394 let deleted = storage.delete_evidence(evidence_id).await.unwrap();
395 assert!(deleted);
396
397 let retrieved: Option<Evidence> = storage.get_evidence(evidence_id).await.unwrap();
398 assert!(retrieved.is_none());
399
400 let evidence_list = storage.list_evidence_for_hypothesis(id).await.unwrap();
402 assert_eq!(evidence_list.len(), 0);
403 }
404
405 #[tokio::test]
406 async fn test_delete_hypothesis_cleans_evidence() {
407 let storage = InMemoryHypothesisStorage::new();
408 let prior = Confidence::new(0.5).unwrap();
409 let h = Hypothesis::new("Test", prior);
410 let id = h.id();
411
412 storage.create_hypothesis(&h).await.unwrap();
413
414 let metadata = EvidenceMetadata::Observation {
415 description: "Test".to_string(),
416 source_path: None,
417 };
418
419 let evidence = Evidence::new(id, EvidenceType::Observation, 0.3, metadata);
420 let evidence_id: EvidenceId = storage.attach_evidence(&evidence).await.unwrap();
421
422 storage.delete_hypothesis(id).await.unwrap();
424
425 let retrieved: Option<Evidence> = storage.get_evidence(evidence_id).await.unwrap();
427 assert!(retrieved.is_none());
428 }
429
430 #[tokio::test]
431 async fn test_list_all_evidence() {
432 let storage = InMemoryHypothesisStorage::new();
433 let prior = Confidence::new(0.5).unwrap();
434
435 let h1 = Hypothesis::new("H1", prior);
436 let h2 = Hypothesis::new("H2", prior);
437
438 let id1 = storage.create_hypothesis(&h1).await.unwrap();
439 let id2 = storage.create_hypothesis(&h2).await.unwrap();
440
441 let evidence1 = Evidence::new(
442 id1,
443 EvidenceType::Observation,
444 0.3,
445 EvidenceMetadata::Observation {
446 description: "Test 1".to_string(),
447 source_path: None,
448 },
449 );
450 let evidence2 = Evidence::new(
451 id2,
452 EvidenceType::Observation,
453 0.3,
454 EvidenceMetadata::Observation {
455 description: "Test 2".to_string(),
456 source_path: None,
457 },
458 );
459
460 storage.attach_evidence(&evidence1).await.unwrap();
461 storage.attach_evidence(&evidence2).await.unwrap();
462
463 let all_evidence = storage.list_all_evidence().await.unwrap();
464 assert_eq!(all_evidence.len(), 2);
465 }
466}