@melandlabs/memory-store 0.1.6 → 0.2.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/dist/{config-D6MzksfT.d.ts → config-BYfTzG3h.d.ts} +17 -0
- package/dist/contracts.d.ts +7 -0
- package/dist/contracts.js +0 -2
- package/dist/http.d.ts +1 -1
- package/dist/http.js +144 -23
- package/dist/index.d.ts +2 -2
- package/dist/index.js +144 -23
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +144 -23
- package/dist/policies/memory-graph-correction-policy.js +0 -2
- package/dist/policies/memory-graph-write-policy.js +0 -2
- package/dist/search/unified-search.d.ts +39 -3
- package/dist/search/unified-search.js +145 -24
- package/dist/server/cli-http.js +144 -23
- package/dist/server/cli-mcp.js +144 -23
- package/dist/server/cli-shared.d.ts +1 -1
- package/dist/server/cli-shared.js +0 -2
- package/dist/storage/chroma-memory-index.js +0 -2
- package/dist/storage/postgres-raw-message-factory.d.ts +1 -1
- package/dist/storage/postgres-raw-message-factory.js +0 -2
- package/dist/storage/raw-message-store.d.ts +9 -1
- package/dist/storage/raw-message-store.js +0 -2
- package/dist/storage/sqlite-raw-message-store.d.ts +15 -2
- package/dist/storage/sqlite-raw-message-store.js +8 -3
- package/dist/storage/sqlite-vector-index.d.ts +1 -1
- package/dist/storage/sqlite-vector-index.js +0 -2
- package/package.json +8 -8
- package/dist/contracts.js.map +0 -1
- package/dist/http.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/mcp.js.map +0 -1
- package/dist/policies/memory-graph-correction-policy.js.map +0 -1
- package/dist/policies/memory-graph-write-policy.js.map +0 -1
- package/dist/search/unified-search.js.map +0 -1
- package/dist/server/cli-http.js.map +0 -1
- package/dist/server/cli-mcp.js.map +0 -1
- package/dist/server/cli-shared.js.map +0 -1
- package/dist/storage/chroma-memory-index.js.map +0 -1
- package/dist/storage/postgres-raw-message-factory.js.map +0 -1
- package/dist/storage/raw-message-store.js.map +0 -1
- package/dist/storage/sqlite-raw-message-store.js.map +0 -1
- package/dist/storage/sqlite-vector-index.js.map +0 -1
package/dist/server/cli-http.js
CHANGED
|
@@ -247,7 +247,14 @@ function clampUnifiedMemorySearchThreshold(threshold) {
|
|
|
247
247
|
}
|
|
248
248
|
return Math.min(1, Math.max(-1, parsed));
|
|
249
249
|
}
|
|
250
|
-
function mergeUnifiedMemorySearchResults(results, limit) {
|
|
250
|
+
function mergeUnifiedMemorySearchResults(results, limit, options = {}) {
|
|
251
|
+
const strategy = normalizeUnifiedMemoryMergeStrategy(options.strategy);
|
|
252
|
+
if (strategy === "rrf") {
|
|
253
|
+
const lists = options.rankedLists ?? [
|
|
254
|
+
{ name: "memory-semantic", hits: results }
|
|
255
|
+
];
|
|
256
|
+
return mergeUnifiedMemorySearchResultsRrf(lists, limit);
|
|
257
|
+
}
|
|
251
258
|
return [...results].sort((a, b) => {
|
|
252
259
|
const scoreDelta = b.similarity - a.similarity;
|
|
253
260
|
if (scoreDelta !== 0) {
|
|
@@ -256,6 +263,42 @@ function mergeUnifiedMemorySearchResults(results, limit) {
|
|
|
256
263
|
return a.type.localeCompare(b.type) || a.id.localeCompare(b.id);
|
|
257
264
|
}).slice(0, limit);
|
|
258
265
|
}
|
|
266
|
+
function normalizeUnifiedMemoryMergeStrategy(value) {
|
|
267
|
+
return value === "rrf" ? "rrf" : "similarity";
|
|
268
|
+
}
|
|
269
|
+
var DEFAULT_RRF_K = 60;
|
|
270
|
+
function mergeUnifiedMemorySearchResultsRrf(lists, limit, k = DEFAULT_RRF_K) {
|
|
271
|
+
if (!Array.isArray(lists) || lists.length === 0) {
|
|
272
|
+
return [];
|
|
273
|
+
}
|
|
274
|
+
const safeK = Number.isFinite(k) && k > 0 ? k : DEFAULT_RRF_K;
|
|
275
|
+
const scores = /* @__PURE__ */ new Map();
|
|
276
|
+
const order = [];
|
|
277
|
+
for (const list of lists) {
|
|
278
|
+
for (let index = 0; index < list.hits.length; index += 1) {
|
|
279
|
+
const hit = list.hits[index];
|
|
280
|
+
const key = `${hit.type}::${hit.id}`;
|
|
281
|
+
const contribution = 1 / (safeK + index + 1);
|
|
282
|
+
const existing = scores.get(key);
|
|
283
|
+
if (existing) {
|
|
284
|
+
existing.rrf += contribution;
|
|
285
|
+
} else {
|
|
286
|
+
scores.set(key, { hit, rrf: contribution });
|
|
287
|
+
order.push(key);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
const ranked = order.map((key) => scores.get(key)).sort((a, b) => {
|
|
292
|
+
if (b.rrf !== a.rrf) {
|
|
293
|
+
return b.rrf - a.rrf;
|
|
294
|
+
}
|
|
295
|
+
return a.hit.type.localeCompare(b.hit.type) || a.hit.id.localeCompare(b.hit.id);
|
|
296
|
+
}).slice(0, limit);
|
|
297
|
+
return ranked.map(({ hit, rrf }) => ({
|
|
298
|
+
...hit,
|
|
299
|
+
metadata: { ...hit.metadata, rrfScore: rrf }
|
|
300
|
+
}));
|
|
301
|
+
}
|
|
259
302
|
function toKnowledgeResult(result) {
|
|
260
303
|
return {
|
|
261
304
|
type: "knowledge",
|
|
@@ -287,6 +330,9 @@ function isRawMemorySemanticResult(result) {
|
|
|
287
330
|
}
|
|
288
331
|
|
|
289
332
|
// src/search/unified-search.ts
|
|
333
|
+
function deriveLexicalKeywords(query) {
|
|
334
|
+
return query.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((token) => token.length >= 2).slice(0, 16);
|
|
335
|
+
}
|
|
290
336
|
function createUnifiedSearch(deps = {}) {
|
|
291
337
|
const logger = deps.logger ?? console;
|
|
292
338
|
async function searchUnifiedMemory(input) {
|
|
@@ -294,8 +340,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
294
340
|
const sources = normalizeUnifiedMemorySearchSources(input.sources);
|
|
295
341
|
const limit = clampUnifiedMemorySearchLimit(input.limit);
|
|
296
342
|
const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
|
|
343
|
+
const mergeStrategy = normalizeUnifiedMemoryMergeStrategy(input.mergeStrategy);
|
|
297
344
|
const warnings = [];
|
|
298
|
-
const results = [];
|
|
299
345
|
if (!query) {
|
|
300
346
|
return {
|
|
301
347
|
query,
|
|
@@ -305,11 +351,11 @@ function createUnifiedSearch(deps = {}) {
|
|
|
305
351
|
warnings
|
|
306
352
|
};
|
|
307
353
|
}
|
|
354
|
+
let memorySubs;
|
|
308
355
|
if (sources.includes("memory")) {
|
|
309
356
|
if (isRawMessageStorageAvailable()) {
|
|
310
357
|
try {
|
|
311
|
-
|
|
312
|
-
results.push(...memoryHits);
|
|
358
|
+
memorySubs = await runMemorySource(input, limit, threshold, warnings);
|
|
313
359
|
} catch (error) {
|
|
314
360
|
logger.warn?.("[memory-store] memory source failed:", error);
|
|
315
361
|
warnings.push({
|
|
@@ -326,9 +372,10 @@ function createUnifiedSearch(deps = {}) {
|
|
|
326
372
|
});
|
|
327
373
|
}
|
|
328
374
|
}
|
|
375
|
+
const insightHits = [];
|
|
329
376
|
if (sources.includes("insights") && typeof deps.searchInsights === "function") {
|
|
330
377
|
try {
|
|
331
|
-
const
|
|
378
|
+
const hits = await deps.searchInsights({
|
|
332
379
|
userId: input.userId,
|
|
333
380
|
query: input.query,
|
|
334
381
|
limit,
|
|
@@ -337,8 +384,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
337
384
|
includeArchived: input.includeArchivedInsights,
|
|
338
385
|
authToken: input.authToken
|
|
339
386
|
});
|
|
340
|
-
for (const hit of
|
|
341
|
-
|
|
387
|
+
for (const hit of hits) {
|
|
388
|
+
insightHits.push({
|
|
342
389
|
type: "insight",
|
|
343
390
|
id: hit.id,
|
|
344
391
|
content: hit.content,
|
|
@@ -361,9 +408,10 @@ function createUnifiedSearch(deps = {}) {
|
|
|
361
408
|
message: "Insights search is not configured for this host."
|
|
362
409
|
});
|
|
363
410
|
}
|
|
411
|
+
const knowledgeHits = [];
|
|
364
412
|
if (sources.includes("knowledge") && typeof deps.searchKnowledge === "function") {
|
|
365
413
|
try {
|
|
366
|
-
const
|
|
414
|
+
const hits = await deps.searchKnowledge({
|
|
367
415
|
userId: input.userId,
|
|
368
416
|
query: input.query,
|
|
369
417
|
options: {
|
|
@@ -373,8 +421,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
373
421
|
},
|
|
374
422
|
authToken: input.authToken
|
|
375
423
|
});
|
|
376
|
-
for (const hit of
|
|
377
|
-
|
|
424
|
+
for (const hit of hits) {
|
|
425
|
+
knowledgeHits.push(toKnowledgeResult(hit));
|
|
378
426
|
}
|
|
379
427
|
} catch (error) {
|
|
380
428
|
logger.warn?.("[memory-store] knowledge source failed:", error);
|
|
@@ -391,7 +439,13 @@ function createUnifiedSearch(deps = {}) {
|
|
|
391
439
|
message: "Knowledge search is not configured for this host."
|
|
392
440
|
});
|
|
393
441
|
}
|
|
394
|
-
const merged =
|
|
442
|
+
const merged = mergeAcrossSources({
|
|
443
|
+
memorySubs,
|
|
444
|
+
insightHits,
|
|
445
|
+
knowledgeHits,
|
|
446
|
+
limit,
|
|
447
|
+
strategy: mergeStrategy
|
|
448
|
+
});
|
|
395
449
|
return {
|
|
396
450
|
query: input.query,
|
|
397
451
|
sources,
|
|
@@ -400,7 +454,7 @@ function createUnifiedSearch(deps = {}) {
|
|
|
400
454
|
warnings
|
|
401
455
|
};
|
|
402
456
|
}
|
|
403
|
-
async function runMemorySource(input, limit, threshold) {
|
|
457
|
+
async function runMemorySource(input, limit, threshold, warnings) {
|
|
404
458
|
if (typeof deps.embedQuery !== "function") {
|
|
405
459
|
throw new Error("embedQuery is not configured");
|
|
406
460
|
}
|
|
@@ -410,9 +464,10 @@ function createUnifiedSearch(deps = {}) {
|
|
|
410
464
|
authToken: input.authToken
|
|
411
465
|
});
|
|
412
466
|
const filters = input.botIds && input.botIds.length > 0 ? input.botIds.map((botId) => ({ botId })) : [{}];
|
|
467
|
+
let semantic = [];
|
|
413
468
|
if (isRawMessageChromaEnabled()) {
|
|
414
469
|
try {
|
|
415
|
-
|
|
470
|
+
semantic = (await Promise.all(
|
|
416
471
|
filters.map((filter) => {
|
|
417
472
|
const botId = "botId" in filter ? filter.botId : void 0;
|
|
418
473
|
return searchRawMessagesWithChroma({
|
|
@@ -427,18 +482,16 @@ function createUnifiedSearch(deps = {}) {
|
|
|
427
482
|
logger.log?.("[memory-store] Raw message semantic search completed", {
|
|
428
483
|
backend: "chroma",
|
|
429
484
|
dimensions: queryEmbedding.length,
|
|
430
|
-
count:
|
|
485
|
+
count: semantic.length
|
|
431
486
|
});
|
|
432
|
-
return chromaHits;
|
|
433
487
|
} catch (error) {
|
|
434
488
|
logger.warn?.(
|
|
435
489
|
"[memory-store] Chroma raw message search failed; falling back to database search:",
|
|
436
490
|
error
|
|
437
491
|
);
|
|
438
492
|
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
const rows = (await Promise.all(
|
|
493
|
+
} else if (typeof deps.searchRawMessagesAnn === "function") {
|
|
494
|
+
semantic = (await Promise.all(
|
|
442
495
|
filters.map(
|
|
443
496
|
(filter) => deps.searchRawMessagesAnn({
|
|
444
497
|
userId: input.userId,
|
|
@@ -449,17 +502,87 @@ function createUnifiedSearch(deps = {}) {
|
|
|
449
502
|
})
|
|
450
503
|
)
|
|
451
504
|
)).flat().filter(isRawMemorySemanticResult).map(toMemoryResult);
|
|
452
|
-
return rows;
|
|
453
505
|
}
|
|
454
|
-
|
|
506
|
+
let lexical = [];
|
|
507
|
+
if (typeof deps.searchRawMessagesLexical === "function") {
|
|
508
|
+
const keywords = deriveLexicalKeywords(input.query);
|
|
509
|
+
if (keywords.length > 0) {
|
|
510
|
+
try {
|
|
511
|
+
const lexFilters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
|
|
512
|
+
lexical = (await Promise.all(
|
|
513
|
+
lexFilters.map(
|
|
514
|
+
(botId) => deps.searchRawMessagesLexical({
|
|
515
|
+
userId: input.userId,
|
|
516
|
+
keywords,
|
|
517
|
+
limit,
|
|
518
|
+
botId
|
|
519
|
+
})
|
|
520
|
+
)
|
|
521
|
+
)).flat().filter(isRawMemorySemanticResult).map((hit) => ({
|
|
522
|
+
...toMemoryResult(hit),
|
|
523
|
+
metadata: { ...hit.metadata, scoring: "bm25" }
|
|
524
|
+
}));
|
|
525
|
+
} catch (error) {
|
|
526
|
+
logger.warn?.("[memory-store] lexical memory search failed:", error);
|
|
527
|
+
warnings.push({
|
|
528
|
+
source: "memory",
|
|
529
|
+
code: "memory_lexical_search_failed",
|
|
530
|
+
message: error.message ?? "memory_lexical_search_failed"
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
} else if (input.mergeStrategy === "rrf") {
|
|
535
|
+
warnings.push({
|
|
536
|
+
source: "memory",
|
|
537
|
+
code: "memory_lexical_search_not_configured",
|
|
538
|
+
message: "RRF merge requested but lexical search is not configured; falling back to semantic-only RRF."
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
return { semantic, lexical };
|
|
455
542
|
}
|
|
456
543
|
async function searchRawMemorySemantically(input) {
|
|
457
544
|
const limit = clampUnifiedMemorySearchLimit(input.limit);
|
|
458
545
|
const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
|
|
459
|
-
|
|
546
|
+
const sub = await runMemorySource(input, limit, threshold, []);
|
|
547
|
+
return mergeAcrossSources({
|
|
548
|
+
memorySubs: sub,
|
|
549
|
+
insightHits: [],
|
|
550
|
+
knowledgeHits: [],
|
|
551
|
+
limit,
|
|
552
|
+
strategy: normalizeUnifiedMemoryMergeStrategy(input.mergeStrategy)
|
|
553
|
+
});
|
|
460
554
|
}
|
|
461
555
|
return { searchUnifiedMemory, searchRawMemorySemantically };
|
|
462
556
|
}
|
|
557
|
+
function mergeAcrossSources(input) {
|
|
558
|
+
const memorySubs = input.memorySubs ?? { semantic: [], lexical: [] };
|
|
559
|
+
const all = [
|
|
560
|
+
...memorySubs.semantic,
|
|
561
|
+
...memorySubs.lexical,
|
|
562
|
+
...input.insightHits,
|
|
563
|
+
...input.knowledgeHits
|
|
564
|
+
];
|
|
565
|
+
if (input.strategy !== "rrf") {
|
|
566
|
+
return mergeUnifiedMemorySearchResults(all, input.limit);
|
|
567
|
+
}
|
|
568
|
+
const lists = [];
|
|
569
|
+
if (memorySubs.semantic.length > 0) {
|
|
570
|
+
lists.push({ name: "memory-semantic", hits: memorySubs.semantic });
|
|
571
|
+
}
|
|
572
|
+
if (memorySubs.lexical.length > 0) {
|
|
573
|
+
lists.push({ name: "memory-bm25", hits: memorySubs.lexical });
|
|
574
|
+
}
|
|
575
|
+
if (input.insightHits.length > 0) {
|
|
576
|
+
lists.push({ name: "insights", hits: input.insightHits });
|
|
577
|
+
}
|
|
578
|
+
if (input.knowledgeHits.length > 0) {
|
|
579
|
+
lists.push({ name: "knowledge", hits: input.knowledgeHits });
|
|
580
|
+
}
|
|
581
|
+
if (lists.length <= 1) {
|
|
582
|
+
return mergeUnifiedMemorySearchResults(all, input.limit);
|
|
583
|
+
}
|
|
584
|
+
return mergeUnifiedMemorySearchResultsRrf(lists, input.limit);
|
|
585
|
+
}
|
|
463
586
|
|
|
464
587
|
// src/http.ts
|
|
465
588
|
async function embedMissingMessages(messages, deps) {
|
|
@@ -934,5 +1057,3 @@ main().catch((error) => {
|
|
|
934
1057
|
console.error("[memory-store/http] fatal:", error);
|
|
935
1058
|
process.exit(1);
|
|
936
1059
|
});
|
|
937
|
-
//# sourceMappingURL=cli-http.js.map
|
|
938
|
-
//# sourceMappingURL=cli-http.js.map
|
package/dist/server/cli-mcp.js
CHANGED
|
@@ -248,7 +248,14 @@ function clampUnifiedMemorySearchThreshold(threshold) {
|
|
|
248
248
|
}
|
|
249
249
|
return Math.min(1, Math.max(-1, parsed));
|
|
250
250
|
}
|
|
251
|
-
function mergeUnifiedMemorySearchResults(results, limit) {
|
|
251
|
+
function mergeUnifiedMemorySearchResults(results, limit, options = {}) {
|
|
252
|
+
const strategy = normalizeUnifiedMemoryMergeStrategy(options.strategy);
|
|
253
|
+
if (strategy === "rrf") {
|
|
254
|
+
const lists = options.rankedLists ?? [
|
|
255
|
+
{ name: "memory-semantic", hits: results }
|
|
256
|
+
];
|
|
257
|
+
return mergeUnifiedMemorySearchResultsRrf(lists, limit);
|
|
258
|
+
}
|
|
252
259
|
return [...results].sort((a, b) => {
|
|
253
260
|
const scoreDelta = b.similarity - a.similarity;
|
|
254
261
|
if (scoreDelta !== 0) {
|
|
@@ -257,6 +264,42 @@ function mergeUnifiedMemorySearchResults(results, limit) {
|
|
|
257
264
|
return a.type.localeCompare(b.type) || a.id.localeCompare(b.id);
|
|
258
265
|
}).slice(0, limit);
|
|
259
266
|
}
|
|
267
|
+
function normalizeUnifiedMemoryMergeStrategy(value) {
|
|
268
|
+
return value === "rrf" ? "rrf" : "similarity";
|
|
269
|
+
}
|
|
270
|
+
var DEFAULT_RRF_K = 60;
|
|
271
|
+
function mergeUnifiedMemorySearchResultsRrf(lists, limit, k = DEFAULT_RRF_K) {
|
|
272
|
+
if (!Array.isArray(lists) || lists.length === 0) {
|
|
273
|
+
return [];
|
|
274
|
+
}
|
|
275
|
+
const safeK = Number.isFinite(k) && k > 0 ? k : DEFAULT_RRF_K;
|
|
276
|
+
const scores = /* @__PURE__ */ new Map();
|
|
277
|
+
const order = [];
|
|
278
|
+
for (const list of lists) {
|
|
279
|
+
for (let index = 0; index < list.hits.length; index += 1) {
|
|
280
|
+
const hit = list.hits[index];
|
|
281
|
+
const key = `${hit.type}::${hit.id}`;
|
|
282
|
+
const contribution = 1 / (safeK + index + 1);
|
|
283
|
+
const existing = scores.get(key);
|
|
284
|
+
if (existing) {
|
|
285
|
+
existing.rrf += contribution;
|
|
286
|
+
} else {
|
|
287
|
+
scores.set(key, { hit, rrf: contribution });
|
|
288
|
+
order.push(key);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
const ranked = order.map((key) => scores.get(key)).sort((a, b) => {
|
|
293
|
+
if (b.rrf !== a.rrf) {
|
|
294
|
+
return b.rrf - a.rrf;
|
|
295
|
+
}
|
|
296
|
+
return a.hit.type.localeCompare(b.hit.type) || a.hit.id.localeCompare(b.hit.id);
|
|
297
|
+
}).slice(0, limit);
|
|
298
|
+
return ranked.map(({ hit, rrf }) => ({
|
|
299
|
+
...hit,
|
|
300
|
+
metadata: { ...hit.metadata, rrfScore: rrf }
|
|
301
|
+
}));
|
|
302
|
+
}
|
|
260
303
|
function toKnowledgeResult(result) {
|
|
261
304
|
return {
|
|
262
305
|
type: "knowledge",
|
|
@@ -288,6 +331,9 @@ function isRawMemorySemanticResult(result) {
|
|
|
288
331
|
}
|
|
289
332
|
|
|
290
333
|
// src/search/unified-search.ts
|
|
334
|
+
function deriveLexicalKeywords(query) {
|
|
335
|
+
return query.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((token) => token.length >= 2).slice(0, 16);
|
|
336
|
+
}
|
|
291
337
|
function createUnifiedSearch(deps = {}) {
|
|
292
338
|
const logger = deps.logger ?? console;
|
|
293
339
|
async function searchUnifiedMemory(input) {
|
|
@@ -295,8 +341,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
295
341
|
const sources = normalizeUnifiedMemorySearchSources(input.sources);
|
|
296
342
|
const limit = clampUnifiedMemorySearchLimit(input.limit);
|
|
297
343
|
const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
|
|
344
|
+
const mergeStrategy = normalizeUnifiedMemoryMergeStrategy(input.mergeStrategy);
|
|
298
345
|
const warnings = [];
|
|
299
|
-
const results = [];
|
|
300
346
|
if (!query) {
|
|
301
347
|
return {
|
|
302
348
|
query,
|
|
@@ -306,11 +352,11 @@ function createUnifiedSearch(deps = {}) {
|
|
|
306
352
|
warnings
|
|
307
353
|
};
|
|
308
354
|
}
|
|
355
|
+
let memorySubs;
|
|
309
356
|
if (sources.includes("memory")) {
|
|
310
357
|
if (isRawMessageStorageAvailable()) {
|
|
311
358
|
try {
|
|
312
|
-
|
|
313
|
-
results.push(...memoryHits);
|
|
359
|
+
memorySubs = await runMemorySource(input, limit, threshold, warnings);
|
|
314
360
|
} catch (error) {
|
|
315
361
|
logger.warn?.("[memory-store] memory source failed:", error);
|
|
316
362
|
warnings.push({
|
|
@@ -327,9 +373,10 @@ function createUnifiedSearch(deps = {}) {
|
|
|
327
373
|
});
|
|
328
374
|
}
|
|
329
375
|
}
|
|
376
|
+
const insightHits = [];
|
|
330
377
|
if (sources.includes("insights") && typeof deps.searchInsights === "function") {
|
|
331
378
|
try {
|
|
332
|
-
const
|
|
379
|
+
const hits = await deps.searchInsights({
|
|
333
380
|
userId: input.userId,
|
|
334
381
|
query: input.query,
|
|
335
382
|
limit,
|
|
@@ -338,8 +385,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
338
385
|
includeArchived: input.includeArchivedInsights,
|
|
339
386
|
authToken: input.authToken
|
|
340
387
|
});
|
|
341
|
-
for (const hit of
|
|
342
|
-
|
|
388
|
+
for (const hit of hits) {
|
|
389
|
+
insightHits.push({
|
|
343
390
|
type: "insight",
|
|
344
391
|
id: hit.id,
|
|
345
392
|
content: hit.content,
|
|
@@ -362,9 +409,10 @@ function createUnifiedSearch(deps = {}) {
|
|
|
362
409
|
message: "Insights search is not configured for this host."
|
|
363
410
|
});
|
|
364
411
|
}
|
|
412
|
+
const knowledgeHits = [];
|
|
365
413
|
if (sources.includes("knowledge") && typeof deps.searchKnowledge === "function") {
|
|
366
414
|
try {
|
|
367
|
-
const
|
|
415
|
+
const hits = await deps.searchKnowledge({
|
|
368
416
|
userId: input.userId,
|
|
369
417
|
query: input.query,
|
|
370
418
|
options: {
|
|
@@ -374,8 +422,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
374
422
|
},
|
|
375
423
|
authToken: input.authToken
|
|
376
424
|
});
|
|
377
|
-
for (const hit of
|
|
378
|
-
|
|
425
|
+
for (const hit of hits) {
|
|
426
|
+
knowledgeHits.push(toKnowledgeResult(hit));
|
|
379
427
|
}
|
|
380
428
|
} catch (error) {
|
|
381
429
|
logger.warn?.("[memory-store] knowledge source failed:", error);
|
|
@@ -392,7 +440,13 @@ function createUnifiedSearch(deps = {}) {
|
|
|
392
440
|
message: "Knowledge search is not configured for this host."
|
|
393
441
|
});
|
|
394
442
|
}
|
|
395
|
-
const merged =
|
|
443
|
+
const merged = mergeAcrossSources({
|
|
444
|
+
memorySubs,
|
|
445
|
+
insightHits,
|
|
446
|
+
knowledgeHits,
|
|
447
|
+
limit,
|
|
448
|
+
strategy: mergeStrategy
|
|
449
|
+
});
|
|
396
450
|
return {
|
|
397
451
|
query: input.query,
|
|
398
452
|
sources,
|
|
@@ -401,7 +455,7 @@ function createUnifiedSearch(deps = {}) {
|
|
|
401
455
|
warnings
|
|
402
456
|
};
|
|
403
457
|
}
|
|
404
|
-
async function runMemorySource(input, limit, threshold) {
|
|
458
|
+
async function runMemorySource(input, limit, threshold, warnings) {
|
|
405
459
|
if (typeof deps.embedQuery !== "function") {
|
|
406
460
|
throw new Error("embedQuery is not configured");
|
|
407
461
|
}
|
|
@@ -411,9 +465,10 @@ function createUnifiedSearch(deps = {}) {
|
|
|
411
465
|
authToken: input.authToken
|
|
412
466
|
});
|
|
413
467
|
const filters = input.botIds && input.botIds.length > 0 ? input.botIds.map((botId) => ({ botId })) : [{}];
|
|
468
|
+
let semantic = [];
|
|
414
469
|
if (isRawMessageChromaEnabled()) {
|
|
415
470
|
try {
|
|
416
|
-
|
|
471
|
+
semantic = (await Promise.all(
|
|
417
472
|
filters.map((filter) => {
|
|
418
473
|
const botId = "botId" in filter ? filter.botId : void 0;
|
|
419
474
|
return searchRawMessagesWithChroma({
|
|
@@ -428,18 +483,16 @@ function createUnifiedSearch(deps = {}) {
|
|
|
428
483
|
logger.log?.("[memory-store] Raw message semantic search completed", {
|
|
429
484
|
backend: "chroma",
|
|
430
485
|
dimensions: queryEmbedding.length,
|
|
431
|
-
count:
|
|
486
|
+
count: semantic.length
|
|
432
487
|
});
|
|
433
|
-
return chromaHits;
|
|
434
488
|
} catch (error) {
|
|
435
489
|
logger.warn?.(
|
|
436
490
|
"[memory-store] Chroma raw message search failed; falling back to database search:",
|
|
437
491
|
error
|
|
438
492
|
);
|
|
439
493
|
}
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
const rows = (await Promise.all(
|
|
494
|
+
} else if (typeof deps.searchRawMessagesAnn === "function") {
|
|
495
|
+
semantic = (await Promise.all(
|
|
443
496
|
filters.map(
|
|
444
497
|
(filter) => deps.searchRawMessagesAnn({
|
|
445
498
|
userId: input.userId,
|
|
@@ -450,17 +503,87 @@ function createUnifiedSearch(deps = {}) {
|
|
|
450
503
|
})
|
|
451
504
|
)
|
|
452
505
|
)).flat().filter(isRawMemorySemanticResult).map(toMemoryResult);
|
|
453
|
-
return rows;
|
|
454
506
|
}
|
|
455
|
-
|
|
507
|
+
let lexical = [];
|
|
508
|
+
if (typeof deps.searchRawMessagesLexical === "function") {
|
|
509
|
+
const keywords = deriveLexicalKeywords(input.query);
|
|
510
|
+
if (keywords.length > 0) {
|
|
511
|
+
try {
|
|
512
|
+
const lexFilters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
|
|
513
|
+
lexical = (await Promise.all(
|
|
514
|
+
lexFilters.map(
|
|
515
|
+
(botId) => deps.searchRawMessagesLexical({
|
|
516
|
+
userId: input.userId,
|
|
517
|
+
keywords,
|
|
518
|
+
limit,
|
|
519
|
+
botId
|
|
520
|
+
})
|
|
521
|
+
)
|
|
522
|
+
)).flat().filter(isRawMemorySemanticResult).map((hit) => ({
|
|
523
|
+
...toMemoryResult(hit),
|
|
524
|
+
metadata: { ...hit.metadata, scoring: "bm25" }
|
|
525
|
+
}));
|
|
526
|
+
} catch (error) {
|
|
527
|
+
logger.warn?.("[memory-store] lexical memory search failed:", error);
|
|
528
|
+
warnings.push({
|
|
529
|
+
source: "memory",
|
|
530
|
+
code: "memory_lexical_search_failed",
|
|
531
|
+
message: error.message ?? "memory_lexical_search_failed"
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
} else if (input.mergeStrategy === "rrf") {
|
|
536
|
+
warnings.push({
|
|
537
|
+
source: "memory",
|
|
538
|
+
code: "memory_lexical_search_not_configured",
|
|
539
|
+
message: "RRF merge requested but lexical search is not configured; falling back to semantic-only RRF."
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
return { semantic, lexical };
|
|
456
543
|
}
|
|
457
544
|
async function searchRawMemorySemantically(input) {
|
|
458
545
|
const limit = clampUnifiedMemorySearchLimit(input.limit);
|
|
459
546
|
const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
|
|
460
|
-
|
|
547
|
+
const sub = await runMemorySource(input, limit, threshold, []);
|
|
548
|
+
return mergeAcrossSources({
|
|
549
|
+
memorySubs: sub,
|
|
550
|
+
insightHits: [],
|
|
551
|
+
knowledgeHits: [],
|
|
552
|
+
limit,
|
|
553
|
+
strategy: normalizeUnifiedMemoryMergeStrategy(input.mergeStrategy)
|
|
554
|
+
});
|
|
461
555
|
}
|
|
462
556
|
return { searchUnifiedMemory, searchRawMemorySemantically };
|
|
463
557
|
}
|
|
558
|
+
function mergeAcrossSources(input) {
|
|
559
|
+
const memorySubs = input.memorySubs ?? { semantic: [], lexical: [] };
|
|
560
|
+
const all = [
|
|
561
|
+
...memorySubs.semantic,
|
|
562
|
+
...memorySubs.lexical,
|
|
563
|
+
...input.insightHits,
|
|
564
|
+
...input.knowledgeHits
|
|
565
|
+
];
|
|
566
|
+
if (input.strategy !== "rrf") {
|
|
567
|
+
return mergeUnifiedMemorySearchResults(all, input.limit);
|
|
568
|
+
}
|
|
569
|
+
const lists = [];
|
|
570
|
+
if (memorySubs.semantic.length > 0) {
|
|
571
|
+
lists.push({ name: "memory-semantic", hits: memorySubs.semantic });
|
|
572
|
+
}
|
|
573
|
+
if (memorySubs.lexical.length > 0) {
|
|
574
|
+
lists.push({ name: "memory-bm25", hits: memorySubs.lexical });
|
|
575
|
+
}
|
|
576
|
+
if (input.insightHits.length > 0) {
|
|
577
|
+
lists.push({ name: "insights", hits: input.insightHits });
|
|
578
|
+
}
|
|
579
|
+
if (input.knowledgeHits.length > 0) {
|
|
580
|
+
lists.push({ name: "knowledge", hits: input.knowledgeHits });
|
|
581
|
+
}
|
|
582
|
+
if (lists.length <= 1) {
|
|
583
|
+
return mergeUnifiedMemorySearchResults(all, input.limit);
|
|
584
|
+
}
|
|
585
|
+
return mergeUnifiedMemorySearchResultsRrf(lists, input.limit);
|
|
586
|
+
}
|
|
464
587
|
|
|
465
588
|
// src/mcp.ts
|
|
466
589
|
var DEFAULT_NAME = "@melandlabs/memory-store";
|
|
@@ -976,5 +1099,3 @@ main().catch((error) => {
|
|
|
976
1099
|
console.error("[memory-store/mcp] fatal:", error);
|
|
977
1100
|
process.exit(1);
|
|
978
1101
|
});
|
|
979
|
-
//# sourceMappingURL=cli-mcp.js.map
|
|
980
|
-
//# sourceMappingURL=cli-mcp.js.map
|
|
@@ -245,5 +245,3 @@ async function searchInsightsWithChroma(input) {
|
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
export { isInsightChromaEnabled, isRawMessageChromaEnabled, searchInsightsWithChroma, searchRawMessagesWithChroma, upsertInsightsToChroma, upsertRawMessagesToChroma };
|
|
248
|
-
//# sourceMappingURL=chroma-memory-index.js.map
|
|
249
|
-
//# sourceMappingURL=chroma-memory-index.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RawMessageStorageManager } from '@melandlabs/indexeddb/storage';
|
|
2
|
-
import { b as MemoryStoreEnv, M as MemoryStoreConfig } from '../config-
|
|
2
|
+
import { b as MemoryStoreEnv, M as MemoryStoreConfig } from '../config-BYfTzG3h.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Top-level raw-message store facade.
|
|
@@ -34,6 +34,14 @@ type RawMessageStorageManagerWithSearch = RawMessageStorageManager & {
|
|
|
34
34
|
startTime?: number;
|
|
35
35
|
endTime?: number;
|
|
36
36
|
}) => Promise<unknown[]>;
|
|
37
|
+
lexicalSearchMessages?: (input: {
|
|
38
|
+
userId: string;
|
|
39
|
+
keywords: string[];
|
|
40
|
+
limit?: number;
|
|
41
|
+
includeArchived?: boolean;
|
|
42
|
+
platform?: string;
|
|
43
|
+
botId?: string;
|
|
44
|
+
}) => Promise<unknown[]>;
|
|
37
45
|
};
|
|
38
46
|
interface CreateRawMessageStoreOptions {
|
|
39
47
|
env?: MemoryStoreEnv;
|
|
@@ -125,5 +125,3 @@ async function closeRawMessageStore() {
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
export { closeRawMessageStore, configureRawMessageStore, createRawMessageStore, getRawMessageManager, getRawMessageStorageBackend, isRawMessageStorageAvailable };
|
|
128
|
-
//# sourceMappingURL=raw-message-store.js.map
|
|
129
|
-
//# sourceMappingURL=raw-message-store.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SQLiteRawMessageManager } from '@melandlabs/sqlite';
|
|
2
|
-
import { b as MemoryStoreEnv } from '../config-
|
|
2
|
+
import { b as MemoryStoreEnv } from '../config-BYfTzG3h.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* SQLite-backed raw message store.
|
|
@@ -30,5 +30,18 @@ declare function closeSQLiteRawMessageManager(): Promise<void>;
|
|
|
30
30
|
* Test-only: reset the singleton. Used by vitest between test cases.
|
|
31
31
|
*/
|
|
32
32
|
declare function __resetSQLiteRawMessageManagerForTests(): void;
|
|
33
|
+
/**
|
|
34
|
+
* BM25 lexical proxy over the SQLite FTS5 index. Mirrors
|
|
35
|
+
* `SQLiteRawMessageManager.lexicalSearchMessages` so callers downstream of the
|
|
36
|
+
* raw-message facade can opt in to a second ranking signal.
|
|
37
|
+
*/
|
|
38
|
+
declare function lexicalSearchRawMessages(input: {
|
|
39
|
+
userId: string;
|
|
40
|
+
keywords: string[];
|
|
41
|
+
limit?: number;
|
|
42
|
+
includeArchived?: boolean;
|
|
43
|
+
platform?: string;
|
|
44
|
+
botId?: string;
|
|
45
|
+
}): Promise<unknown[]>;
|
|
33
46
|
|
|
34
|
-
export { __resetSQLiteRawMessageManagerForTests, closeSQLiteRawMessageManager, getSQLiteRawMessageManager, isSQLiteRawMessageStorageAvailable, resolveSQLiteRawMessageDbPath };
|
|
47
|
+
export { __resetSQLiteRawMessageManagerForTests, closeSQLiteRawMessageManager, getSQLiteRawMessageManager, isSQLiteRawMessageStorageAvailable, lexicalSearchRawMessages, resolveSQLiteRawMessageDbPath };
|
|
@@ -56,7 +56,12 @@ async function closeSQLiteRawMessageManager() {
|
|
|
56
56
|
function __resetSQLiteRawMessageManagerForTests() {
|
|
57
57
|
manager = null;
|
|
58
58
|
}
|
|
59
|
+
async function lexicalSearchRawMessages(input) {
|
|
60
|
+
const mgr = await getSQLiteRawMessageManager();
|
|
61
|
+
if (typeof mgr.lexicalSearchMessages !== "function") {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
return mgr.lexicalSearchMessages(input);
|
|
65
|
+
}
|
|
59
66
|
|
|
60
|
-
export { __resetSQLiteRawMessageManagerForTests, closeSQLiteRawMessageManager, getSQLiteRawMessageManager, isSQLiteRawMessageStorageAvailable, resolveSQLiteRawMessageDbPath };
|
|
61
|
-
//# sourceMappingURL=sqlite-raw-message-store.js.map
|
|
62
|
-
//# sourceMappingURL=sqlite-raw-message-store.js.map
|
|
67
|
+
export { __resetSQLiteRawMessageManagerForTests, closeSQLiteRawMessageManager, getSQLiteRawMessageManager, isSQLiteRawMessageStorageAvailable, lexicalSearchRawMessages, resolveSQLiteRawMessageDbPath };
|