use crate::retrieval::code_store::{CodeVectorStore, VectorBackend};
use crate::retrieval::config::RetrievalConfig;
use crate::retrieval::embedder::EmbedderHttp;
#[cfg(feature = "server-stack")]
use crate::retrieval::qdrant::QdrantWrap;
use crate::retrieval::reranker::RerankerHttp;
use anyhow::Result;
use std::sync::Arc;
pub struct RetrievalClient {
pub(crate) code_store: Arc<dyn CodeVectorStore>,
pub embedder: EmbedderHttp,
pub reranker: RerankerHttp,
pub config: RetrievalConfig,
pub(crate) lite: bool,
}
impl RetrievalClient {
pub async fn from_env() -> Result<Self> {
let config = RetrievalConfig::from_env()?;
let backend = VectorBackend::resolve();
let lite = matches!(backend, VectorBackend::SqliteVec);
let code_store: Arc<dyn CodeVectorStore> = match backend {
VectorBackend::SqliteVec => {
Arc::new(crate::retrieval::sqlite_code_store::SqliteVecCodeStore::from_env()?)
}
VectorBackend::Qdrant => Self::qdrant_code_store(&config).await?,
};
let dense_only = lite || config.disable_sparse;
let embedder = EmbedderHttp::new(
&config.embedder_url,
&config.sparse_embedder_url,
config.model_dim,
)
.dense_only(dense_only);
let reranker = RerankerHttp::new(&config.reranker_url);
Ok(Self {
code_store,
embedder,
reranker,
config,
lite,
})
}
#[cfg(feature = "server-stack")]
async fn qdrant_code_store(config: &RetrievalConfig) -> Result<Arc<dyn CodeVectorStore>> {
Ok(Arc::new(QdrantWrap::connect(&config.qdrant_url).await?))
}
#[cfg(not(feature = "server-stack"))]
async fn qdrant_code_store(_config: &RetrievalConfig) -> Result<Arc<dyn CodeVectorStore>> {
anyhow::bail!(
"CODESCOUT_VECTOR_BACKEND=qdrant requires the `server-stack` build feature. \
Rebuild with `--features server-stack`, or run the lean lite stack with \
CODESCOUT_VECTOR_BACKEND=sqlite-vec."
)
}
#[cfg(feature = "server-stack")]
pub fn from_config_only(config: RetrievalConfig) -> Self {
let embedder = EmbedderHttp::new(
&config.embedder_url,
&config.sparse_embedder_url,
config.model_dim,
);
let reranker = RerankerHttp::new(&config.reranker_url);
let client = qdrant_client::Qdrant::from_url(&config.qdrant_url)
.timeout(std::time::Duration::from_secs(120))
.build()
.expect("invalid qdrant url");
let code_store: Arc<dyn CodeVectorStore> = Arc::new(QdrantWrap { client });
Self {
code_store,
embedder,
reranker,
config,
lite: false,
}
}
pub async fn project_index_stats(
&self,
collection: &str,
project_id: &str,
) -> Result<(usize, usize)> {
self.code_store
.project_index_stats(collection, project_id)
.await
}
}