fix(memory): preserve BM25 relevance ordering (#33757, thanks @lsdcc01)

Land #33757 by @lsdcc01 without the unrelated dependency bump. Preserve negative FTS5 BM25 ordering in hybrid scoring and add changelog coverage for #5767.

Co-authored-by: 丁春才0668000523 <ding.chuncai1@xydigit.com>
This commit is contained in:
Peter Steinberger
2026-03-07 22:41:38 +00:00
parent 99de6515a0
commit e45d62ba26
3 changed files with 21 additions and 3 deletions

View File

@@ -14,7 +14,18 @@ describe("memory hybrid helpers", () => {
expect(bm25RankToScore(0)).toBeCloseTo(1);
expect(bm25RankToScore(1)).toBeCloseTo(0.5);
expect(bm25RankToScore(10)).toBeLessThan(bm25RankToScore(1));
expect(bm25RankToScore(-100)).toBeCloseTo(1);
expect(bm25RankToScore(-100)).toBeCloseTo(1, 1);
});
it("bm25RankToScore preserves FTS5 BM25 relevance ordering", () => {
const strongest = bm25RankToScore(-4.2);
const middle = bm25RankToScore(-2.1);
const weakest = bm25RankToScore(-0.5);
expect(strongest).toBeGreaterThan(middle);
expect(middle).toBeGreaterThan(weakest);
expect(strongest).not.toBe(middle);
expect(middle).not.toBe(weakest);
});
it("mergeHybridResults unions by id and combines weighted scores", async () => {

View File

@@ -44,8 +44,14 @@ export function buildFtsQuery(raw: string): string | null {
}
export function bm25RankToScore(rank: number): number {
const normalized = Number.isFinite(rank) ? Math.max(0, rank) : 999;
return 1 / (1 + normalized);
if (!Number.isFinite(rank)) {
return 1 / (1 + 999);
}
if (rank < 0) {
const relevance = -rank;
return relevance / (1 + relevance);
}
return 1 / (1 + rank);
}
export async function mergeHybridResults(params: {