@melandlabs/memory-store 0.2.6 → 0.3.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-2Q092MUz.d.ts +470 -0
- package/dist/http.d.ts +1 -1
- package/dist/http.js +386 -105
- package/dist/index.d.ts +3 -3
- package/dist/index.js +745 -107
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +389 -105
- package/dist/search/unified-search.d.ts +3 -101
- package/dist/search/unified-search.js +387 -106
- package/dist/server/cli-http.js +395 -105
- package/dist/server/cli-mcp.js +398 -105
- package/dist/server/cli-shared.d.ts +1 -1
- package/dist/server/cli-shared.js +17 -5
- package/dist/storage/postgres-raw-message-factory.d.ts +1 -1
- package/dist/storage/raw-message-store.d.ts +3 -1
- package/dist/storage/raw-message-store.js +20 -9
- package/dist/storage/sqlite-raw-message-store.d.ts +6 -3
- package/dist/storage/sqlite-raw-message-store.js +4 -3
- package/dist/storage/sqlite-vector-index.d.ts +1 -1
- package/dist/storage/sqlite-vector-index.js +1 -1
- package/package.json +10 -9
- package/dist/config-BYfTzG3h.d.ts +0 -170
package/dist/http.js
CHANGED
|
@@ -183,16 +183,17 @@ __export(sqlite_raw_message_store_exports, {
|
|
|
183
183
|
lexicalSearchRawMessages: () => lexicalSearchRawMessages,
|
|
184
184
|
resolveSQLiteRawMessageDbPath: () => resolveSQLiteRawMessageDbPath
|
|
185
185
|
});
|
|
186
|
-
function resolveSQLiteRawMessageDbPath() {
|
|
186
|
+
function resolveSQLiteRawMessageDbPath(dbPath) {
|
|
187
|
+
if (dbPath && dbPath.length > 0) return dbPath;
|
|
187
188
|
const fromEnv = process.env.MEMORY_STORE_DB_PATH?.trim();
|
|
188
189
|
return fromEnv && fromEnv.length > 0 ? fromEnv : DEFAULT_SQLITE_PATH;
|
|
189
190
|
}
|
|
190
191
|
function isSQLiteRawMessageStorageAvailable(_env) {
|
|
191
192
|
return true;
|
|
192
193
|
}
|
|
193
|
-
async function getSQLiteRawMessageManager(
|
|
194
|
+
async function getSQLiteRawMessageManager(options = {}) {
|
|
194
195
|
if (!manager) {
|
|
195
|
-
const dbPath = resolveSQLiteRawMessageDbPath();
|
|
196
|
+
const dbPath = resolveSQLiteRawMessageDbPath(options.dbPath);
|
|
196
197
|
mkdirSync(dirname(dbPath), { recursive: true });
|
|
197
198
|
manager = new SQLiteRawMessageManager({
|
|
198
199
|
dbPath,
|
|
@@ -238,7 +239,7 @@ __export(raw_message_store_exports, {
|
|
|
238
239
|
getRawMessageStorageBackend: () => getRawMessageStorageBackend,
|
|
239
240
|
isRawMessageStorageAvailable: () => isRawMessageStorageAvailable
|
|
240
241
|
});
|
|
241
|
-
function resolveBackend(
|
|
242
|
+
function resolveBackend(_env) {
|
|
242
243
|
const explicit = process.env.OPENCONTEXT_MEMORY_STORE_BACKEND?.trim().toLowerCase();
|
|
243
244
|
if (explicit === "postgres") return "postgres";
|
|
244
245
|
return "sqlite";
|
|
@@ -265,33 +266,43 @@ function createRawMessageStore(options = {}) {
|
|
|
265
266
|
}
|
|
266
267
|
return pg;
|
|
267
268
|
}
|
|
268
|
-
return await getSQLiteRawMessageManager(
|
|
269
|
+
return await getSQLiteRawMessageManager({
|
|
270
|
+
dbPath: options.dbPath
|
|
271
|
+
});
|
|
269
272
|
},
|
|
270
273
|
async close() {
|
|
271
274
|
await closeSQLiteRawMessageManager();
|
|
272
275
|
}
|
|
273
276
|
};
|
|
274
277
|
}
|
|
278
|
+
function resolveDbPath(config) {
|
|
279
|
+
if (!config) return void 0;
|
|
280
|
+
if (config.dbPath) return config.dbPath;
|
|
281
|
+
if (config.db && typeof config.db === "object" && "path" in config.db) {
|
|
282
|
+
return config.db.path;
|
|
283
|
+
}
|
|
284
|
+
return void 0;
|
|
285
|
+
}
|
|
275
286
|
function configureRawMessageStore(config) {
|
|
276
287
|
moduleConfig = config;
|
|
277
|
-
moduleStore = createRawMessageStore({ env: config.env });
|
|
288
|
+
moduleStore = createRawMessageStore({ env: config.env, dbPath: resolveDbPath(config) });
|
|
278
289
|
return moduleStore;
|
|
279
290
|
}
|
|
280
291
|
function getRawMessageStorageBackend() {
|
|
281
292
|
if (!moduleStore) {
|
|
282
|
-
moduleStore = createRawMessageStore({ env: moduleConfig?.env });
|
|
293
|
+
moduleStore = createRawMessageStore({ env: moduleConfig?.env, dbPath: resolveDbPath(moduleConfig) });
|
|
283
294
|
}
|
|
284
295
|
return moduleStore.getBackend();
|
|
285
296
|
}
|
|
286
297
|
function isRawMessageStorageAvailable() {
|
|
287
298
|
if (!moduleStore) {
|
|
288
|
-
moduleStore = createRawMessageStore({ env: moduleConfig?.env });
|
|
299
|
+
moduleStore = createRawMessageStore({ env: moduleConfig?.env, dbPath: resolveDbPath(moduleConfig) });
|
|
289
300
|
}
|
|
290
301
|
return moduleStore.isAvailable();
|
|
291
302
|
}
|
|
292
303
|
async function getRawMessageManager() {
|
|
293
304
|
if (!moduleStore) {
|
|
294
|
-
moduleStore = createRawMessageStore({ env: moduleConfig?.env });
|
|
305
|
+
moduleStore = createRawMessageStore({ env: moduleConfig?.env, dbPath: resolveDbPath(moduleConfig) });
|
|
295
306
|
}
|
|
296
307
|
return moduleStore.getManager();
|
|
297
308
|
}
|
|
@@ -361,6 +372,12 @@ function mergeUnifiedMemorySearchResults(results, limit, options = {}) {
|
|
|
361
372
|
function normalizeUnifiedMemoryMergeStrategy(value) {
|
|
362
373
|
return value === "rrf" ? "rrf" : "similarity";
|
|
363
374
|
}
|
|
375
|
+
function normalizeUnifiedMemoryReasoningStrategy(value, defaultStrategy = "none") {
|
|
376
|
+
if (value === "rewrite" || value === "iterative") {
|
|
377
|
+
return value;
|
|
378
|
+
}
|
|
379
|
+
return defaultStrategy;
|
|
380
|
+
}
|
|
364
381
|
var DEFAULT_RRF_K = 60;
|
|
365
382
|
function mergeUnifiedMemorySearchResultsRrf(lists, limit, k = DEFAULT_RRF_K) {
|
|
366
383
|
if (!Array.isArray(lists) || lists.length === 0) {
|
|
@@ -383,7 +400,13 @@ function mergeUnifiedMemorySearchResultsRrf(lists, limit, k = DEFAULT_RRF_K) {
|
|
|
383
400
|
}
|
|
384
401
|
}
|
|
385
402
|
}
|
|
386
|
-
const ranked = order.map((key) =>
|
|
403
|
+
const ranked = order.map((key) => {
|
|
404
|
+
const entry = scores.get(key);
|
|
405
|
+
if (!entry) {
|
|
406
|
+
throw new Error(`Invariant violation: missing RRF score for key "${key}"`);
|
|
407
|
+
}
|
|
408
|
+
return entry;
|
|
409
|
+
}).sort((a, b) => {
|
|
387
410
|
if (b.rrf !== a.rrf) {
|
|
388
411
|
return b.rrf - a.rrf;
|
|
389
412
|
}
|
|
@@ -428,14 +451,222 @@ function isRawMemorySemanticResult(result) {
|
|
|
428
451
|
function deriveLexicalKeywords(query) {
|
|
429
452
|
return query.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((token) => token.length >= 2).slice(0, 16);
|
|
430
453
|
}
|
|
454
|
+
function parseDateBoundary(value, role) {
|
|
455
|
+
const trimmed = value.trim();
|
|
456
|
+
if (!trimmed) {
|
|
457
|
+
return void 0;
|
|
458
|
+
}
|
|
459
|
+
const dateOnlyMatch = trimmed.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
460
|
+
if (dateOnlyMatch) {
|
|
461
|
+
const year = Number.parseInt(dateOnlyMatch[1], 10);
|
|
462
|
+
const month = Number.parseInt(dateOnlyMatch[2], 10) - 1;
|
|
463
|
+
const day = Number.parseInt(dateOnlyMatch[3], 10);
|
|
464
|
+
if (role === "from") {
|
|
465
|
+
return Date.UTC(year, month, day);
|
|
466
|
+
}
|
|
467
|
+
return Date.UTC(year, month, day + 1) - 1;
|
|
468
|
+
}
|
|
469
|
+
const parsed = Date.parse(trimmed);
|
|
470
|
+
if (Number.isNaN(parsed)) {
|
|
471
|
+
return void 0;
|
|
472
|
+
}
|
|
473
|
+
return parsed;
|
|
474
|
+
}
|
|
475
|
+
function getCandidateTimestamp(metadata) {
|
|
476
|
+
for (const key of ["timestamp", "createdAt", "time"]) {
|
|
477
|
+
const value = metadata[key];
|
|
478
|
+
if (typeof value === "number") {
|
|
479
|
+
return value;
|
|
480
|
+
}
|
|
481
|
+
if (typeof value === "string") {
|
|
482
|
+
const parsed = Date.parse(value);
|
|
483
|
+
if (!Number.isNaN(parsed)) {
|
|
484
|
+
return parsed;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
return void 0;
|
|
489
|
+
}
|
|
490
|
+
function filterByDateRange(items, dateFrom, dateTo) {
|
|
491
|
+
if (!dateFrom && !dateTo) {
|
|
492
|
+
return items;
|
|
493
|
+
}
|
|
494
|
+
const fromMs = dateFrom ? parseDateBoundary(dateFrom, "from") : void 0;
|
|
495
|
+
const toMs = dateTo ? parseDateBoundary(dateTo, "to") : void 0;
|
|
496
|
+
if (fromMs === void 0 && toMs === void 0) {
|
|
497
|
+
return items;
|
|
498
|
+
}
|
|
499
|
+
return items.filter((item) => {
|
|
500
|
+
const ts = getCandidateTimestamp(item.metadata);
|
|
501
|
+
if (ts === void 0) {
|
|
502
|
+
return true;
|
|
503
|
+
}
|
|
504
|
+
if (fromMs !== void 0 && ts < fromMs) {
|
|
505
|
+
return false;
|
|
506
|
+
}
|
|
507
|
+
if (toMs !== void 0 && ts > toMs) {
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
return true;
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
async function embedQueryVariant(embedQuery, input, query) {
|
|
514
|
+
return embedQuery({
|
|
515
|
+
userId: input.userId,
|
|
516
|
+
query,
|
|
517
|
+
authToken: input.authToken
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
async function runSemanticSearchForEmbedding(deps, input, queryEmbedding, limit, threshold, logger) {
|
|
521
|
+
const filters = input.botIds && input.botIds.length > 0 ? input.botIds.map((botId) => ({ botId })) : [{}];
|
|
522
|
+
let semantic = [];
|
|
523
|
+
if (isRawMessageChromaEnabled()) {
|
|
524
|
+
try {
|
|
525
|
+
semantic = (await Promise.all(
|
|
526
|
+
filters.map((filter) => {
|
|
527
|
+
const botId = "botId" in filter ? filter.botId : void 0;
|
|
528
|
+
return searchRawMessagesWithChroma({
|
|
529
|
+
userId: input.userId,
|
|
530
|
+
queryEmbedding,
|
|
531
|
+
limit,
|
|
532
|
+
threshold,
|
|
533
|
+
botId
|
|
534
|
+
});
|
|
535
|
+
})
|
|
536
|
+
)).flat().map(toMemoryResult);
|
|
537
|
+
logger.log?.("[memory-store] Raw message semantic search completed", {
|
|
538
|
+
backend: "chroma",
|
|
539
|
+
dimensions: queryEmbedding.length,
|
|
540
|
+
count: semantic.length
|
|
541
|
+
});
|
|
542
|
+
} catch (error) {
|
|
543
|
+
logger.warn?.(
|
|
544
|
+
"[memory-store] Chroma raw message search failed; falling back to database search:",
|
|
545
|
+
error
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
} else if (typeof deps.searchRawMessagesAnn === "function") {
|
|
549
|
+
const searchRawMessagesAnn = deps.searchRawMessagesAnn;
|
|
550
|
+
semantic = (await Promise.all(
|
|
551
|
+
filters.map(
|
|
552
|
+
(filter) => searchRawMessagesAnn({
|
|
553
|
+
userId: input.userId,
|
|
554
|
+
queryEmbedding,
|
|
555
|
+
limit,
|
|
556
|
+
threshold,
|
|
557
|
+
botId: "botId" in filter ? filter.botId : void 0
|
|
558
|
+
})
|
|
559
|
+
)
|
|
560
|
+
)).flat().filter(isRawMemorySemanticResult).map(toMemoryResult);
|
|
561
|
+
}
|
|
562
|
+
if (semantic.length === 0) {
|
|
563
|
+
try {
|
|
564
|
+
const { getRawMessageManager: getRawMessageManager2 } = await Promise.resolve().then(() => (init_raw_message_store(), raw_message_store_exports));
|
|
565
|
+
const manager2 = await getRawMessageManager2();
|
|
566
|
+
if (typeof manager2.searchMessagesSemantically === "function") {
|
|
567
|
+
const results = await manager2.searchMessagesSemantically({
|
|
568
|
+
userId: input.userId,
|
|
569
|
+
queryEmbedding,
|
|
570
|
+
limit,
|
|
571
|
+
threshold
|
|
572
|
+
});
|
|
573
|
+
semantic = results.map((r) => ({
|
|
574
|
+
type: "memory",
|
|
575
|
+
id: r.id,
|
|
576
|
+
content: r.content,
|
|
577
|
+
similarity: r.similarity,
|
|
578
|
+
metadata: r.metadata ?? {}
|
|
579
|
+
}));
|
|
580
|
+
}
|
|
581
|
+
} catch (error) {
|
|
582
|
+
logger.warn?.("[memory-store] SQLite semantic search failed:", error);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
return semantic;
|
|
586
|
+
}
|
|
587
|
+
function mergeByMaxScore(lists) {
|
|
588
|
+
const best = /* @__PURE__ */ new Map();
|
|
589
|
+
for (const list of lists) {
|
|
590
|
+
for (const hit of list) {
|
|
591
|
+
const existing = best.get(hit.id);
|
|
592
|
+
if (!existing || hit.similarity > existing.similarity) {
|
|
593
|
+
best.set(hit.id, hit);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
return Array.from(best.values()).sort((a, b) => b.similarity - a.similarity);
|
|
598
|
+
}
|
|
599
|
+
function toIterativeRecallCandidate(hit) {
|
|
600
|
+
return {
|
|
601
|
+
id: hit.id,
|
|
602
|
+
content: hit.content,
|
|
603
|
+
similarity: hit.similarity,
|
|
604
|
+
metadata: hit.metadata
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
async function runLexicalSearchForKeywords(deps, input, keywords, limit, logger) {
|
|
608
|
+
if (keywords.length === 0) {
|
|
609
|
+
return [];
|
|
610
|
+
}
|
|
611
|
+
if (typeof deps.searchRawMessagesLexical === "function") {
|
|
612
|
+
try {
|
|
613
|
+
const filters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
|
|
614
|
+
const searchRawMessagesLexical = deps.searchRawMessagesLexical;
|
|
615
|
+
return (await Promise.all(
|
|
616
|
+
filters.map(
|
|
617
|
+
(botId) => searchRawMessagesLexical({
|
|
618
|
+
userId: input.userId,
|
|
619
|
+
keywords,
|
|
620
|
+
limit: Math.ceil(limit / filters.length),
|
|
621
|
+
botId
|
|
622
|
+
})
|
|
623
|
+
)
|
|
624
|
+
)).flat().filter(isRawMemorySemanticResult).map((hit) => ({
|
|
625
|
+
...toMemoryResult(hit),
|
|
626
|
+
metadata: { ...hit.metadata, scoring: "bm25" }
|
|
627
|
+
}));
|
|
628
|
+
} catch (error) {
|
|
629
|
+
logger.warn?.("[memory-store] lexical memory search failed:", error);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
try {
|
|
633
|
+
const { lexicalSearchRawMessages: lexicalSearchRawMessages2 } = await Promise.resolve().then(() => (init_sqlite_raw_message_store(), sqlite_raw_message_store_exports));
|
|
634
|
+
const filters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
|
|
635
|
+
return (await Promise.all(
|
|
636
|
+
filters.map(
|
|
637
|
+
(botId) => lexicalSearchRawMessages2({
|
|
638
|
+
userId: input.userId,
|
|
639
|
+
keywords,
|
|
640
|
+
limit: Math.ceil(limit / filters.length),
|
|
641
|
+
botId
|
|
642
|
+
})
|
|
643
|
+
)
|
|
644
|
+
)).flat().filter(Boolean).map(
|
|
645
|
+
(r) => toMemoryResult(
|
|
646
|
+
r
|
|
647
|
+
)
|
|
648
|
+
);
|
|
649
|
+
} catch (error) {
|
|
650
|
+
logger.warn?.("[memory-store] SQLite lexical search failed:", error);
|
|
651
|
+
}
|
|
652
|
+
return [];
|
|
653
|
+
}
|
|
431
654
|
function createUnifiedSearch(deps = {}) {
|
|
432
655
|
const logger = deps.logger ?? console;
|
|
656
|
+
function resolveReasoningStrategy(input) {
|
|
657
|
+
const requested = normalizeUnifiedMemoryReasoningStrategy(input.reasoningStrategy);
|
|
658
|
+
if (requested !== "none") {
|
|
659
|
+
return requested;
|
|
660
|
+
}
|
|
661
|
+
return normalizeUnifiedMemoryReasoningStrategy(deps.reasoning?.defaultStrategy, "none");
|
|
662
|
+
}
|
|
433
663
|
async function searchUnifiedMemory(input) {
|
|
434
664
|
const query = input.query.trim();
|
|
435
665
|
const sources = normalizeUnifiedMemorySearchSources(input.sources);
|
|
436
666
|
const limit = clampUnifiedMemorySearchLimit(input.limit);
|
|
437
667
|
const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
|
|
438
668
|
const mergeStrategy = normalizeUnifiedMemoryMergeStrategy(input.mergeStrategy);
|
|
669
|
+
const reasoningStrategy = resolveReasoningStrategy(input);
|
|
439
670
|
const warnings = [];
|
|
440
671
|
if (!query) {
|
|
441
672
|
return {
|
|
@@ -447,10 +678,21 @@ function createUnifiedSearch(deps = {}) {
|
|
|
447
678
|
};
|
|
448
679
|
}
|
|
449
680
|
let memorySubs;
|
|
681
|
+
const reasoningInfo = { strategy: reasoningStrategy };
|
|
682
|
+
if (input.dateFrom || input.dateTo) {
|
|
683
|
+
reasoningInfo.dateRange = { from: input.dateFrom, to: input.dateTo };
|
|
684
|
+
}
|
|
450
685
|
if (sources.includes("memory")) {
|
|
451
686
|
if (isRawMessageStorageAvailable()) {
|
|
452
687
|
try {
|
|
453
|
-
memorySubs = await runMemorySource(
|
|
688
|
+
memorySubs = await runMemorySource(
|
|
689
|
+
input,
|
|
690
|
+
limit,
|
|
691
|
+
threshold,
|
|
692
|
+
warnings,
|
|
693
|
+
reasoningStrategy,
|
|
694
|
+
reasoningInfo
|
|
695
|
+
);
|
|
454
696
|
} catch (error) {
|
|
455
697
|
logger.warn?.("[memory-store] memory source failed:", error);
|
|
456
698
|
warnings.push({
|
|
@@ -529,120 +771,155 @@ function createUnifiedSearch(deps = {}) {
|
|
|
529
771
|
limit,
|
|
530
772
|
strategy: mergeStrategy
|
|
531
773
|
});
|
|
532
|
-
|
|
774
|
+
const output = {
|
|
533
775
|
query: input.query,
|
|
534
776
|
sources,
|
|
535
777
|
results: merged,
|
|
536
778
|
count: merged.length,
|
|
537
779
|
warnings
|
|
538
780
|
};
|
|
781
|
+
if (reasoningInfo && (reasoningInfo.strategy !== "none" || reasoningInfo.dateRange !== void 0)) {
|
|
782
|
+
output.reasoning = reasoningInfo;
|
|
783
|
+
}
|
|
784
|
+
return output;
|
|
539
785
|
}
|
|
540
|
-
async function runMemorySource(input, limit, threshold, warnings) {
|
|
786
|
+
async function runMemorySource(input, limit, threshold, warnings, reasoningStrategy = "none", reasoningInfo) {
|
|
787
|
+
if (reasoningStrategy === "iterative" && !deps.reasoning?.iterativePlanner) {
|
|
788
|
+
warnings.push({
|
|
789
|
+
source: "memory",
|
|
790
|
+
code: "memory_iterative_planner_not_configured",
|
|
791
|
+
message: "Iterative reasoning requested but no iterativePlanner is configured; falling back to default search."
|
|
792
|
+
});
|
|
793
|
+
if (reasoningInfo) reasoningInfo.degraded = true;
|
|
794
|
+
}
|
|
795
|
+
if (reasoningStrategy === "rewrite" && !deps.reasoning?.queryRewriter) {
|
|
796
|
+
warnings.push({
|
|
797
|
+
source: "memory",
|
|
798
|
+
code: "memory_query_rewrite_not_configured",
|
|
799
|
+
message: "Rewrite reasoning requested but no queryRewriter is configured; falling back to default search."
|
|
800
|
+
});
|
|
801
|
+
if (reasoningInfo) reasoningInfo.degraded = true;
|
|
802
|
+
}
|
|
803
|
+
if (reasoningStrategy === "iterative" && deps.reasoning?.iterativePlanner) {
|
|
804
|
+
const planner = deps.reasoning.iterativePlanner;
|
|
805
|
+
const result = await planner.plan({
|
|
806
|
+
query: input.query,
|
|
807
|
+
dateFrom: input.dateFrom,
|
|
808
|
+
dateTo: input.dateTo,
|
|
809
|
+
executor: {
|
|
810
|
+
search: async (request) => {
|
|
811
|
+
const keywords2 = request.keywords.length > 0 ? request.keywords : deriveLexicalKeywords(input.query);
|
|
812
|
+
const lexicalHits = await runLexicalSearchForKeywords(deps, input, keywords2, limit, logger);
|
|
813
|
+
let semanticHits = [];
|
|
814
|
+
if (typeof deps.embedQuery === "function") {
|
|
815
|
+
try {
|
|
816
|
+
const semanticQuery = keywords2.join(" ");
|
|
817
|
+
const queryEmbedding = await embedQueryVariant(deps.embedQuery, input, semanticQuery);
|
|
818
|
+
semanticHits = await runSemanticSearchForEmbedding(
|
|
819
|
+
deps,
|
|
820
|
+
input,
|
|
821
|
+
queryEmbedding,
|
|
822
|
+
limit,
|
|
823
|
+
threshold,
|
|
824
|
+
logger
|
|
825
|
+
);
|
|
826
|
+
} catch (error) {
|
|
827
|
+
logger.warn?.(
|
|
828
|
+
"[memory-store] Iterative semantic search failed; using lexical results only:",
|
|
829
|
+
error
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
const candidates = filterByDateRange(
|
|
834
|
+
mergeByMaxScore([
|
|
835
|
+
lexicalHits.map(toIterativeRecallCandidate),
|
|
836
|
+
semanticHits.map(toIterativeRecallCandidate)
|
|
837
|
+
]),
|
|
838
|
+
request.dateFrom,
|
|
839
|
+
request.dateTo
|
|
840
|
+
);
|
|
841
|
+
return { candidates };
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
});
|
|
845
|
+
if (reasoningInfo) {
|
|
846
|
+
reasoningInfo.iterations = result.stats.iterations;
|
|
847
|
+
reasoningInfo.evidenceCount = result.evidence.length;
|
|
848
|
+
if (planner.lastDegraded?.()) {
|
|
849
|
+
reasoningInfo.degraded = true;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
const semantic2 = result.evidence.map((candidate) => ({
|
|
853
|
+
type: "memory",
|
|
854
|
+
id: candidate.id,
|
|
855
|
+
content: candidate.content,
|
|
856
|
+
similarity: candidate.similarity,
|
|
857
|
+
metadata: { ...candidate.metadata, reasoning: "iterative" }
|
|
858
|
+
}));
|
|
859
|
+
return { semantic: semantic2, lexical: [] };
|
|
860
|
+
}
|
|
541
861
|
if (typeof deps.embedQuery !== "function") {
|
|
542
862
|
warnings.push({
|
|
543
863
|
source: "memory",
|
|
544
864
|
code: "memory_lexical_search_fallback",
|
|
545
865
|
message: "Semantic search not configured, using keyword search as fallback"
|
|
546
866
|
});
|
|
547
|
-
const
|
|
548
|
-
const
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
const results = await Promise.all(
|
|
554
|
-
filters2.map(
|
|
555
|
-
(botId) => lexicalSearchRawMessages2({
|
|
556
|
-
userId: input.userId,
|
|
557
|
-
keywords,
|
|
558
|
-
limit: Math.ceil(limit / filters2.length),
|
|
559
|
-
botId
|
|
560
|
-
})
|
|
561
|
-
)
|
|
562
|
-
);
|
|
563
|
-
lexical2 = results.flat().filter(Boolean).map(toMemoryResult);
|
|
564
|
-
} catch (error) {
|
|
565
|
-
logger.warn?.("[memory-store] Default lexical search failed:", error);
|
|
566
|
-
}
|
|
567
|
-
}
|
|
867
|
+
const keywords2 = deriveLexicalKeywords(input.query);
|
|
868
|
+
const lexical2 = filterByDateRange(
|
|
869
|
+
await runLexicalSearchForKeywords(deps, input, keywords2, limit, logger),
|
|
870
|
+
input.dateFrom,
|
|
871
|
+
input.dateTo
|
|
872
|
+
);
|
|
568
873
|
return { semantic: [], lexical: lexical2 };
|
|
569
874
|
}
|
|
570
|
-
const queryEmbedding = await deps.embedQuery({
|
|
571
|
-
userId: input.userId,
|
|
572
|
-
query: input.query,
|
|
573
|
-
authToken: input.authToken
|
|
574
|
-
});
|
|
575
|
-
const filters = input.botIds && input.botIds.length > 0 ? input.botIds.map((botId) => ({ botId })) : [{}];
|
|
576
875
|
let semantic = [];
|
|
577
|
-
|
|
876
|
+
const embedQuery = deps.embedQuery;
|
|
877
|
+
if (reasoningStrategy === "rewrite" && deps.reasoning?.queryRewriter) {
|
|
578
878
|
try {
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
userId: input.userId,
|
|
584
|
-
queryEmbedding,
|
|
585
|
-
limit,
|
|
586
|
-
threshold,
|
|
587
|
-
botId
|
|
588
|
-
});
|
|
589
|
-
})
|
|
590
|
-
)).flat().map(toMemoryResult);
|
|
591
|
-
logger.log?.("[memory-store] Raw message semantic search completed", {
|
|
592
|
-
backend: "chroma",
|
|
593
|
-
dimensions: queryEmbedding.length,
|
|
594
|
-
count: semantic.length
|
|
879
|
+
const variants = await deps.reasoning.queryRewriter.rewrite({
|
|
880
|
+
query: input.query,
|
|
881
|
+
userId: input.userId,
|
|
882
|
+
authToken: input.authToken
|
|
595
883
|
});
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
884
|
+
if (reasoningInfo) {
|
|
885
|
+
reasoningInfo.rewrittenQueries = variants;
|
|
886
|
+
if (deps.reasoning.queryRewriter.lastDegraded?.()) {
|
|
887
|
+
reasoningInfo.degraded = true;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
const embeddings = await Promise.all(
|
|
891
|
+
variants.map((variant) => embedQueryVariant(embedQuery, input, variant))
|
|
600
892
|
);
|
|
893
|
+
const lists = await Promise.all(
|
|
894
|
+
embeddings.map(
|
|
895
|
+
(embedding) => runSemanticSearchForEmbedding(deps, input, embedding, limit, threshold, logger)
|
|
896
|
+
)
|
|
897
|
+
);
|
|
898
|
+
semantic = mergeByMaxScore(lists);
|
|
899
|
+
} catch (error) {
|
|
900
|
+
logger.warn?.("[memory-store] Query rewriting failed; falling back to original query:", error);
|
|
901
|
+
warnings.push({
|
|
902
|
+
source: "memory",
|
|
903
|
+
code: "memory_query_rewrite_failed",
|
|
904
|
+
message: `Query rewriting failed: ${error.message ?? "Unknown error"}. Using original query.`
|
|
905
|
+
});
|
|
906
|
+
if (reasoningInfo) reasoningInfo.degraded = true;
|
|
601
907
|
}
|
|
602
|
-
} else if (typeof deps.searchRawMessagesAnn === "function") {
|
|
603
|
-
semantic = (await Promise.all(
|
|
604
|
-
filters.map(
|
|
605
|
-
(filter) => deps.searchRawMessagesAnn({
|
|
606
|
-
userId: input.userId,
|
|
607
|
-
queryEmbedding,
|
|
608
|
-
limit,
|
|
609
|
-
threshold,
|
|
610
|
-
botId: "botId" in filter ? filter.botId : void 0
|
|
611
|
-
})
|
|
612
|
-
)
|
|
613
|
-
)).flat().filter(isRawMemorySemanticResult).map(toMemoryResult);
|
|
614
908
|
}
|
|
615
909
|
if (semantic.length === 0) {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
const manager2 = await getRawMessageManager2();
|
|
619
|
-
if (typeof manager2.searchMessagesSemantically === "function") {
|
|
620
|
-
const results = await manager2.searchMessagesSemantically({
|
|
621
|
-
userId: input.userId,
|
|
622
|
-
queryEmbedding,
|
|
623
|
-
limit
|
|
624
|
-
});
|
|
625
|
-
semantic = results.map((r) => ({
|
|
626
|
-
type: "memory",
|
|
627
|
-
id: r.id,
|
|
628
|
-
content: r.content,
|
|
629
|
-
similarity: r.similarity,
|
|
630
|
-
metadata: r.metadata ?? {}
|
|
631
|
-
}));
|
|
632
|
-
}
|
|
633
|
-
} catch (error) {
|
|
634
|
-
logger.warn?.("[memory-store] SQLite semantic search failed:", error);
|
|
635
|
-
}
|
|
910
|
+
const queryEmbedding = await embedQueryVariant(embedQuery, input, input.query);
|
|
911
|
+
semantic = await runSemanticSearchForEmbedding(deps, input, queryEmbedding, limit, threshold, logger);
|
|
636
912
|
}
|
|
637
913
|
let lexical = [];
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
if (
|
|
914
|
+
const keywords = deriveLexicalKeywords(input.query);
|
|
915
|
+
if (keywords.length > 0) {
|
|
916
|
+
if (typeof deps.searchRawMessagesLexical === "function") {
|
|
641
917
|
try {
|
|
642
918
|
const lexFilters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
|
|
919
|
+
const searchRawMessagesLexical = deps.searchRawMessagesLexical;
|
|
643
920
|
lexical = (await Promise.all(
|
|
644
921
|
lexFilters.map(
|
|
645
|
-
(botId) =>
|
|
922
|
+
(botId) => searchRawMessagesLexical({
|
|
646
923
|
userId: input.userId,
|
|
647
924
|
keywords,
|
|
648
925
|
limit,
|
|
@@ -661,20 +938,24 @@ function createUnifiedSearch(deps = {}) {
|
|
|
661
938
|
message: error.message ?? "memory_lexical_search_failed"
|
|
662
939
|
});
|
|
663
940
|
}
|
|
941
|
+
} else if (input.mergeStrategy === "rrf") {
|
|
942
|
+
warnings.push({
|
|
943
|
+
source: "memory",
|
|
944
|
+
code: "memory_lexical_search_not_configured",
|
|
945
|
+
message: "RRF merge requested but lexical search is not configured; falling back to semantic-only RRF."
|
|
946
|
+
});
|
|
664
947
|
}
|
|
665
|
-
} else if (input.mergeStrategy === "rrf") {
|
|
666
|
-
warnings.push({
|
|
667
|
-
source: "memory",
|
|
668
|
-
code: "memory_lexical_search_not_configured",
|
|
669
|
-
message: "RRF merge requested but lexical search is not configured; falling back to semantic-only RRF."
|
|
670
|
-
});
|
|
671
948
|
}
|
|
672
|
-
return {
|
|
949
|
+
return {
|
|
950
|
+
semantic: filterByDateRange(semantic, input.dateFrom, input.dateTo),
|
|
951
|
+
lexical: filterByDateRange(lexical, input.dateFrom, input.dateTo)
|
|
952
|
+
};
|
|
673
953
|
}
|
|
674
954
|
async function searchRawMemorySemantically(input) {
|
|
675
955
|
const limit = clampUnifiedMemorySearchLimit(input.limit);
|
|
676
956
|
const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
|
|
677
|
-
const
|
|
957
|
+
const reasoningStrategy = resolveReasoningStrategy(input);
|
|
958
|
+
const sub = await runMemorySource(input, limit, threshold, [], reasoningStrategy);
|
|
678
959
|
return mergeAcrossSources({
|
|
679
960
|
memorySubs: sub,
|
|
680
961
|
insightHits: [],
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { M as MemoryStoreConfig } from './config-
|
|
2
|
-
export { E as EmbedQueryFn, a as
|
|
1
|
+
import { M as MemoryStoreConfig } from './config-2Q092MUz.js';
|
|
2
|
+
export { E as EmbedQueryFn, I as IterativeRecallCandidate, a as IterativeRecallExecutor, b as IterativeRecallPlanner, c as IterativeRecallPlannerOptions, d as IterativeRecallResult, e as IterativeRecallSearchRequest, f as IterativeRecallSearchResult, g as IterativeRecallStats, h as MemoryStoreDb, i as MemoryStoreEnv, Q as QueryRewriter, j as QueryRewriterInput, k as QueryRewriterOptions, U as UnifiedMemoryReasoningInfo, l as UnifiedMemoryReasoningStrategy, m as UnifiedMemorySearchInput, n as UnifiedMemorySearchOutput, o as UnifiedMemorySearchResult, p as UnifiedMemorySearchSource, q as UnifiedMemorySearchWarning, r as UnifiedSearchInsightsResult, s as UnifiedSearchKnowledgeResult, t as UnifiedSearchReasoningDeps, V as VectorBackend, u as clampUnifiedMemorySearchLimit, v as clampUnifiedMemorySearchThreshold, w as createIdentityIterativePlanner, x as createIdentityRewriter, y as createIterativeRecallPlanner, z as createUserVoiceRewriter, A as isRawMemorySemanticResult, B as mergeUnifiedMemorySearchResults, C as normalizeUnifiedMemoryReasoningStrategy, D as normalizeUnifiedMemorySearchSources, F as toKnowledgeResult, G as toMemoryResult } from './config-2Q092MUz.js';
|
|
3
3
|
import { UnifiedSearch } from './search/unified-search.js';
|
|
4
|
-
export {
|
|
4
|
+
export { createUnifiedSearch } from './search/unified-search.js';
|
|
5
5
|
import { RawMessageStore, getRawMessageManager } from './storage/raw-message-store.js';
|
|
6
6
|
export { RawMessageStorageBackend, RawMessageStorageManagerWithSearch, closeRawMessageStore, createRawMessageStore, getRawMessageStorageBackend, isRawMessageStorageAvailable } from './storage/raw-message-store.js';
|
|
7
7
|
export { PostgresFactoryFn, PostgresRawMessageManagerLike, clearPostgresFactory, hasPostgresFactory, registerPostgresFactory, resolvePostgresFactory } from './storage/postgres-raw-message-factory.js';
|