symbi_runtime/context/
vector_db_factory.rs1#[cfg(feature = "vector-lancedb")]
7use std::path::PathBuf;
8use std::sync::Arc;
9
10use crate::context::types::ContextError;
11use crate::context::vector_db::NoOpVectorDatabase;
12#[cfg(feature = "vector-lancedb")]
13use crate::context::vector_db_lance::{LanceDbBackend, LanceDbConfig};
14#[cfg(feature = "vector-lancedb")]
15use crate::context::vector_db_trait::DistanceMetric;
16use crate::context::vector_db_trait::VectorDb;
17
18#[derive(Debug, Clone)]
20pub enum VectorBackendConfig {
21 #[cfg(feature = "vector-lancedb")]
22 LanceDb(LanceDbConfig),
23 #[cfg(feature = "vector-qdrant")]
24 Qdrant(crate::context::vector_db::QdrantConfig),
25 NoOp,
26}
27
28#[allow(clippy::derivable_impls)] impl Default for VectorBackendConfig {
30 fn default() -> Self {
31 #[cfg(feature = "vector-lancedb")]
32 {
33 Self::LanceDb(LanceDbConfig::default())
34 }
35 #[cfg(not(feature = "vector-lancedb"))]
36 {
37 Self::NoOp
38 }
39 }
40}
41
42pub async fn create_vector_backend(
44 config: VectorBackendConfig,
45) -> Result<Arc<dyn VectorDb>, ContextError> {
46 match config {
47 #[cfg(feature = "vector-lancedb")]
48 VectorBackendConfig::LanceDb(cfg) => {
49 let backend = LanceDbBackend::new(cfg).await?;
50 Ok(Arc::new(backend))
51 }
52 #[cfg(feature = "vector-qdrant")]
53 VectorBackendConfig::Qdrant(cfg) => {
54 let backend = crate::context::vector_db::QdrantClientWrapper::new(cfg);
55 Ok(Arc::new(backend))
56 }
57 VectorBackendConfig::NoOp => Ok(Arc::new(NoOpVectorDatabase)),
58 }
59}
60
61pub fn resolve_vector_config() -> VectorBackendConfig {
75 let backend =
76 std::env::var("SYMBIONT_VECTOR_BACKEND").unwrap_or_else(|_| "lancedb".to_string());
77
78 match backend.to_lowercase().as_str() {
79 #[cfg(feature = "vector-qdrant")]
80 "qdrant" => {
81 let dimension: usize = std::env::var("SYMBIONT_VECTOR_DIMENSION")
82 .ok()
83 .and_then(|s| s.parse().ok())
84 .unwrap_or(384);
85 let collection = std::env::var("SYMBIONT_VECTOR_COLLECTION")
86 .unwrap_or_else(|_| "symbiont_context".to_string());
87 let host =
88 std::env::var("SYMBIONT_VECTOR_HOST").unwrap_or_else(|_| "localhost".to_string());
89 let port = std::env::var("SYMBIONT_VECTOR_PORT").unwrap_or_else(|_| "6333".to_string());
90 let api_key = std::env::var("SYMBIONT_VECTOR_API_KEY").ok();
91 VectorBackendConfig::Qdrant(crate::context::vector_db::QdrantConfig {
92 url: format!("http://{}:{}", host, port),
93 api_key,
94 collection_name: collection,
95 vector_dimension: dimension,
96 ..Default::default()
97 })
98 }
99 "noop" | "none" => VectorBackendConfig::NoOp,
100 #[cfg(feature = "vector-lancedb")]
101 _ => {
102 let dimension: usize = std::env::var("SYMBIONT_VECTOR_DIMENSION")
103 .ok()
104 .and_then(|s| s.parse().ok())
105 .unwrap_or(384);
106 let collection = std::env::var("SYMBIONT_VECTOR_COLLECTION")
107 .unwrap_or_else(|_| "symbiont_context".to_string());
108 let path = std::env::var("SYMBIONT_VECTOR_DATA_PATH")
109 .unwrap_or_else(|_| "./data/vector_db".to_string());
110 VectorBackendConfig::LanceDb(LanceDbConfig {
111 data_path: PathBuf::from(path),
112 collection_name: collection,
113 vector_dimension: dimension,
114 distance_metric: DistanceMetric::Cosine,
115 })
116 }
117 #[cfg(not(feature = "vector-lancedb"))]
118 _ => VectorBackendConfig::NoOp,
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[cfg(feature = "vector-lancedb")]
127 #[test]
128 fn test_resolve_defaults_to_lancedb() {
129 std::env::remove_var("SYMBIONT_VECTOR_BACKEND");
130 let config = resolve_vector_config();
131 assert!(matches!(config, VectorBackendConfig::LanceDb(_)));
132 }
133
134 #[cfg(feature = "vector-lancedb")]
135 #[test]
136 fn test_resolve_lancedb_explicit() {
137 std::env::set_var("SYMBIONT_VECTOR_BACKEND", "lancedb");
138 let config = resolve_vector_config();
139 assert!(matches!(config, VectorBackendConfig::LanceDb(_)));
140 std::env::remove_var("SYMBIONT_VECTOR_BACKEND");
141 }
142
143 #[cfg(feature = "vector-lancedb")]
144 #[test]
145 fn test_resolve_custom_data_path() {
146 std::env::set_var("SYMBIONT_VECTOR_BACKEND", "lancedb");
147 std::env::set_var("SYMBIONT_VECTOR_DATA_PATH", "/tmp/custom_vectors");
148 let config = resolve_vector_config();
149 match config {
150 VectorBackendConfig::LanceDb(cfg) => {
151 assert_eq!(cfg.data_path, PathBuf::from("/tmp/custom_vectors"));
152 }
153 #[allow(unreachable_patterns)]
154 _ => panic!("Expected LanceDb config"),
155 }
156 std::env::remove_var("SYMBIONT_VECTOR_BACKEND");
157 std::env::remove_var("SYMBIONT_VECTOR_DATA_PATH");
158 }
159
160 #[cfg(feature = "vector-lancedb")]
161 #[tokio::test]
162 async fn test_create_lance_backend() {
163 let tmp = tempfile::TempDir::new().unwrap();
164 let config = VectorBackendConfig::LanceDb(LanceDbConfig {
165 data_path: tmp.path().to_path_buf(),
166 ..Default::default()
167 });
168 let backend = create_vector_backend(config).await;
169 assert!(backend.is_ok());
170 }
171
172 #[tokio::test]
173 async fn test_create_noop_backend() {
174 let backend = create_vector_backend(VectorBackendConfig::NoOp).await;
175 assert!(backend.is_ok());
176 }
177}