use anyhow::Result;
use super::super::EmbeddingEngine;
use super::super::prompt::split_identifier;
use super::super::runtime::configured_rerank_blend;
use crate::embedding_store::ScoredChunk;
use crate::embedding_types::SemanticMatch;
impl EmbeddingEngine {
pub fn search(&self, query: &str, max_results: usize) -> Result<Vec<SemanticMatch>> {
let results = self.search_scored(query, max_results)?;
Ok(results.into_iter().map(SemanticMatch::from).collect())
}
pub fn search_scored(&self, query: &str, max_results: usize) -> Result<Vec<ScoredChunk>> {
self.search_scored_inner(query, max_results, None)
}
pub fn search_scored_in_scope(
&self,
query: &str,
max_results: usize,
path_scope: Option<&str>,
) -> Result<Vec<ScoredChunk>> {
self.search_scored_inner(query, max_results, path_scope)
}
fn search_scored_inner(
&self,
query: &str,
max_results: usize,
path_scope: Option<&str>,
) -> Result<Vec<ScoredChunk>> {
let query_embedding = self.embed_query_cached(query)?;
let factor = std::env::var("CODELENS_RERANK_FACTOR")
.ok()
.and_then(|v| v.parse::<usize>().ok())
.unwrap_or(5);
let candidate_count = max_results.saturating_mul(factor).max(max_results);
let mut candidates =
self.store
.search_scoped(&query_embedding, candidate_count, path_scope)?;
if candidates.len() <= max_results {
return Ok(candidates);
}
let query_lower = query.to_lowercase();
let query_tokens: Vec<&str> = query_lower
.split(|c: char| c.is_whitespace() || c == '_' || c == '-')
.filter(|t| t.len() >= 2)
.collect();
if query_tokens.is_empty() {
candidates.truncate(max_results);
return Ok(candidates);
}
let blend = configured_rerank_blend();
for chunk in &mut candidates {
let split_name = split_identifier(&chunk.symbol_name);
let searchable = format!(
"{} {} {} {} {}",
chunk.symbol_name.to_lowercase(),
split_name.to_lowercase(),
chunk.name_path.to_lowercase(),
chunk.signature.to_lowercase(),
chunk.file_path.to_lowercase(),
);
let overlap = query_tokens
.iter()
.filter(|t| searchable.contains(**t))
.count() as f64;
let overlap_ratio = overlap / query_tokens.len().max(1) as f64;
chunk.score = chunk.score * blend + overlap_ratio * (1.0 - blend);
}
candidates.sort_by(|a, b| {
b.score
.partial_cmp(&a.score)
.unwrap_or(std::cmp::Ordering::Equal)
});
candidates.truncate(max_results);
Ok(candidates)
}
}