/** * @module tests/unit/brainy/similar-threshold * @description Pins `brain.similar({ threshold })` — the min-similarity filter. * * Before 8.0 `similar()` accepted a `threshold` but silently DROPPED it (it was * never forwarded to the query, so callers got unfiltered results). 8.0 applies * it as a post-filter on `result.score` — the canonical way to impose a minimum * score on plain semantic results (top-level vector search does not honor a * `threshold`; see the `find({ near })` guidance). These tests use explicit * vectors so the embedder is never invoked. */ import { describe, it, expect, afterEach } from 'vitest' import { Brainy } from '../../../src/brainy.js' import { NounType } from '../../../src/types/graphTypes.js' /** Deterministic 384-dim vectors — no embedder, distinct per seed. */ function vec(seed: number): number[] { return Array.from({ length: 384 }, (_, i) => ((seed * 31 + i * 13) % 100) / 100) } describe('brain.similar() — threshold post-filter (8.0)', () => { const brains: Brainy[] = [] afterEach(async () => { for (const b of brains.splice(0)) await b.close().catch(() => {}) }) it('honors the min-similarity threshold (was silently dropped before 8.0)', async () => { const brain = new Brainy({ requireSubtype: false, storage: { type: 'memory' } }) brains.push(brain) await brain.init() for (let i = 0; i < 12; i++) { await brain.add({ data: `e${i}`, type: NounType.Thing, vector: vec(i) }) } const target = vec(0) const all = await brain.similar({ to: target, limit: 100 }) expect(all.length).toBe(12) const scores = all.map((r) => r.score) const min = Math.min(...scores) const max = Math.max(...scores) // The corpus has a real score spread (one vector is identical to the target). expect(max).toBeGreaterThan(min) const threshold = (min + max) / 2 const filtered = await brain.similar({ to: target, limit: 100, threshold }) // THE invariant the fix guarantees: every result meets the threshold. expect(filtered.every((r) => r.score >= threshold)).toBe(true) // The threshold is actually applied — weaker matches dropped, strong kept. expect(filtered.length).toBeGreaterThan(0) expect(filtered.length).toBeLessThan(all.length) }) it('returns the full set when no threshold is given (unchanged behavior)', async () => { const brain = new Brainy({ requireSubtype: false, storage: { type: 'memory' } }) brains.push(brain) await brain.init() for (let i = 0; i < 6; i++) { await brain.add({ data: `n${i}`, type: NounType.Thing, vector: vec(i + 50) }) } const all = await brain.similar({ to: vec(50), limit: 100 }) expect(all.length).toBe(6) }) })