rig_core/vector_store/
mod.rs1use http::StatusCode;
13pub use request::VectorSearchRequest;
14use serde::{Deserialize, Serialize, de::DeserializeOwned};
15use serde_json::{Value, json};
16
17use crate::error::ProviderError;
18use crate::{
19 Embed,
20 embeddings::Embedding,
21 tool::{Tool, ToolContext},
22 vector_store::request::{FilterError, SearchFilter},
23 wasm_compat::{WasmCompatSend, WasmCompatSync},
24};
25
26pub mod in_memory_store;
27pub mod lsh;
28pub mod request;
29
30#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
32pub enum IndexStrategy {
33 #[default]
35 BruteForce,
36 LSH {
41 num_tables: usize,
44 num_hyperplanes: usize,
46 },
47}
48
49#[derive(Debug, thiserror::Error)]
51pub enum VectorStoreError {
52 #[error("Embedding error: {0}")]
54 EmbeddingError(#[from] ProviderError),
55
56 #[error("Json error: {0}")]
58 JsonError(#[from] serde_json::Error),
59
60 #[cfg(not(target_family = "wasm"))]
61 #[error("Datastore error: {0}")]
63 DatastoreError(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
64
65 #[error("Filter error: {0}")]
67 FilterError(#[from] FilterError),
68
69 #[cfg(target_family = "wasm")]
70 #[error("Datastore error: {0}")]
72 DatastoreError(#[from] Box<dyn std::error::Error + 'static>),
73
74 #[error("Missing Id: {0}")]
76 MissingIdError(String),
77
78 #[error("HTTP request error: {0}")]
85 Http(#[from] crate::http_client::Error),
86
87 #[error("External call to API returned an error. Error code: {0} Message: {1}")]
89 ExternalAPIError(StatusCode, String),
90
91 #[error("Requested {requested} samples, but this vector store returns at most {max}")]
93 SamplesOutOfRange {
94 requested: u64,
96 max: u64,
98 },
99}
100
101impl VectorStoreError {
102 #[cfg(not(target_family = "wasm"))]
107 pub fn datastore(e: impl std::error::Error + Send + Sync + 'static) -> Self {
108 Self::DatastoreError(Box::new(e))
109 }
110
111 #[cfg(target_family = "wasm")]
113 pub fn datastore(e: impl std::error::Error + 'static) -> Self {
114 Self::DatastoreError(Box::new(e))
115 }
116}
117
118pub fn flatten_embedded<Doc: Serialize, R>(
125 documents: Vec<(Doc, Vec<Embedding>)>,
126 mut f: impl FnMut(&Value, Embedding) -> Result<R, VectorStoreError>,
127) -> Result<Vec<R>, VectorStoreError> {
128 let mut records = Vec::new();
129 for (document, embeddings) in documents {
130 let json_document = serde_json::to_value(&document)?;
131 for embedding in embeddings {
132 records.push(f(&json_document, embedding)?);
133 }
134 }
135 Ok(records)
136}
137
138pub trait InsertDocuments: WasmCompatSend + WasmCompatSync {
140 fn insert_documents<Doc: Serialize + Embed + WasmCompatSend>(
145 &self,
146 documents: Vec<(Doc, Vec<Embedding>)>,
147 ) -> impl std::future::Future<Output = Result<(), VectorStoreError>> + WasmCompatSend;
148}
149
150pub trait VectorStoreIndex: WasmCompatSend + WasmCompatSync {
152 type Filter: SearchFilter + WasmCompatSend + WasmCompatSync;
154
155 fn top_n<T: DeserializeOwned + WasmCompatSend>(
157 &self,
158 req: VectorSearchRequest<Self::Filter>,
159 ) -> impl std::future::Future<Output = Result<Vec<VectorSearchResult<T>>, VectorStoreError>>
160 + WasmCompatSend;
161
162 fn top_n_ids(
164 &self,
165 req: VectorSearchRequest<Self::Filter>,
166 ) -> impl std::future::Future<Output = Result<Vec<VectorSearchIdResult>, VectorStoreError>>
167 + WasmCompatSend;
168}
169
170#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
172pub struct VectorSearchResult<T> {
173 pub score: f64,
175 pub id: String,
177 pub document: T,
179}
180
181#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
183pub struct VectorSearchIdResult {
184 pub score: f64,
186 pub id: String,
188}
189
190impl<T, F> Tool for T
191where
192 F: SearchFilter<Value = serde_json::Value>
193 + WasmCompatSend
194 + WasmCompatSync
195 + serde::de::DeserializeOwned,
196 T: VectorStoreIndex<Filter = F>,
197{
198 const NAME: &'static str = "search_vector_store";
199 type Error = VectorStoreError;
200 type Args = VectorSearchRequest<F>;
201 type Output = Vec<VectorSearchResult<Value>>;
202
203 fn description(&self) -> String {
204 "Retrieves the most relevant documents from a vector store based on a query.".to_string()
205 }
206
207 fn parameters(&self) -> serde_json::Value {
208 json!({
209 "type": "object",
210 "properties": {
211 "query": {
212 "type": "string",
213 "description": "The query string to search for relevant documents in the vector store."
214 },
215 "samples": {
216 "type": "integer",
217 "description": "The maximum number of samples / documents to retrieve.",
218 "default": 5,
219 "minimum": 1
220 },
221 "threshold": {
222 "type": "number",
223 "description": "Similarity search threshold. If present, any result with a distance less than this may be omitted from the final result."
224 }
225 },
226 "required": ["query", "samples"]
227 })
228 }
229
230 async fn call(
231 &self,
232 _: &mut ToolContext,
233 args: Self::Args,
234 ) -> Result<Self::Output, Self::Error> {
235 self.top_n(args).await
236 }
237}
238
239#[cfg(test)]
240mod tests;