feat(server): memory v2 — BM25 + local embedding hybrid search
- Bm25Ranker: Okapi BM25 scoring (pure TS, no deps) - Embedding module: ONNX-based local embeddings via onnxruntime-node - Hybrid recall: BM25 (30%) + cosine similarity (70%) weighted merge - Falls back to keyword-only via MEMORY_SEARCH=keyword env var - extract_memory agent tool for persisting memory entries
This commit is contained in:
44
apps/server/src/services/tools/extract_memory.ts
Normal file
44
apps/server/src/services/tools/extract_memory.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { z } from 'zod';
|
||||
import type { ToolDef } from '../tools/types.js';
|
||||
import { ensureMemoryScaffold, getMemoryRoot } from '../memory/paths.js';
|
||||
import { writeEntry } from '../memory/store.js';
|
||||
|
||||
const ExtractMemoryInput = z.object({
|
||||
topic: z.enum(['project', 'user', 'reference']).describe('Memory topic category'),
|
||||
title: z.string().min(1).max(200).describe('Entry title (will be normalized to filename)'),
|
||||
content: z.string().min(1).describe('Memory content body'),
|
||||
tags: z.array(z.string()).optional().describe('Optional tags for search'),
|
||||
});
|
||||
|
||||
type InputT = z.infer<typeof ExtractMemoryInput>;
|
||||
|
||||
export const extractMemoryTool: ToolDef<InputT> = {
|
||||
name: 'extract_memory',
|
||||
description: 'Persist a memory entry to .boocode/memory/ for cross-session recall. Use for project conventions, user preferences, and architectural decisions.',
|
||||
inputSchema: ExtractMemoryInput,
|
||||
jsonSchema: {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'extract_memory',
|
||||
description: 'Persist a memory entry for cross-session recall',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
topic: { type: 'string', enum: ['project', 'user', 'reference'] },
|
||||
title: { type: 'string', description: 'Entry title' },
|
||||
content: { type: 'string', description: 'Memory content' },
|
||||
tags: { type: 'array', items: { type: 'string' }, description: 'Search tags' },
|
||||
},
|
||||
required: ['topic', 'title', 'content'],
|
||||
},
|
||||
},
|
||||
},
|
||||
async execute(input: InputT, projectRoot: string): Promise<unknown> {
|
||||
const root = getMemoryRoot(projectRoot);
|
||||
await ensureMemoryScaffold(root);
|
||||
await writeEntry(root, input.topic, input.title, input.content, input.tags ?? []);
|
||||
return {
|
||||
result: `Memory entry "${input.title}" saved to .boocode/memory/${input.topic}/`,
|
||||
};
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user