Files
waggle-os/packages/agent/tests/w42-reranker-recall.test.ts
Oleg Maslov b20b138fe4 moving
2026-09-02 10:14:22 +02:00

118 lines
4.4 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { createInProcessReranker, MindDB, type Reranker } from '@waggle/core';
import { Orchestrator } from '../src/orchestrator.js';
import { MockEmbedder } from '../../hive-mind-core/tests/mind/helpers/mock-embedder.js';
vi.mock('@waggle/core', async (importOriginal) => ({
...await importOriginal<typeof import('@waggle/core')>(),
createInProcessReranker: vi.fn(),
}));
/**
* W4.2 — reranker wiring in recallMemory (W4-PRODUCTION-PORT-PLAN §5 W4.2).
* The orchestrator accepts an injected Reranker (tests) and otherwise
* lazy-creates one only behind WAGGLE_RERANKER=1 — flag-off default keeps
* CI free of the ~22MB model download.
*/
describe('W4.2 — recallMemory reranker wiring', () => {
let db: MindDB;
beforeEach(() => {
db = new MindDB(':memory:');
});
afterEach(() => {
db.close();
vi.clearAllMocks();
vi.unstubAllEnvs();
});
function markerReranker(marker: string, calls: { n: number }): Reranker {
const scoreOf = (doc: string): number => (doc.includes(marker) ? 10 : -10);
return {
score: async (_q, doc) => { calls.n++; return scoreOf(doc); },
scoreBatch: async (_q, docs) => { calls.n++; return docs.map(scoreOf); },
};
}
it('skips embedding and lazy reranker startup when both minds are empty', async () => {
vi.stubEnv('WAGGLE_RERANKER', '1');
const embedder = new MockEmbedder();
const embedSpy = vi.spyOn(embedder, 'embed');
const embedBatchSpy = vi.spyOn(embedder, 'embedBatch');
const workspaceDb = new MindDB(':memory:');
const orchestrator = new Orchestrator({ db, embedder });
orchestrator.setWorkspaceMind(workspaceDb);
try {
const result = await orchestrator.recallMemory('weekly report');
expect(result).toEqual({ text: '', count: 0, recalled: [], recalledFrames: [] });
expect(createInProcessReranker).not.toHaveBeenCalled();
expect(embedSpy).not.toHaveBeenCalled();
expect(embedBatchSpy).not.toHaveBeenCalled();
} finally {
workspaceDb.close();
}
});
it('uses an injected reranker to order recall results', async () => {
const calls = { n: 0 };
const orchestrator = new Orchestrator({
db,
embedder: new MockEmbedder(),
reranker: markerReranker('Fridays', calls),
});
await orchestrator.executeTool('save_memory', {
content: 'User preference: weekly report goes out on Fridays',
importance: 'normal',
});
await orchestrator.executeTool('save_memory', {
content: 'User preference: weekly report wraps before standup',
importance: 'normal',
});
const result = await orchestrator.recallMemory('weekly report');
expect(result.count).toBeGreaterThan(0);
expect(calls.n).toBeGreaterThan(0); // reranker actually consulted
const fridayIdx = result.text.indexOf('Fridays');
const standupIdx = result.text.indexOf('standup');
expect(fridayIdx).toBeGreaterThan(-1);
if (standupIdx > -1) expect(fridayIdx).toBeLessThan(standupIdx);
});
it('does not create a reranker when the kill switch is set and none injected', async () => {
process.env['WAGGLE_RERANKER'] = '0'; // kill switch (also pinned in vitest.setup)
const orchestrator = new Orchestrator({ db, embedder: new MockEmbedder() });
await orchestrator.executeTool('save_memory', {
content: 'User preference: weekly report goes out on Fridays',
importance: 'normal',
});
// Must not attempt the ~22MB model load — recall just works RRF-only.
const result = await orchestrator.recallMemory('weekly report');
expect(result.count).toBeGreaterThan(0);
});
it('passes the managed cache directory to the lazy reranker factory', async () => {
vi.stubEnv('WAGGLE_RERANKER', '1');
const calls = { n: 0 };
vi.mocked(createInProcessReranker).mockResolvedValueOnce(markerReranker('Fridays', calls));
const rerankerCacheDir = 'C:\\Waggle\\models\\reranker';
const orchestrator = new Orchestrator({
db,
embedder: new MockEmbedder(),
rerankerCacheDir,
});
await orchestrator.executeTool('save_memory', {
content: 'User preference: weekly report goes out on Fridays',
importance: 'normal',
});
await orchestrator.recallMemory('weekly report');
expect(createInProcessReranker).toHaveBeenCalledWith({ cacheDir: rerankerCacheDir });
});
});