import { applyMMRToMemoryHits, computeMMRScore, DEFAULT_MMR_CONFIG, jaccardSimilarity, mmrRerank, textSimilarity, tokenize, type MMRItem, } from '../mmr'; describe('tokenize', () => { it('returns empty set for empty string', () => { expect(tokenize('')).toEqual(new Set()); }); it('dedupes and lowercases ASCII', () => { expect(tokenize('Hello Hello World')).toEqual(new Set(['hello', 'world'])); }); it('produces CJK unigrams and adjacent bigrams', () => { expect(tokenize('今天讨论')).toEqual( new Set(['今', '天', '讨', '论', '今天', '天讨', '讨论']) ); }); it('does not create bigrams across non-adjacent CJK chars', () => { expect(tokenize('我a好')).toEqual(new Set(['a', '我', '好'])); }); }); describe('jaccardSimilarity', () => { it('is 1 for identical sets', () => { expect(jaccardSimilarity(new Set(['a', 'b']), new Set(['a', 'b']))).toBe(1); }); it('is 0 for disjoint sets', () => { expect(jaccardSimilarity(new Set(['a']), new Set(['b']))).toBe(0); }); it('handles partial overlap', () => { expect( jaccardSimilarity(new Set(['a', 'b', 'c']), new Set(['b', 'c', 'd'])) ).toBe(0.5); }); }); describe('textSimilarity', () => { it('is case insensitive', () => { expect(textSimilarity('Hello World', 'hello world')).toBe(1); }); it('returns 0 for unrelated text', () => { expect(textSimilarity('apple banana', 'stone iron')).toBe(0); }); }); describe('computeMMRScore', () => { it('matches the formula λ*r - (1-λ)*s', () => { expect(computeMMRScore(1, 0.5, 0.7)).toBeCloseTo(0.55); expect(computeMMRScore(0.8, 0.5, 1)).toBe(0.8); expect(computeMMRScore(0.8, 0.5, 0)).toBe(-0.5); }); }); describe('mmrRerank', () => { it('is a no-op when disabled', () => { const items: MMRItem[] = [ { id: '1', score: 0.9, content: 'apple apple' }, { id: '2', score: 0.8, content: 'apple apple' }, ]; expect(mmrRerank(items)).toEqual(items); }); it('returns single item unchanged', () => { const items: MMRItem[] = [{ id: '1', score: 0.9, content: 'x' }]; expect(mmrRerank(items, { enabled: true })).toEqual(items); }); it('promotes diverse items over near-duplicates', () => { const items: MMRItem[] = [ { id: '1', score: 1.0, content: 'machine learning neural networks' }, { id: '2', score: 0.95, content: 'machine learning deep networks' }, { id: '3', score: 0.9, content: 'database sql queries' }, ]; const reranked = mmrRerank(items, { enabled: true, lambda: 0.5 }); expect(reranked[0].id).toBe('1'); expect(reranked[1].id).toBe('3'); // diverse beats the ML near-duplicate }); it('with λ=1, falls back to pure score order', () => { const items: MMRItem[] = [ { id: '1', score: 1.0, content: 'a' }, { id: '2', score: 0.5, content: 'a' }, { id: '3', score: 0.9, content: 'a' }, ]; expect( mmrRerank(items, { enabled: true, lambda: 1 }).map((i) => i.id) ).toEqual(['1', '3', '2']); }); it('default config is disabled with lambda 0.7', () => { expect(DEFAULT_MMR_CONFIG).toEqual({ enabled: false, lambda: 0.7 }); }); }); describe('applyMMRToMemoryHits', () => { it('preserves original MemoryEntry fields', () => { const hits = [ { id: 'a', path: 'memory/x.md', content: 'alpha beta', score: 0.9, createdAt: new Date(), source: 'vector' as const, }, ]; const out = applyMMRToMemoryHits(hits, { enabled: true }); expect(out[0]).toBe(hits[0]); }); it('diversifies across path-duplicate hits', () => { const hits = [ { id: 'a', path: 'memory/1.md', content: 'ml neural', score: 1.0, createdAt: new Date(), }, { id: 'b', path: 'memory/2.md', content: 'ml neural', score: 0.99, createdAt: new Date(), }, { id: 'c', path: 'memory/3.md', content: 'sql db', score: 0.8, createdAt: new Date(), }, ]; const out = applyMMRToMemoryHits(hits, { enabled: true, lambda: 0.5 }); expect(out[0].id).toBe('a'); expect(out[1].id).toBe('c'); }); });