qmd-web / src /pipeline /blend.ts
shreyask's picture
Deploy qmd-web
ac50275 verified
import type { RRFResult, RerankedResult, FinalResult } from "../types";
import {
BLEND_TAIL_RRF_WEIGHT,
BLEND_TOP10_RRF_WEIGHT,
BLEND_TOP3_RRF_WEIGHT,
} from "../constants";
const LOW_RANK_PROMOTION_OVERRIDE = 0.9;
const LOW_RANK_MAX_PROMOTION = 1;
const SCORE_EPSILON = 1e-6;
function getRrfWeight(rank: number): number {
if (rank <= 3) return BLEND_TOP3_RRF_WEIGHT;
if (rank <= 10) return BLEND_TOP10_RRF_WEIGHT;
return BLEND_TAIL_RRF_WEIGHT;
}
// Blend RRF rank position with reranker score using qmd's position-aware weights.
export function blendScores(
rrfResults: RRFResult[],
rerankScores: Map<string, number>, // docId -> rerank score
): FinalResult[] {
const blended: RerankedResult[] = rrfResults.map((result, index) => {
const rank = index + 1;
const rrfWeight = getRrfWeight(rank);
const positionScore = 1 / rank;
const rerankScore = rerankScores.get(result.docId) ?? 0;
let blendedScore =
rrfWeight * positionScore + (1 - rrfWeight) * rerankScore;
// Low-ranked retrieval candidates need especially strong reranker evidence
// before they can leapfrog well-supported higher-ranked results.
if (rank > 3 && rerankScore < LOW_RANK_PROMOTION_OVERRIDE) {
const guardedRank = Math.max(1, rank - LOW_RANK_MAX_PROMOTION);
const promotionCap = (1 / guardedRank) - SCORE_EPSILON;
blendedScore = Math.min(blendedScore, promotionCap);
}
return {
...result,
rerankScore,
blendedScore,
};
});
// Sort by blended score descending
blended.sort((a, b) => b.blendedScore - a.blendedScore);
// Dedup by docId (keep highest blended score)
const seen = new Set<string>();
const final: FinalResult[] = [];
for (const result of blended) {
if (seen.has(result.docId)) continue;
seen.add(result.docId);
final.push({
filepath: result.filepath,
title: result.title,
bestChunk: result.bestChunk,
score: result.blendedScore,
docId: result.docId,
});
}
return final;
}