use std::time::Duration; use config::AppConfig; use qdrant_client::qdrant::Distance; use crate::error::{AiError, AiResult}; #[derive(Clone, Debug)] pub struct RagConfig { pub url: String, pub api_key: Option, pub collection_name: String, pub vector_size: u64, pub distance: Distance, pub timeout: Duration, pub upsert_batch_size: usize, pub default_search_limit: u64, pub exact_session_search: bool, } impl RagConfig { pub fn new( url: impl Into, collection_name: impl Into, vector_size: u64, ) -> AiResult { let config = Self { url: url.into(), api_key: None, collection_name: collection_name.into(), vector_size, distance: Distance::Cosine, timeout: Duration::from_secs(10), upsert_batch_size: 64, default_search_limit: 8, exact_session_search: true, }; config.validate()?; Ok(config) } pub fn with_api_key(mut self, api_key: Option) -> Self { self.api_key = api_key; self } pub fn with_distance(mut self, distance: Distance) -> Self { self.distance = distance; self } pub fn with_timeout(mut self, timeout: Duration) -> Self { self.timeout = timeout; self } pub fn with_upsert_batch_size(mut self, upsert_batch_size: usize) -> Self { self.upsert_batch_size = upsert_batch_size; self } pub fn with_default_search_limit( mut self, default_search_limit: u64, ) -> Self { self.default_search_limit = default_search_limit; self } pub fn with_exact_session_search( mut self, exact_session_search: bool, ) -> Self { self.exact_session_search = exact_session_search; self } pub fn validate(&self) -> AiResult<()> { if self.url.trim().is_empty() { return Err(AiError::Config("qdrant url is required".to_string())); } if !self.url.trim().starts_with("http://") && !self.url.trim().starts_with("https://") { return Err(AiError::Config( "qdrant url must start with http:// or https://".to_string(), )); } if self.collection_name.trim().is_empty() { return Err(AiError::Config( "qdrant collection_name is required".to_string(), )); } if self.vector_size == 0 { return Err(AiError::Config( "qdrant vector_size must be greater than 0".to_string(), )); } if self.upsert_batch_size == 0 { return Err(AiError::Config( "qdrant upsert_batch_size must be greater than 0".to_string(), )); } if self.default_search_limit == 0 { return Err(AiError::Config( "qdrant default_search_limit must be greater than 0" .to_string(), )); } Ok(()) } } impl RagConfig { pub fn from_app_config( config: &AppConfig, collection_name: impl Into, ) -> AiResult { Ok(Self::new( config .qdrant_url() .map_err(|error| AiError::Config(error.to_string()))?, collection_name, config .get_embed_model_dimensions() .map_err(|error| AiError::Config(error.to_string()))?, )? .with_api_key( config .qdrant_api_key() .map_err(|error| AiError::Config(error.to_string()))?, )) } }