1pub use request::VectorSearchRequest;
14use reqwest::StatusCode;
15use serde::{Deserialize, Serialize};
16use serde_json::{Value, json};
17
18use crate::{
19 Embed, OneOrMany,
20 embeddings::{Embedding, EmbeddingError},
21 tool::Tool,
22 vector_store::request::{Filter, FilterError, SearchFilter},
23 wasm_compat::{WasmBoxedFuture, WasmCompatSend, WasmCompatSync},
24};
25
26pub mod builder;
27pub mod in_memory_store;
28pub mod lsh;
29pub mod request;
30
31#[derive(Debug, thiserror::Error)]
33pub enum VectorStoreError {
34 #[error("Embedding error: {0}")]
36 EmbeddingError(#[from] EmbeddingError),
37
38 #[error("Json error: {0}")]
40 JsonError(#[from] serde_json::Error),
41
42 #[cfg(not(target_family = "wasm"))]
43 #[error("Datastore error: {0}")]
45 DatastoreError(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
46
47 #[error("Filter error: {0}")]
49 FilterError(#[from] FilterError),
50
51 #[cfg(target_family = "wasm")]
52 #[error("Datastore error: {0}")]
54 DatastoreError(#[from] Box<dyn std::error::Error + 'static>),
55
56 #[error("Missing Id: {0}")]
58 MissingIdError(String),
59
60 #[error("HTTP request error: {0}")]
62 ReqwestError(#[from] reqwest::Error),
63
64 #[error("External call to API returned an error. Error code: {0} Message: {1}")]
66 ExternalAPIError(StatusCode, String),
67
68 #[error("Error while building VectorSearchRequest: {0}")]
70 BuilderError(String),
71}
72
73pub trait InsertDocuments: WasmCompatSend + WasmCompatSync {
75 fn insert_documents<Doc: Serialize + Embed + WasmCompatSend>(
77 &self,
78 documents: Vec<(Doc, OneOrMany<Embedding>)>,
79 ) -> impl std::future::Future<Output = Result<(), VectorStoreError>> + WasmCompatSend;
80}
81
82pub trait VectorStoreIndex: WasmCompatSend + WasmCompatSync {
84 type Filter: SearchFilter + WasmCompatSend + WasmCompatSync;
86
87 fn top_n<T: for<'a> Deserialize<'a> + WasmCompatSend>(
89 &self,
90 req: VectorSearchRequest<Self::Filter>,
91 ) -> impl std::future::Future<Output = Result<Vec<(f64, String, T)>, VectorStoreError>>
92 + WasmCompatSend;
93
94 fn top_n_ids(
96 &self,
97 req: VectorSearchRequest<Self::Filter>,
98 ) -> impl std::future::Future<Output = Result<Vec<(f64, String)>, VectorStoreError>> + WasmCompatSend;
99}
100
101pub type TopNResults = Result<Vec<(f64, String, Value)>, VectorStoreError>;
103
104pub trait VectorStoreIndexDyn: WasmCompatSend + WasmCompatSync {
106 fn top_n<'a>(
108 &'a self,
109 req: VectorSearchRequest<Filter<serde_json::Value>>,
110 ) -> WasmBoxedFuture<'a, TopNResults>;
111
112 fn top_n_ids<'a>(
114 &'a self,
115 req: VectorSearchRequest<Filter<serde_json::Value>>,
116 ) -> WasmBoxedFuture<'a, Result<Vec<(f64, String)>, VectorStoreError>>;
117}
118
119impl<I: VectorStoreIndex<Filter = F>, F> VectorStoreIndexDyn for I
120where
121 F: std::fmt::Debug
122 + Clone
123 + SearchFilter<Value = serde_json::Value>
124 + WasmCompatSend
125 + WasmCompatSync
126 + Serialize
127 + for<'de> Deserialize<'de>
128 + 'static,
129{
130 fn top_n<'a>(
131 &'a self,
132 req: VectorSearchRequest<Filter<serde_json::Value>>,
133 ) -> WasmBoxedFuture<'a, TopNResults> {
134 let req = req.map_filter(Filter::interpret);
135
136 Box::pin(async move {
137 Ok(self
138 .top_n::<serde_json::Value>(req)
139 .await?
140 .into_iter()
141 .map(|(score, id, doc)| (score, id, prune_document(doc).unwrap_or_default()))
142 .collect::<Vec<_>>())
143 })
144 }
145
146 fn top_n_ids<'a>(
147 &'a self,
148 req: VectorSearchRequest<Filter<serde_json::Value>>,
149 ) -> WasmBoxedFuture<'a, Result<Vec<(f64, String)>, VectorStoreError>> {
150 let req = req.map_filter(Filter::interpret);
151
152 Box::pin(self.top_n_ids(req))
153 }
154}
155
156fn prune_document(document: serde_json::Value) -> Option<serde_json::Value> {
157 match document {
158 Value::Object(mut map) => {
159 let new_map = map
160 .iter_mut()
161 .filter_map(|(key, value)| {
162 prune_document(value.take()).map(|value| (key.clone(), value))
163 })
164 .collect::<serde_json::Map<_, _>>();
165
166 Some(Value::Object(new_map))
167 }
168 Value::Array(vec) if vec.len() > 400 => None,
169 Value::Array(vec) => Some(Value::Array(
170 vec.into_iter().filter_map(prune_document).collect(),
171 )),
172 Value::Number(num) => Some(Value::Number(num)),
173 Value::String(s) => Some(Value::String(s)),
174 Value::Bool(b) => Some(Value::Bool(b)),
175 Value::Null => Some(Value::Null),
176 }
177}
178
179#[derive(Serialize, Deserialize, Debug)]
181pub struct VectorStoreOutput {
182 pub score: f64,
184 pub id: String,
186 pub document: Value,
188}
189
190impl<T, F> Tool for T
191where
192 F: SearchFilter<Value = serde_json::Value>
193 + WasmCompatSend
194 + WasmCompatSync
195 + for<'de> Deserialize<'de>,
196 T: VectorStoreIndex<Filter = F>,
197{
198 const NAME: &'static str = "search_vector_store";
199
200 type Error = VectorStoreError;
201 type Args = VectorSearchRequest<F>;
202 type Output = Vec<VectorStoreOutput>;
203
204 fn description(&self) -> String {
205 "Retrieves the most relevant documents from a vector store based on a query.".to_string()
206 }
207
208 fn parameters(&self) -> serde_json::Value {
209 json!({
210 "type": "object",
211 "properties": {
212 "query": {
213 "type": "string",
214 "description": "The query string to search for relevant documents in the vector store."
215 },
216 "samples": {
217 "type": "integer",
218 "description": "The maximum number of samples / documents to retrieve.",
219 "default": 5,
220 "minimum": 1
221 },
222 "threshold": {
223 "type": "number",
224 "description": "Similarity search threshold. If present, any result with a distance less than this may be omitted from the final result."
225 }
226 },
227 "required": ["query", "samples"]
228 })
229 }
230
231 async fn call(&self, args: Self::Args) -> Result<Self::Output, Self::Error> {
232 let results = self.top_n(args).await?;
233 Ok(results
234 .into_iter()
235 .map(|(score, id, document)| VectorStoreOutput {
236 score,
237 id,
238 document,
239 })
240 .collect())
241 }
242}
243
244#[derive(Clone, Debug, Default)]
246pub enum IndexStrategy {
247 #[default]
249 BruteForce,
250
251 LSH {
253 num_tables: usize,
255 num_hyperplanes: usize,
257 },
258}