Skip to main content

brainos_backends/
memory.rs

1//! Memory backend for the action dispatcher.
2
3use std::sync::Arc;
4
5#[derive(Clone)]
6pub struct DefaultMemoryBackend {
7    pub semantic: Option<hippocampus::SemanticStore>,
8    pub embedder: Arc<tokio::sync::Mutex<Option<hippocampus::Embedder>>>,
9    pub embedding_dim: usize,
10}
11
12#[async_trait::async_trait]
13impl cortex::actions::MemoryBackend for DefaultMemoryBackend {
14    async fn store_fact(
15        &self,
16        namespace: &str,
17        _category: &str,
18        subject: &str,
19        predicate: &str,
20        object: &str,
21    ) -> Result<String, cortex::actions::ActionError> {
22        let Some(semantic) = &self.semantic else {
23            return Err(cortex::actions::ActionError::ExecutionFailed(
24                "Semantic store unavailable".to_string(),
25            ));
26        };
27
28        let content = format!("{subject} {predicate} {object}");
29        let vector = {
30            let mut guard = self.embedder.lock().await;
31            if let Some(embedder) = guard.as_mut() {
32                match embedder.embed(&content).await {
33                    Ok(v) => {
34                        hippocampus::embedding::sanitize_embedding(v, self.embedding_dim, &content)
35                    }
36                    Err(e) => {
37                        tracing::warn!("ActionDispatcher embedding failed: {e}");
38                        hippocampus::embedding::deterministic_fallback_embedding(
39                            &content,
40                            self.embedding_dim,
41                        )
42                    }
43                }
44            } else {
45                hippocampus::embedding::deterministic_fallback_embedding(
46                    &content,
47                    self.embedding_dim,
48                )
49            }
50        };
51
52        semantic
53            .store_fact(
54                namespace, _category, subject, predicate, object, 1.0, None, vector, None,
55            )
56            .await
57            .map_err(|e| cortex::actions::ActionError::ExecutionFailed(e.to_string()))
58    }
59
60    async fn recall(
61        &self,
62        query: &str,
63        top_k: usize,
64        namespace: Option<&str>,
65    ) -> Result<Vec<cortex::actions::MemoryFact>, cortex::actions::ActionError> {
66        let Some(semantic) = &self.semantic else {
67            return Err(cortex::actions::ActionError::ExecutionFailed(
68                "Semantic store unavailable".to_string(),
69            ));
70        };
71
72        let vector = {
73            let mut guard = self.embedder.lock().await;
74            if let Some(embedder) = guard.as_mut() {
75                match embedder.embed(query).await {
76                    Ok(v) => {
77                        hippocampus::embedding::sanitize_embedding(v, self.embedding_dim, query)
78                    }
79                    Err(e) => {
80                        tracing::warn!("ActionDispatcher embedding failed: {e}");
81                        hippocampus::embedding::deterministic_fallback_embedding(
82                            query,
83                            self.embedding_dim,
84                        )
85                    }
86                }
87            } else {
88                hippocampus::embedding::deterministic_fallback_embedding(query, self.embedding_dim)
89            }
90        };
91
92        let results = semantic
93            .search_similar(vector, top_k.max(1), namespace, None)
94            .await
95            .map_err(|e| cortex::actions::ActionError::ExecutionFailed(e.to_string()))?;
96
97        Ok(results
98            .into_iter()
99            .map(|r| cortex::actions::MemoryFact {
100                namespace: r.fact.namespace,
101                subject: r.fact.subject,
102                predicate: r.fact.predicate,
103                object: r.fact.object,
104                confidence: r.fact.confidence,
105            })
106            .collect())
107    }
108}