/** * Reciprocal Rank Fusion (RRF). * * Combines several ranked id lists (e.g. keyword + vector) into one ranking * without needing comparable raw scores: each list contributes * 1 / (k + rank) to an id's fused score. Standard k = 60. * * Pure and dependency-free so it is unit-testable in isolation. */ export const RRF_K = 60; export function rrfFuse( lists: string[][], k: number = RRF_K, ): Array<{ id: string; score: number }> { const scores = new Map(); for (const list of lists) { list.forEach((id, i) => { scores.set(id, (scores.get(id) || 0) + 1 / (k + i + 1)); }); } return Array.from(scores.entries()) .map(([id, score]) => ({ id, score })) .sort((a, b) => b.score - a.score); }