import type { RRFResult, RerankedResult, FinalResult } from '../types'; import { InfoTooltip } from './PipelineView'; interface FusionColumnState { rrf: { status: 'idle' | 'done'; data?: { merged: RRFResult[] } }; rerank: { status: 'idle' | 'running' | 'done'; data?: { before: RRFResult[]; after: RerankedResult[] } }; finalResults?: FinalResult[]; } interface FusionColumnProps { state: FusionColumnState; accent: string; info: string; } function Spinner({ color }: { color: string }) { return ( ); } function SectionHeader({ label, color, badge }: { label: string; color: string; badge?: string }) { return (
{label} {badge && ( {badge} )}
); } function RRFRow({ result, rank }: { result: RRFResult; rank: number }) { return (
#{rank} {result.title} {result.score.toFixed(4)}
); } // Rank badge: shows rank # with color coding for movement function RankBadge({ label, rank, color }: { label: string; rank: number; color: string }) { return ( {label} #{rank} ); } interface RankJourneyRow { docId: string; title: string; rrfRank?: number; rerankRank?: number; finalRank?: number; } // Shows each doc's rank journey: RRF #N → Reranker #N → Final #N function RankJourney({ before, after, finalResults }: { before: RRFResult[]; after: RerankedResult[]; finalResults?: FinalResult[]; }) { const topLimit = 5; const topBefore = before.slice(0, topLimit); const topFinal = (finalResults ?? []).slice(0, topLimit); const rerankOrder = [...after].sort((a, b) => b.rerankScore - a.rerankScore); const titleMap = new Map([ ...before.map((result) => [result.docId, result.title] as const), ...after.map((result) => [result.docId, result.title] as const), ...topFinal.map((result) => [result.docId, result.title] as const), ]); const rrfRankMap = new Map(before.map((result, index) => [result.docId, index + 1])); const rerankRankMap = new Map(rerankOrder.map((r, i) => [r.docId, i + 1])); const finalRankMap = new Map((finalResults ?? []).map((result, index) => [result.docId, index + 1])); const rowMap = new Map(); for (const result of [...topBefore, ...topFinal]) { const existing = rowMap.get(result.docId); rowMap.set(result.docId, { docId: result.docId, title: titleMap.get(result.docId) ?? result.title, rrfRank: rrfRankMap.get(result.docId), rerankRank: rerankRankMap.get(result.docId), finalRank: finalRankMap.get(result.docId), ...existing, }); } const rows = [...rowMap.values()] .sort((a, b) => (a.finalRank ?? Number.POSITIVE_INFINITY) - (b.finalRank ?? Number.POSITIVE_INFINITY) || (a.rrfRank ?? Number.POSITIVE_INFINITY) - (b.rrfRank ?? Number.POSITIVE_INFINITY) || (a.rerankRank ?? Number.POSITIVE_INFINITY) - (b.rerankRank ?? Number.POSITIVE_INFINITY), ) .slice(0, topLimit); return (
{rows.map((row) => { return (
{row.title}
{row.rrfRank !== undefined && ( <> {'\u2192'} )} {row.rerankRank !== undefined && ( <> {'\u2192'} )} {row.finalRank !== undefined ? ( ) : ( blending... )}
); })}
Final ranking blends reranker scores with retrieval position.
); } export default function FusionColumn({ state, accent, info }: FusionColumnProps) { const rrfDone = state.rrf.status === 'done'; const rerankRunning = state.rerank.status === 'running'; const rerankDone = state.rerank.status === 'done'; const isIdle = !rrfDone && !rerankRunning && !rerankDone; return (

Fusion & Reranking

{rerankRunning && }
{isIdle && (

Awaiting search...

)} {rrfDone && state.rrf.data && (
{state.rrf.data.merged.slice(0, 5).map((r, i) => ( ))} {state.rrf.data.merged.length > 5 && (
+{state.rrf.data.merged.length - 5} more
)}
)} {rerankRunning && !rerankDone && (

Reranking with cross-encoder...

)} {rerankDone && state.rerank.data && (
)}
); }