daimon_core/
vector_store.rs1use std::future::Future;
8use std::pin::Pin;
9use std::sync::Arc;
10
11use crate::document::{Document, ScoredDocument};
12use crate::error::Result;
13
14pub trait VectorStore: Send + Sync {
20 fn upsert(
22 &self,
23 id: &str,
24 embedding: Vec<f32>,
25 document: Document,
26 ) -> impl Future<Output = Result<()>> + Send;
27
28 fn upsert_many(
35 &self,
36 items: Vec<(String, Vec<f32>, Document)>,
37 ) -> impl Future<Output = Result<()>> + Send {
38 async move {
39 for (id, embedding, document) in items {
40 self.upsert(&id, embedding, document).await?;
41 }
42 Ok(())
43 }
44 }
45
46 fn query(
49 &self,
50 embedding: Vec<f32>,
51 top_k: usize,
52 ) -> impl Future<Output = Result<Vec<ScoredDocument>>> + Send;
53
54 fn delete(&self, id: &str) -> impl Future<Output = Result<bool>> + Send;
57
58 fn count(&self) -> impl Future<Output = Result<usize>> + Send;
60}
61
62pub trait ErasedVectorStore: Send + Sync {
64 fn upsert_erased<'a>(
65 &'a self,
66 id: &'a str,
67 embedding: Vec<f32>,
68 document: Document,
69 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>;
70
71 fn upsert_many_erased(
72 &self,
73 items: Vec<(String, Vec<f32>, Document)>,
74 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + '_>>;
75
76 fn query_erased<'a>(
77 &'a self,
78 embedding: Vec<f32>,
79 top_k: usize,
80 ) -> Pin<Box<dyn Future<Output = Result<Vec<ScoredDocument>>> + Send + 'a>>;
81
82 fn delete_erased<'a>(
83 &'a self,
84 id: &'a str,
85 ) -> Pin<Box<dyn Future<Output = Result<bool>> + Send + 'a>>;
86
87 fn count_erased(&self) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + '_>>;
88}
89
90impl<T: VectorStore> ErasedVectorStore for T {
91 fn upsert_erased<'a>(
92 &'a self,
93 id: &'a str,
94 embedding: Vec<f32>,
95 document: Document,
96 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>> {
97 Box::pin(self.upsert(id, embedding, document))
98 }
99
100 fn upsert_many_erased(
101 &self,
102 items: Vec<(String, Vec<f32>, Document)>,
103 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + '_>> {
104 Box::pin(self.upsert_many(items))
105 }
106
107 fn query_erased<'a>(
108 &'a self,
109 embedding: Vec<f32>,
110 top_k: usize,
111 ) -> Pin<Box<dyn Future<Output = Result<Vec<ScoredDocument>>> + Send + 'a>> {
112 Box::pin(self.query(embedding, top_k))
113 }
114
115 fn delete_erased<'a>(
116 &'a self,
117 id: &'a str,
118 ) -> Pin<Box<dyn Future<Output = Result<bool>> + Send + 'a>> {
119 Box::pin(self.delete(id))
120 }
121
122 fn count_erased(&self) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + '_>> {
123 Box::pin(self.count())
124 }
125}
126
127pub type SharedVectorStore = Arc<dyn ErasedVectorStore>;
129
130#[cfg(test)]
131mod tests {
132 use std::sync::atomic::{AtomicUsize, Ordering};
133
134 use super::*;
135
136 struct CountingStore {
137 upserts: AtomicUsize,
138 }
139
140 impl VectorStore for CountingStore {
141 async fn upsert(&self, _id: &str, _embedding: Vec<f32>, _document: Document) -> Result<()> {
142 self.upserts.fetch_add(1, Ordering::SeqCst);
143 Ok(())
144 }
145
146 async fn query(&self, _embedding: Vec<f32>, _top_k: usize) -> Result<Vec<ScoredDocument>> {
147 Ok(Vec::new())
148 }
149
150 async fn delete(&self, _id: &str) -> Result<bool> {
151 Ok(false)
152 }
153
154 async fn count(&self) -> Result<usize> {
155 Ok(self.upserts.load(Ordering::SeqCst))
156 }
157 }
158
159 #[test]
160 fn test_upsert_many_default_delegates_per_item() {
161 futures::executor::block_on(async {
162 let store = CountingStore {
163 upserts: AtomicUsize::new(0),
164 };
165 let items = (0..3)
166 .map(|i| (format!("id-{i}"), vec![0.0], Document::new("doc")))
167 .collect();
168 store.upsert_many(items).await.unwrap();
169 assert_eq!(store.upserts.load(Ordering::SeqCst), 3);
170
171 let shared: SharedVectorStore = Arc::new(store);
173 shared
174 .upsert_many_erased(vec![("id-3".into(), vec![0.0], Document::new("doc"))])
175 .await
176 .unwrap();
177 assert_eq!(shared.count_erased().await.unwrap(), 4);
178 });
179 }
180}