@claude-flow/embeddings
Version:
V3 Embedding Service - OpenAI, Transformers.js, Agentic-Flow (ONNX), Mock providers with hyperbolic embeddings, normalization, and chunking
74 lines • 3.22 kB
TypeScript
/**
* ADR-121 Phase 11 — Reciprocal Rank Fusion (RRF).
*
* When you have N ranked result lists for the same query (or for N
* variants of the same intent) — e.g. from a question-reformulation
* pipeline, from a hybrid lexical+vector search, or from multiple
* embedding models — RRF combines them into a single fused ranking
* without needing the underlying scores to be comparable.
*
* Cormack, Clarke, Büttcher (SIGIR 2009):
*
* RRF_score(item) = Σ over lists i where item appears: 1 / (k_rrf + rank_i)
*
* where rank_i is the item's 1-based position in list i and k_rrf is a
* small constant (the original paper recommends 60 — small enough that
* top results dominate, large enough that mid-list ranks still
* contribute). The score is heuristic but practically excellent: RRF
* consistently matches or beats supervised learning-to-rank in TREC
* evaluations despite needing zero training data.
*
* This module ships the algorithm as a pure function so callers can
* compose it with any source of ranked lists — AnnRouter results,
* lexical search, MMR-reranked picks, etc. The CLI's
* `embeddings_search_text_ensemble` tool composes
* `search_text_batch` + `reciprocalRankFusion` for the common case of
* "expand one user query into N variants, retrieve top-k for each,
* fuse to a single ranking".
*/
export interface RrfListItem {
/** Caller-supplied identifier. Items with the same id across lists are fused. */
readonly id: string;
/** Free-form payload — preserved from the FIRST list this item appears in. */
readonly payload?: unknown;
}
export interface RrfOptions {
/** Number of fused results to return. Clamped to total unique items. */
readonly k: number;
/**
* RRF smoothing constant. Default 60 (the SIGIR 2009 recommendation).
* Smaller k_rrf → top-ranked items dominate more sharply.
* Larger k_rrf → flatter contribution from each list, more like simple
* vote-counting.
*/
readonly kRrf?: number;
/** Optional per-list weights. Default = 1 for every list. */
readonly listWeights?: ReadonlyArray<number>;
}
export interface RrfFusedHit {
readonly id: string;
readonly payload?: unknown;
/** Final fused RRF score (higher = better). */
readonly score: number;
/**
* Per-list contribution: the 1-based rank in each list the item
* appeared in. Length === number of input lists. `null` means the
* item didn't appear in that list.
*/
readonly ranks: ReadonlyArray<number | null>;
/** Number of lists the item appeared in. */
readonly listOccurrences: number;
}
/**
* Iterative RRF over N ranked lists. Time complexity:
* O(N · L) where N = number of lists, L = avg list length
*
* Lists are 1-indexed inside the formula (first item = rank 1).
*/
export declare function reciprocalRankFusion(lists: ReadonlyArray<ReadonlyArray<RrfListItem>>, options: RrfOptions): RrfFusedHit[];
/**
* Convenience: same as reciprocalRankFusion but returns just the fused
* ids in rank order.
*/
export declare function rrfIds(lists: ReadonlyArray<ReadonlyArray<RrfListItem>>, options: RrfOptions): string[];
//# sourceMappingURL=rrf.d.ts.map