@lotargo/memory_plugin 1.3.2 → 1.4.0
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/README.md +334 -324
- package/mcp-server/admin/auth.js +90 -0
- package/mcp-server/admin/snapshot.js +34 -32
- package/mcp-server/cli.js +109 -12
- package/mcp-server/config/auth_store.js +101 -0
- package/mcp-server/config/config_manager.js +3 -0
- package/mcp-server/db/database.js +180 -13
- package/mcp-server/db/migrations.js +62 -33
- package/mcp-server/db/sync_queue.js +211 -0
- package/mcp-server/graph/graph_extractor.js +40 -17
- package/mcp-server/graph/knowledge_linker.js +14 -14
- package/mcp-server/index.js +26 -17
- package/mcp-server/ingest/exporter.js +12 -12
- package/mcp-server/ingest/normalizer.js +102 -5
- package/mcp-server/ingest/pipeline.js +53 -29
- package/mcp-server/memory.js +73 -0
- package/mcp-server/retrieval/retriever.js +24 -15
- package/package.json +6 -2
|
@@ -11,7 +11,7 @@ export function sanitizeFtsQuery(query) {
|
|
|
11
11
|
return words.join(" OR ");
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export function bm25Search(db, query, limit = 30) {
|
|
14
|
+
export async function bm25Search(db, query, limit = 30) {
|
|
15
15
|
const ftsQuery = sanitizeFtsQuery(query);
|
|
16
16
|
if (!ftsQuery) return [];
|
|
17
17
|
|
|
@@ -23,7 +23,7 @@ export function bm25Search(db, query, limit = 30) {
|
|
|
23
23
|
ORDER BY rank
|
|
24
24
|
LIMIT ?;
|
|
25
25
|
`);
|
|
26
|
-
const rows = stmt.all(ftsQuery, limit);
|
|
26
|
+
const rows = await stmt.all(ftsQuery, limit);
|
|
27
27
|
return rows.map((r, i) => ({
|
|
28
28
|
id: r.id,
|
|
29
29
|
content: r.content,
|
|
@@ -37,7 +37,7 @@ export function bm25Search(db, query, limit = 30) {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
export function vectorSearch(db, queryVector, limit = 30, minSim = 0.25) {
|
|
40
|
+
export async function vectorSearch(db, queryVector, limit = 30, minSim = 0.25) {
|
|
41
41
|
if (!queryVector || queryVector.length === 0) return [];
|
|
42
42
|
|
|
43
43
|
const vectorDim = queryVector.length;
|
|
@@ -52,8 +52,17 @@ export function vectorSearch(db, queryVector, limit = 30, minSim = 0.25) {
|
|
|
52
52
|
`);
|
|
53
53
|
|
|
54
54
|
const scored = [];
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
const rows = await stmt.all();
|
|
56
|
+
for (const r of rows) {
|
|
57
|
+
let vecSub = r.vector;
|
|
58
|
+
if (typeof vecSub === "string") {
|
|
59
|
+
vecSub = Buffer.from(vecSub, "base64");
|
|
60
|
+
} else if (vecSub.type === "Buffer" && Array.isArray(vecSub.data)) {
|
|
61
|
+
vecSub = Buffer.from(vecSub.data);
|
|
62
|
+
} else if (Array.isArray(vecSub)) {
|
|
63
|
+
vecSub = Buffer.from(vecSub);
|
|
64
|
+
}
|
|
65
|
+
tempView.set(vecSub.subarray(0, vectorDim * 4));
|
|
57
66
|
|
|
58
67
|
const sim = cosineSimilarity(queryVector, tempVec);
|
|
59
68
|
if (!isNaN(sim) && sim >= minSim) {
|
|
@@ -205,7 +214,7 @@ export async function hybridQuery({
|
|
|
205
214
|
instruction = null,
|
|
206
215
|
generateEmbeddings = true,
|
|
207
216
|
}) {
|
|
208
|
-
const db = customDb || getDatabase();
|
|
217
|
+
const db = customDb || await getDatabase();
|
|
209
218
|
const activeConfig = getConfig();
|
|
210
219
|
|
|
211
220
|
// If embeddings are disabled (e.g. fast/offline test mode or model not cached),
|
|
@@ -223,28 +232,28 @@ export async function hybridQuery({
|
|
|
223
232
|
let fusedHits = [];
|
|
224
233
|
|
|
225
234
|
if (algo === "lexical_only" || algo === "bm25_only") {
|
|
226
|
-
const bm25Hits = bm25Search(db, query, limit * 4);
|
|
235
|
+
const bm25Hits = await bm25Search(db, query, limit * 4);
|
|
227
236
|
fusedHits = bm25Hits.map((hit) => ({
|
|
228
237
|
...hit,
|
|
229
238
|
score: 1.0 / hit.bm25_rank,
|
|
230
239
|
}));
|
|
231
240
|
} else if (algo === "semantic_only" || algo === "vector_only") {
|
|
232
241
|
const queryVector = await embedText(query, true, embModel, null, instruction);
|
|
233
|
-
const vectorHits = vectorSearch(db, queryVector, limit * 4, 0.10);
|
|
242
|
+
const vectorHits = await vectorSearch(db, queryVector, limit * 4, 0.10);
|
|
234
243
|
fusedHits = vectorHits.map((hit) => ({
|
|
235
244
|
...hit,
|
|
236
245
|
score: hit.cosine_sim,
|
|
237
246
|
}));
|
|
238
247
|
} else if (algo === "rrf") {
|
|
239
|
-
const bm25Hits = bm25Search(db, query, 30);
|
|
248
|
+
const bm25Hits = await bm25Search(db, query, 30);
|
|
240
249
|
const queryVector = await embedText(query, true, embModel, null, instruction);
|
|
241
|
-
const vectorHits = vectorSearch(db, queryVector, 30, 0.10);
|
|
250
|
+
const vectorHits = await vectorSearch(db, queryVector, 30, 0.10);
|
|
242
251
|
fusedHits = rrfFusion(bm25Hits, vectorHits, 60, scoreThreshold);
|
|
243
252
|
} else {
|
|
244
253
|
// Default: RSF
|
|
245
|
-
const bm25Hits = bm25Search(db, query, 30);
|
|
254
|
+
const bm25Hits = await bm25Search(db, query, 30);
|
|
246
255
|
const queryVector = await embedText(query, true, embModel, null, instruction);
|
|
247
|
-
const vectorHits = vectorSearch(db, queryVector, 30, 0.10);
|
|
256
|
+
const vectorHits = await vectorSearch(db, queryVector, 30, 0.10);
|
|
248
257
|
fusedHits = rsfFusion(bm25Hits, vectorHits, alphaWeight, scoreThreshold);
|
|
249
258
|
}
|
|
250
259
|
|
|
@@ -260,7 +269,7 @@ export async function hybridQuery({
|
|
|
260
269
|
`);
|
|
261
270
|
|
|
262
271
|
for (const hit of fusedHits) {
|
|
263
|
-
const row = parentLookupStmt.get(hit.id);
|
|
272
|
+
const row = await parentLookupStmt.get(hit.id);
|
|
264
273
|
const parentKey = row ? (row.medium_id || row.section_id) : hit.id;
|
|
265
274
|
if (!seenParents.has(parentKey)) {
|
|
266
275
|
seenParents.add(parentKey);
|
|
@@ -282,12 +291,12 @@ export async function hybridQuery({
|
|
|
282
291
|
`);
|
|
283
292
|
|
|
284
293
|
for (const hit of topHits) {
|
|
285
|
-
const detail = secStmt.get(hit.id);
|
|
294
|
+
const detail = await secStmt.get(hit.id);
|
|
286
295
|
if (!detail) continue;
|
|
287
296
|
|
|
288
297
|
let symbols = [];
|
|
289
298
|
if (includeGraphContext) {
|
|
290
|
-
symbols = getRelatedSymbols(db, detail.section_id);
|
|
299
|
+
symbols = await getRelatedSymbols(db, detail.section_id);
|
|
291
300
|
}
|
|
292
301
|
|
|
293
302
|
results.push({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lotargo/memory_plugin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "100% local hybrid RAG memory for AI coding agents (OpenCode, Claude Code, Codex, Antigravity). MCP server + plugin: persistent user facts, document ingestion, vector + SQLite FTS5 retrieval across sessions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "opencode-plugin/index.js",
|
|
@@ -68,8 +68,12 @@
|
|
|
68
68
|
"url": "https://github.com/Lotargo/memory_pugin.git"
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
72
71
|
"@huggingface/transformers": "^3.3.3",
|
|
72
|
+
"@libsql/client": "^0.17.4",
|
|
73
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
74
|
+
"mammoth": "^1.12.0",
|
|
75
|
+
"pdf-parse": "^2.4.5",
|
|
76
|
+
"xlsx": "^0.18.5",
|
|
73
77
|
"zod": "^4.1.0"
|
|
74
78
|
}
|
|
75
79
|
}
|