Skip to main content

apollo/memory/
traits.rs

1//! Core MemoryBackend trait.
2
3use async_trait::async_trait;
4use serde_json::Value;
5
6/// Memory entry
7#[derive(Debug, Clone)]
8pub struct MemoryEntry {
9    pub key: String,
10    pub value: String,
11    pub metadata: Option<Value>,
12    pub created_at: chrono::DateTime<chrono::Utc>,
13}
14
15/// Embedding entry (vector + source text)
16#[derive(Debug, Clone)]
17pub struct EmbeddingEntry {
18    pub namespace: String,
19    pub key: String,
20    pub vector: Vec<f32>,
21    pub text: String,
22    pub created_at: chrono::DateTime<chrono::Utc>,
23}
24
25/// Indexed file record
26#[derive(Debug, Clone)]
27pub struct FileIndex {
28    pub path: String,
29    pub hash: String,
30    pub last_indexed: chrono::DateTime<chrono::Utc>,
31}
32
33/// Code chunk with optional embedding
34#[derive(Debug, Clone)]
35pub struct Chunk {
36    pub file_path: String,
37    pub start_line: u32,
38    pub end_line: u32,
39    pub content: String,
40    pub embedding: Option<Vec<f32>>,
41    pub created_at: chrono::DateTime<chrono::Utc>,
42}
43
44#[derive(Debug, Clone)]
45pub struct ConversationSearchHit {
46    pub chat_id: String,
47    pub role: String,
48    pub content: String,
49    pub created_at: chrono::DateTime<chrono::Utc>,
50}
51
52/// The core MemoryBackend trait.
53#[async_trait]
54pub trait MemoryBackend: Send + Sync {
55    /// For downcasting to concrete types
56    fn as_any(&self) -> &dyn std::any::Any;
57
58    /// Store a key-value memory
59    async fn store(
60        &self,
61        namespace: &str,
62        key: &str,
63        value: &str,
64        metadata: Option<Value>,
65    ) -> anyhow::Result<()>;
66
67    /// Recall a specific memory by key
68    async fn recall(&self, namespace: &str, key: &str) -> anyhow::Result<Option<MemoryEntry>>;
69
70    /// Search memories by query (semantic or keyword)
71    async fn search(
72        &self,
73        namespace: &str,
74        query: &str,
75        limit: usize,
76    ) -> anyhow::Result<Vec<MemoryEntry>>;
77
78    /// Delete a memory
79    async fn forget(&self, namespace: &str, key: &str) -> anyhow::Result<()>;
80
81    /// List all memories in a namespace
82    async fn list(&self, namespace: &str) -> anyhow::Result<Vec<MemoryEntry>>;
83
84    /// Store a conversation message
85    async fn store_conversation(
86        &self,
87        chat_id: &str,
88        sender_id: &str,
89        role: &str,
90        content: &str,
91    ) -> anyhow::Result<()>;
92
93    /// Store multiple conversation messages in one batch.
94    async fn store_conversation_batch(
95        &self,
96        entries: &[(&str, &str, &str, &str)],
97    ) -> anyhow::Result<()> {
98        for (chat_id, sender_id, role, content) in entries {
99            self.store_conversation(chat_id, sender_id, role, content)
100                .await?;
101        }
102        Ok(())
103    }
104
105    /// Get recent conversation history (returns role, content pairs)
106    async fn get_conversation_history(
107        &self,
108        chat_id: &str,
109        limit: usize,
110    ) -> anyhow::Result<Vec<(String, String)>>;
111
112    async fn search_conversations(
113        &self,
114        query: &str,
115        limit: usize,
116        chat_id: Option<&str>,
117    ) -> anyhow::Result<Vec<ConversationSearchHit>> {
118        let _ = (query, limit, chat_id);
119        Ok(Vec::new())
120    }
121
122    /// Get cached sticker description by ID
123    async fn get_sticker_cache(&self, sticker_id: &str) -> anyhow::Result<Option<String>>;
124
125    /// Store sticker cache (sticker_id → description)
126    async fn store_sticker_cache(
127        &self,
128        sticker_id: &str,
129        file_id: &str,
130        description: &str,
131    ) -> anyhow::Result<()>;
132
133    // ── Embeddings ──
134
135    /// Store a vector embedding for a memory key
136    async fn store_embedding(
137        &self,
138        namespace: &str,
139        key: &str,
140        vector: &[f32],
141        text: &str,
142    ) -> anyhow::Result<()> {
143        let _ = (namespace, key, vector, text);
144        Ok(())
145    }
146
147    /// Search embeddings by cosine similarity (returns nearest matches)
148    async fn search_embeddings(
149        &self,
150        namespace: &str,
151        query_vector: &[f32],
152        limit: usize,
153    ) -> anyhow::Result<Vec<EmbeddingEntry>> {
154        let _ = (namespace, query_vector, limit);
155        Ok(Vec::new())
156    }
157
158    // ── File indexing ──
159
160    /// Record that a file has been indexed
161    async fn store_file_index(&self, path: &str, hash: &str) -> anyhow::Result<()> {
162        let _ = (path, hash);
163        Ok(())
164    }
165
166    /// Get file index entry (to check if re-indexing is needed)
167    async fn get_file_index(&self, path: &str) -> anyhow::Result<Option<FileIndex>> {
168        let _ = path;
169        Ok(None)
170    }
171
172    // ── Code chunks ──
173
174    /// Store a code chunk with optional embedding
175    async fn store_chunk(
176        &self,
177        file_path: &str,
178        start_line: u32,
179        end_line: u32,
180        content: &str,
181        embedding: Option<&[f32]>,
182    ) -> anyhow::Result<()> {
183        let _ = (file_path, start_line, end_line, content, embedding);
184        Ok(())
185    }
186
187    /// Search chunks by file path
188    async fn get_chunks_for_file(&self, file_path: &str) -> anyhow::Result<Vec<Chunk>> {
189        let _ = file_path;
190        Ok(Vec::new())
191    }
192
193    /// Delete all chunks for a file (before re-indexing)
194    async fn delete_chunks_for_file(&self, file_path: &str) -> anyhow::Result<()> {
195        let _ = file_path;
196        Ok(())
197    }
198}