// Reciprocal Rank Fusion — tests the REAL fuseEpisodeResults from the orchestration // chat pipeline (previously test-fusion.js reimplemented the math, which could drift). const { test } = require('node:test'); const assert = require('node:assert'); const { fuseEpisodeResults } = require('../packages/orchestration-service/src/chat/index'); // fuseEpisodeResults returns {episode, score}[]; map to episodes for readability const fuse = (sem, kw, opts) => fuseEpisodeResults(sem, kw, opts).map(r => r.episode); const semantic = [ { id: 1, user_message: 'ep1 — semantic only, rank 1' }, { id: 2, user_message: 'ep2 — in both lists, rank 2 semantic' }, { id: 3, user_message: 'ep3 — in both lists, rank 3 semantic' }, ]; const keyword = [ { id: 3, user_message: 'ep3 — rank 1 FTS' }, { id: 2, user_message: 'ep2 — rank 2 FTS' }, { id: 4, user_message: 'ep4 — FTS only, rank 3' }, ]; test('episodes appearing in both lists rank highest', () => { const r = fuse(semantic, keyword, { semanticWeight: 1, keywordWeight: 1, limit: 5 }); assert.ok([2, 3].includes(r[0].id), 'a dual-list episode should be rank 1'); const idx = id => r.findIndex(e => e.id === id); assert.ok(idx(1) > idx(2), 'ep1 (semantic-only) should rank below ep2 (both lists)'); }); test('keywordWeight:0 → pure semantic passthrough in original order', () => { const r = fuse(semantic, keyword, { semanticWeight: 1, keywordWeight: 0, limit: 5 }); assert.strictEqual(r.length, 3, 'only the 3 semantic episodes survive'); assert.strictEqual(r[0].id, 1); assert.strictEqual(r[1].id, 2); }); test('limit is respected', () => { const r = fuse(semantic, keyword, { semanticWeight: 1, keywordWeight: 1, limit: 2 }); assert.strictEqual(r.length, 2); }); test('no overlap → both episodes appear, semantic tie-breaks first', () => { const r = fuse( [{ id: 10, user_message: 'sem' }], [{ id: 20, user_message: 'fts' }], { semanticWeight: 1, keywordWeight: 1, limit: 5 }, ); assert.strictEqual(r.length, 2); assert.strictEqual(r[0].id, 10, 'equal rank+weight: semantic inserted first, so it tie-breaks ahead'); }); test('raising keywordWeight lifts a keyword-only hit above a weak semantic tail', () => { // ep4 is keyword-only at FTS rank 3; with a high keyword weight it should // outrank ep1 (semantic rank 1) once keywordWeight dominates. const lowKw = fuse(semantic, keyword, { semanticWeight: 1, keywordWeight: 0.1, limit: 5 }); const highKw = fuse(semantic, keyword, { semanticWeight: 1, keywordWeight: 5, limit: 5 }); const rank = (list, id) => list.findIndex(e => e.id === id); assert.ok(rank(highKw, 4) < rank(lowKw, 4), 'ep4 should climb as keywordWeight increases'); });