@lotargo/memory_plugin 1.6.6 → 1.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +148 -101
- package/README.md +436 -304
- package/mcp-server/cli/direct_commands.js +39 -0
- package/mcp-server/cli.js +16 -5
- package/mcp-server/cli_boot.js +4 -1
- package/mcp-server/client_cli.js +73 -0
- package/mcp-server/client_paths.js +44 -0
- package/mcp-server/client_registration.js +38 -0
- package/mcp-server/codex_config.js +86 -8
- package/mcp-server/db/database.js +14 -21
- package/mcp-server/db/migrations.js +66 -77
- package/mcp-server/db/rag_blob_transport.js +143 -0
- package/mcp-server/db/rag_sync.js +284 -0
- package/mcp-server/db/sync_queue.js +219 -307
- package/mcp-server/dev_link.js +142 -0
- package/mcp-server/fact_format.js +44 -12
- package/mcp-server/index.js +17 -7
- package/mcp-server/ingest/exporter.js +44 -38
- package/mcp-server/ingest/normalizer.js +1 -1
- package/mcp-server/ingest/pipeline.js +260 -248
- package/mcp-server/persona_migration.js +39 -0
- package/mcp-server/prompt_manager.js +162 -55
- package/mcp-server/retrieval/retriever.js +99 -64
- package/mcp-server/setup.js +150 -100
- package/mcp-server/storage/blob_store.js +53 -1
- package/mcp-server/tools/core/knowledge_read_core.js +163 -0
- package/mcp-server/tools/core/memory_core.js +24 -4
- package/mcp-server/tools/core/memory_routing.js +10 -0
- package/mcp-server/tools/core/note_core.js +53 -0
- package/mcp-server/tools/core/rag_query_core.js +169 -0
- package/mcp-server/tools/index.js +11 -9
- package/mcp-server/tools/memory_tools.js +4 -1
- package/mcp-server/tools/note_tools.js +35 -0
- package/mcp-server/tools/rag_tools.js +211 -364
- package/mcp-server/uninstall.js +627 -0
- package/opencode-plugin/index.js +80 -12
- package/opencode-plugin/main.js +136 -0
- package/package.json +25 -5
- package/skills/using-memory/SKILL.md +28 -19
- package/mcp-server/benchmarks/fetch_real_corpus.js +0 -351
- package/mcp-server/benchmarks/gpu_profile_benchmark.js +0 -170
- package/mcp-server/benchmarks/policy_dominance_test.js +0 -221
- package/mcp-server/benchmarks/quality_evaluator.js +0 -598
- package/mcp-server/benchmarks/raw_corpus_data.js +0 -613
- package/mcp-server/benchmarks/run_benchmarks.js +0 -366
- package/mcp-server/benchmarks/stress_ingestion.js +0 -195
- package/mcp-server/benchmarks/table_code_retrieval.js +0 -453
- package/mcp-server/benchmarks/test_dual_layer.js +0 -141
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
import { tmpdir } from "node:os";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { rmSync, existsSync } from "node:fs";
|
|
4
|
-
import { getDatabase } from "../db/database.js";
|
|
5
|
-
import { ingestDocument } from "../ingest/pipeline.js";
|
|
6
|
-
import { embedText } from "../ml/model_manager.js";
|
|
7
|
-
import { hybridQuery } from "../retrieval/retriever.js";
|
|
8
|
-
import { RAW_CORPUS, POLICY_DOMINANCE_QUERIES } from "./raw_corpus_data.js";
|
|
9
|
-
|
|
10
|
-
const K = 5;
|
|
11
|
-
|
|
12
|
-
function chunkTypeLabel(retrievalPolicy) {
|
|
13
|
-
if (retrievalPolicy === "table_summary") return "TABLE";
|
|
14
|
-
if (retrievalPolicy === "code_signature") return "CODE";
|
|
15
|
-
return "MICRO";
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async function ingestCorpus(db, blobDir) {
|
|
19
|
-
let totalSections = 0;
|
|
20
|
-
let totalMicro = 0;
|
|
21
|
-
|
|
22
|
-
for (const doc of RAW_CORPUS) {
|
|
23
|
-
const res = await ingestDocument({
|
|
24
|
-
content: doc.content,
|
|
25
|
-
type: "text",
|
|
26
|
-
title: doc.title,
|
|
27
|
-
path: `benchmark://${doc.id}`,
|
|
28
|
-
generateEmbeddings: true,
|
|
29
|
-
customDb: db,
|
|
30
|
-
customBlobDir: blobDir,
|
|
31
|
-
});
|
|
32
|
-
totalSections += res.sectionsCount;
|
|
33
|
-
totalMicro += res.microChunksCount;
|
|
34
|
-
if (global.gc) global.gc();
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const policyRow = await db.prepare(
|
|
38
|
-
`SELECT COUNT(*) as cnt FROM micro_chunks WHERE retrieval_policy IN ('table_summary', 'code_signature')`
|
|
39
|
-
).get();
|
|
40
|
-
const totalPolicy = policyRow?.cnt || 0;
|
|
41
|
-
|
|
42
|
-
return { totalSections, totalMicro, totalPolicy };
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async function runMode(db, query, mode, alpha = 0.5) {
|
|
46
|
-
const hits = await hybridQuery({
|
|
47
|
-
query: query.query,
|
|
48
|
-
limit: K,
|
|
49
|
-
customDb: db,
|
|
50
|
-
fusionAlgorithm: mode,
|
|
51
|
-
alpha,
|
|
52
|
-
generateEmbeddings: true,
|
|
53
|
-
includeGraphContext: false,
|
|
54
|
-
});
|
|
55
|
-
return hits;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function analyzeChunkTypes(hits) {
|
|
59
|
-
let policyCount = 0;
|
|
60
|
-
let microCount = 0;
|
|
61
|
-
const types = [];
|
|
62
|
-
for (const hit of hits) {
|
|
63
|
-
const isPolicy = hit.retrieval_policy === "table_summary" || hit.retrieval_policy === "code_signature";
|
|
64
|
-
if (isPolicy) {
|
|
65
|
-
policyCount++;
|
|
66
|
-
types.push(chunkTypeLabel(hit.retrieval_policy));
|
|
67
|
-
} else {
|
|
68
|
-
microCount++;
|
|
69
|
-
types.push("MICRO");
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return { policyCount, microCount, types };
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function renderResultsTable(results) {
|
|
76
|
-
const wQ = 42;
|
|
77
|
-
const wMode = 12;
|
|
78
|
-
const wTypes = 32;
|
|
79
|
-
const wWinner = 8;
|
|
80
|
-
|
|
81
|
-
const sep = `├${"─".repeat(wQ + 2)}┼${"─".repeat(wMode + 2)}┼${"─".repeat(wTypes + 2)}┼${"─".repeat(wWinner + 2)}┤`;
|
|
82
|
-
const top = `┌${"─".repeat(wQ + 2)}┬${"─".repeat(wMode + 2)}┬${"─".repeat(wTypes + 2)}┬${"─".repeat(wWinner + 2)}┐`;
|
|
83
|
-
const bot = `└${"─".repeat(wQ + 2)}┴${"─".repeat(wMode + 2)}┴${"─".repeat(wTypes + 2)}┴${"─".repeat(wWinner + 2)}┘`;
|
|
84
|
-
|
|
85
|
-
console.log(`\x1b[36m${top}\x1b[0m`);
|
|
86
|
-
console.log(`\x1b[36m│\x1b[0m \x1b[1m${"Query".padEnd(wQ)}\x1b[0m \x1b[36m│\x1b[0m \x1b[1m${"Mode".padEnd(wMode)}\x1b[0m \x1b[36m│\x1b[0m \x1b[1m${"Chunk Types (top-5)".padEnd(wTypes)}\x1b[0m \x1b[36m│\x1b[0m \x1b[1m${"Winner".padEnd(wWinner)}\x1b[0m \x1b[36m│\x1b[0m`);
|
|
87
|
-
console.log(`\x1b[36m${sep}\x1b[0m`);
|
|
88
|
-
|
|
89
|
-
for (const r of results) {
|
|
90
|
-
const qShort = r.query.length > wQ ? r.query.substring(0, wQ - 3) + "..." : r.query;
|
|
91
|
-
const typesStr = r.types.join(", ") || "(none)";
|
|
92
|
-
const winner = r.policyCount > r.microCount ? "\x1b[33mPOLICY\x1b[0m" : r.microCount > 0 ? "\x1b[32mMICRO\x1b[0m" : "—";
|
|
93
|
-
console.log(
|
|
94
|
-
`\x1b[36m│\x1b[0m ${qShort.padEnd(wQ)} \x1b[36m│\x1b[0m ${r.mode.padEnd(wMode)} \x1b[36m│\x1b[0m ${typesStr.padEnd(wTypes)} \x1b[36m│\x1b[0m ${winner.padEnd(wWinner)} \x1b[36m│\x1b[0m`
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
console.log(`\x1b[36m${bot}\x1b[0m`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
async function main() {
|
|
101
|
-
const line = "─".repeat(60);
|
|
102
|
-
console.log(`\x1b[36m╭${line}╮\x1b[0m`);
|
|
103
|
-
console.log(`\x1b[36m│\x1b[0m \x1b[1m\x1b[37mPOLICY DOMINANCE BENCHMARK\x1b[0m`.padEnd(72) + `\x1b[36m│\x1b[0m`);
|
|
104
|
-
console.log(`\x1b[36m│\x1b[0m \x1b[90mRaw docs: tables + code + text | chunk-type distribution\x1b[0m`.padEnd(72) + `\x1b[36m│\x1b[0m`);
|
|
105
|
-
console.log(`\x1b[36m╰${line}╯\x1b[0m`);
|
|
106
|
-
|
|
107
|
-
const TEST_DIR = join(tmpdir(), `memory_policy_bench_${Date.now()}`);
|
|
108
|
-
const TEST_DB_PATH = join(TEST_DIR, "bench_memory.sqlite");
|
|
109
|
-
const TEST_BLOB_DIR = join(TEST_DIR, "blobs");
|
|
110
|
-
|
|
111
|
-
const db = await getDatabase(TEST_DB_PATH);
|
|
112
|
-
|
|
113
|
-
console.log("\n[INGEST] Processing raw corpus...");
|
|
114
|
-
const ingestMeta = await ingestCorpus(db, TEST_BLOB_DIR);
|
|
115
|
-
console.log(` Sections: ${ingestMeta.totalSections}, Micro-chunks: ${ingestMeta.totalMicro}, Policy chunks: ${ingestMeta.totalPolicy}`);
|
|
116
|
-
|
|
117
|
-
const modes = ["bm25_only", "vector_only", "rrf", "rsf"];
|
|
118
|
-
const allResults = [];
|
|
119
|
-
|
|
120
|
-
// Per-mode aggregation
|
|
121
|
-
const modeStats = {};
|
|
122
|
-
for (const mode of modes) modeStats[mode] = { policy: 0, micro: 0, total: 0 };
|
|
123
|
-
|
|
124
|
-
// Per-category aggregation
|
|
125
|
-
const catStats = {};
|
|
126
|
-
|
|
127
|
-
console.log("\n[EVAL] Running queries across modes...");
|
|
128
|
-
for (const q of POLICY_DOMINANCE_QUERIES) {
|
|
129
|
-
const cat = q.expectedDocIds[0].split("_")[0];
|
|
130
|
-
if (!catStats[cat]) catStats[cat] = { policy: 0, micro: 0, total: 0 };
|
|
131
|
-
|
|
132
|
-
for (const mode of modes) {
|
|
133
|
-
const hits = await runMode(db, q, mode);
|
|
134
|
-
const analysis = analyzeChunkTypes(hits);
|
|
135
|
-
|
|
136
|
-
allResults.push({
|
|
137
|
-
query: q.query,
|
|
138
|
-
mode,
|
|
139
|
-
types: analysis.types,
|
|
140
|
-
policyCount: analysis.policyCount,
|
|
141
|
-
microCount: analysis.microCount,
|
|
142
|
-
expectedWinner: q.expectedWinner,
|
|
143
|
-
queryType: q.query_type,
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
modeStats[mode].policy += analysis.policyCount;
|
|
147
|
-
modeStats[mode].micro += analysis.microCount;
|
|
148
|
-
modeStats[mode].total += hits.length;
|
|
149
|
-
|
|
150
|
-
catStats[cat].policy += analysis.policyCount;
|
|
151
|
-
catStats[cat].micro += analysis.microCount;
|
|
152
|
-
catStats[cat].total += hits.length;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Render detailed table
|
|
157
|
-
console.log("\n[RESULTS] Chunk-type distribution per query per mode:");
|
|
158
|
-
renderResultsTable(allResults);
|
|
159
|
-
|
|
160
|
-
// Render mode summary
|
|
161
|
-
console.log("\n[SUMMARY] Aggregate chunk-type ratio per mode:");
|
|
162
|
-
console.log("┌────────────┬─────────┬─────────┬──────────┐");
|
|
163
|
-
console.log("│ Mode │ Policy │ Micro │ Policy % │");
|
|
164
|
-
console.log("├────────────┼─────────┼─────────┼──────────┤");
|
|
165
|
-
for (const mode of modes) {
|
|
166
|
-
const s = modeStats[mode];
|
|
167
|
-
const pct = s.total > 0 ? ((s.policy / s.total) * 100).toFixed(1) : "0.0";
|
|
168
|
-
console.log(`│ ${mode.padEnd(10)} │ ${String(s.policy).padStart(7)} │ ${String(s.micro).padStart(7)} │ ${pct.padStart(7)}% │`);
|
|
169
|
-
}
|
|
170
|
-
console.log("└────────────┴─────────┴─────────┴──────────┘");
|
|
171
|
-
|
|
172
|
-
// Render category summary
|
|
173
|
-
console.log("\n[CATEGORY] Chunk-type ratio by document category:");
|
|
174
|
-
console.log("┌──────────────────┬─────────┬─────────┬──────────┐");
|
|
175
|
-
console.log("│ Category │ Policy │ Micro │ Policy % │");
|
|
176
|
-
console.log("├──────────────────┼─────────┼─────────┼──────────┤");
|
|
177
|
-
for (const [cat, s] of Object.entries(catStats)) {
|
|
178
|
-
const pct = s.total > 0 ? ((s.policy / s.total) * 100).toFixed(1) : "0.0";
|
|
179
|
-
console.log(`│ ${cat.padEnd(16)} │ ${String(s.policy).padStart(7)} │ ${String(s.micro).padStart(7)} │ ${pct.padStart(7)}% │`);
|
|
180
|
-
}
|
|
181
|
-
console.log("└──────────────────┴─────────┴─────────┴──────────┘");
|
|
182
|
-
|
|
183
|
-
// Diagnosis
|
|
184
|
-
console.log("\n[DIAGNOSIS]");
|
|
185
|
-
const allPolicy = Object.values(modeStats).reduce((a, m) => a + m.policy, 0);
|
|
186
|
-
const allMicro = Object.values(modeStats).reduce((a, m) => a + m.micro, 0);
|
|
187
|
-
const allTotal = allPolicy + allMicro;
|
|
188
|
-
const globalPct = allTotal > 0 ? ((allPolicy / allTotal) * 100).toFixed(1) : "0.0";
|
|
189
|
-
console.log(` Global policy dominance: ${globalPct}% (${allPolicy}/${allTotal} chunks)`);
|
|
190
|
-
|
|
191
|
-
if (Number(globalPct) > 80) {
|
|
192
|
-
console.log(" ⚠️ Policy chunks dominate — benchmark confirms the issue.");
|
|
193
|
-
console.log(" → Consider: separate policy/micro slots (Option A) or per-source dedup (Option B)");
|
|
194
|
-
} else if (Number(globalPct) < 40) {
|
|
195
|
-
console.log(" ✅ Micro chunks dominate — policy expansion is conservative.");
|
|
196
|
-
} else {
|
|
197
|
-
console.log(" ✅ Balanced distribution — both chunk types compete fairly.");
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Check if modes differ
|
|
201
|
-
const modePcts = modes.map(m => {
|
|
202
|
-
const s = modeStats[m];
|
|
203
|
-
return s.total > 0 ? (s.policy / s.total) : 0;
|
|
204
|
-
});
|
|
205
|
-
const modeSpread = Math.max(...modePcts) - Math.min(...modePcts);
|
|
206
|
-
console.log(` Mode spread: ${(modeSpread * 100).toFixed(1)}% (max policy% - min policy% across modes)`);
|
|
207
|
-
if (modeSpread < 0.1) {
|
|
208
|
-
console.log(" ⚠️ All modes produce similar distributions — modes are not differentiated.");
|
|
209
|
-
} else {
|
|
210
|
-
console.log(" ✅ Modes produce different distributions — benchmark can distinguish them.");
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Cleanup
|
|
214
|
-
try { db.close(); } catch {}
|
|
215
|
-
if (existsSync(TEST_DIR)) rmSync(TEST_DIR, { recursive: true, force: true });
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
main().catch(err => {
|
|
219
|
-
console.error("Benchmark failed:", err);
|
|
220
|
-
process.exit(1);
|
|
221
|
-
});
|