brainwires_core/vector_store.rs
1//! Vector store abstraction
2//!
3//! Provides the `VectorStore` trait for pluggable vector database backends.
4//! Implementations live in downstream crates (storage with LanceDB, rag with
5//! LanceDB/Qdrant) — this trait enables consumers to swap backends without
6//! changing application code.
7
8use anyhow::Result;
9use async_trait::async_trait;
10use serde::{Deserialize, Serialize};
11
12/// Result from a vector similarity search.
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct VectorSearchResult {
15 /// Unique identifier for the stored item.
16 pub id: String,
17 /// Similarity score (higher = more similar, typically 0.0–1.0).
18 pub score: f32,
19 /// The text content of the matched chunk.
20 pub content: String,
21 /// Arbitrary metadata associated with the item.
22 pub metadata: serde_json::Value,
23}
24
25/// Trait for vector database operations.
26///
27/// Provides a backend-agnostic interface for storing and searching embeddings.
28/// Implementations should handle connection management internally.
29///
30/// # Example
31///
32/// ```ignore
33/// use brainwires_core::{VectorStore, VectorSearchResult};
34///
35/// async fn search(store: &dyn VectorStore, query_vec: Vec<f32>) -> anyhow::Result<Vec<VectorSearchResult>> {
36/// store.search(query_vec, 10, 0.7).await
37/// }
38/// ```
39#[async_trait]
40pub trait VectorStore: Send + Sync {
41 /// Initialize the store (create tables/collections if needed).
42 async fn initialize(&self, dimension: usize) -> Result<()>;
43
44 /// Insert embeddings with associated content and metadata.
45 ///
46 /// Returns the number of items successfully stored.
47 async fn upsert(
48 &self,
49 ids: Vec<String>,
50 embeddings: Vec<Vec<f32>>,
51 contents: Vec<String>,
52 metadata: Vec<serde_json::Value>,
53 ) -> Result<usize>;
54
55 /// Search for similar vectors.
56 ///
57 /// Returns up to `limit` results with score >= `min_score`.
58 async fn search(
59 &self,
60 query_vector: Vec<f32>,
61 limit: usize,
62 min_score: f32,
63 ) -> Result<Vec<VectorSearchResult>>;
64
65 /// Delete items by their IDs.
66 async fn delete(&self, ids: Vec<String>) -> Result<usize>;
67
68 /// Delete all stored data.
69 async fn clear(&self) -> Result<()>;
70
71 /// Get the number of stored items.
72 async fn count(&self) -> Result<usize>;
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 // Verify the types compile and serialize correctly
80 #[test]
81 fn test_search_result_serialization() {
82 let result = VectorSearchResult {
83 id: "chunk-1".to_string(),
84 score: 0.95,
85 content: "fn main() {}".to_string(),
86 metadata: serde_json::json!({"file": "main.rs", "language": "rust"}),
87 };
88
89 let json = serde_json::to_string(&result).unwrap();
90 let deserialized: VectorSearchResult = serde_json::from_str(&json).unwrap();
91 assert_eq!(deserialized.id, "chunk-1");
92 assert!((deserialized.score - 0.95).abs() < f32::EPSILON);
93 }
94}