use async_trait::async_trait;
use super::bm25::BM25Reranker;
use super::result::RerankResult;
use super::rrf::RRFReranker;
use super::traits::Reranker;
use crate::error::Result;
pub struct HybridReranker {
bm25: BM25Reranker,
rrf: RRFReranker,
model: String,
}
impl HybridReranker {
pub fn new() -> Self {
Self {
bm25: BM25Reranker::new(),
rrf: RRFReranker::new(),
model: "hybrid-reranker".to_string(),
}
}
pub async fn rerank_hybrid(
&self,
query: &str,
documents: &[String],
vector_rankings: Option<Vec<usize>>,
top_n: Option<usize>,
) -> Result<Vec<RerankResult>> {
if documents.is_empty() {
return Ok(vec![]);
}
let bm25_results = self.bm25.rerank(query, documents, None).await?;
let bm25_ranking: Vec<usize> = bm25_results.iter().map(|r| r.index).collect();
let mut ranked_lists = vec![bm25_ranking];
if let Some(vec_ranking) = vector_rankings {
ranked_lists.push(vec_ranking);
}
let mut results = self.rrf.fuse(&ranked_lists, documents.len());
if let Some(n) = top_n {
results.truncate(n);
}
Ok(results)
}
}
impl Default for HybridReranker {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl Reranker for HybridReranker {
fn name(&self) -> &str {
"hybrid"
}
fn model(&self) -> &str {
&self.model
}
async fn rerank(
&self,
query: &str,
documents: &[String],
top_n: Option<usize>,
) -> Result<Vec<RerankResult>> {
self.bm25.rerank(query, documents, top_n).await
}
}