@melandlabs/memory-store 0.2.7 → 0.3.1
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 +371 -103
- package/dist/index.d.ts +3 -3
- package/dist/index.js +728 -104
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +374 -103
- package/dist/search/unified-search.d.ts +3 -101
- package/dist/search/unified-search.js +372 -104
- package/dist/server/cli-http.js +380 -103
- package/dist/server/cli-mcp.js +383 -103
- package/dist/server/cli-shared.d.ts +1 -1
- package/dist/server/cli-shared.js +13 -5
- package/dist/storage/postgres-raw-message-factory.d.ts +1 -1
- package/dist/storage/raw-message-store.d.ts +1 -1
- package/dist/storage/raw-message-store.js +4 -5
- package/dist/storage/sqlite-raw-message-store.d.ts +1 -1
- package/dist/storage/sqlite-raw-message-store.js +3 -4
- package/dist/storage/sqlite-vector-index.d.ts +1 -1
- package/dist/storage/sqlite-vector-index.js +3 -4
- package/package.json +11 -9
- package/dist/config-Ll6ktm4D.d.ts +0 -183
package/dist/http.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { buildMemoryRecordEmbeddingDocument } from '@melandlabs/ai/memory';
|
|
2
2
|
import { ChromaVectorStore } from '@melandlabs/rag';
|
|
3
3
|
import { mkdirSync } from 'fs';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { dirname } from 'path';
|
|
5
|
+
import { getOpenContextPath } from '@melandlabs/env-config';
|
|
6
6
|
import { SQLiteRawMessageManager } from '@melandlabs/sqlite';
|
|
7
7
|
import { serve } from '@hono/node-server';
|
|
8
8
|
import { Hono } from 'hono';
|
|
@@ -186,7 +186,7 @@ __export(sqlite_raw_message_store_exports, {
|
|
|
186
186
|
function resolveSQLiteRawMessageDbPath(dbPath) {
|
|
187
187
|
if (dbPath && dbPath.length > 0) return dbPath;
|
|
188
188
|
const fromEnv = process.env.MEMORY_STORE_DB_PATH?.trim();
|
|
189
|
-
return fromEnv && fromEnv.length > 0 ? fromEnv :
|
|
189
|
+
return fromEnv && fromEnv.length > 0 ? fromEnv : getOpenContextPath("memory", "store.db");
|
|
190
190
|
}
|
|
191
191
|
function isSQLiteRawMessageStorageAvailable(_env) {
|
|
192
192
|
return true;
|
|
@@ -220,12 +220,11 @@ async function lexicalSearchRawMessages(input) {
|
|
|
220
220
|
}
|
|
221
221
|
return mgr.lexicalSearchMessages(input);
|
|
222
222
|
}
|
|
223
|
-
var manager
|
|
223
|
+
var manager;
|
|
224
224
|
var init_sqlite_raw_message_store = __esm({
|
|
225
225
|
"src/storage/sqlite-raw-message-store.ts"() {
|
|
226
226
|
init_chroma_memory_index();
|
|
227
227
|
manager = null;
|
|
228
|
-
DEFAULT_SQLITE_PATH = join(homedir(), ".opencontext", "memory", "store.db");
|
|
229
228
|
}
|
|
230
229
|
});
|
|
231
230
|
|
|
@@ -239,7 +238,7 @@ __export(raw_message_store_exports, {
|
|
|
239
238
|
getRawMessageStorageBackend: () => getRawMessageStorageBackend,
|
|
240
239
|
isRawMessageStorageAvailable: () => isRawMessageStorageAvailable
|
|
241
240
|
});
|
|
242
|
-
function resolveBackend(
|
|
241
|
+
function resolveBackend(_env) {
|
|
243
242
|
const explicit = process.env.OPENCONTEXT_MEMORY_STORE_BACKEND?.trim().toLowerCase();
|
|
244
243
|
if (explicit === "postgres") return "postgres";
|
|
245
244
|
return "sqlite";
|
|
@@ -372,6 +371,12 @@ function mergeUnifiedMemorySearchResults(results, limit, options = {}) {
|
|
|
372
371
|
function normalizeUnifiedMemoryMergeStrategy(value) {
|
|
373
372
|
return value === "rrf" ? "rrf" : "similarity";
|
|
374
373
|
}
|
|
374
|
+
function normalizeUnifiedMemoryReasoningStrategy(value, defaultStrategy = "none") {
|
|
375
|
+
if (value === "rewrite" || value === "iterative") {
|
|
376
|
+
return value;
|
|
377
|
+
}
|
|
378
|
+
return defaultStrategy;
|
|
379
|
+
}
|
|
375
380
|
var DEFAULT_RRF_K = 60;
|
|
376
381
|
function mergeUnifiedMemorySearchResultsRrf(lists, limit, k = DEFAULT_RRF_K) {
|
|
377
382
|
if (!Array.isArray(lists) || lists.length === 0) {
|
|
@@ -394,7 +399,13 @@ function mergeUnifiedMemorySearchResultsRrf(lists, limit, k = DEFAULT_RRF_K) {
|
|
|
394
399
|
}
|
|
395
400
|
}
|
|
396
401
|
}
|
|
397
|
-
const ranked = order.map((key) =>
|
|
402
|
+
const ranked = order.map((key) => {
|
|
403
|
+
const entry = scores.get(key);
|
|
404
|
+
if (!entry) {
|
|
405
|
+
throw new Error(`Invariant violation: missing RRF score for key "${key}"`);
|
|
406
|
+
}
|
|
407
|
+
return entry;
|
|
408
|
+
}).sort((a, b) => {
|
|
398
409
|
if (b.rrf !== a.rrf) {
|
|
399
410
|
return b.rrf - a.rrf;
|
|
400
411
|
}
|
|
@@ -439,14 +450,222 @@ function isRawMemorySemanticResult(result) {
|
|
|
439
450
|
function deriveLexicalKeywords(query) {
|
|
440
451
|
return query.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((token) => token.length >= 2).slice(0, 16);
|
|
441
452
|
}
|
|
453
|
+
function parseDateBoundary(value, role) {
|
|
454
|
+
const trimmed = value.trim();
|
|
455
|
+
if (!trimmed) {
|
|
456
|
+
return void 0;
|
|
457
|
+
}
|
|
458
|
+
const dateOnlyMatch = trimmed.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
459
|
+
if (dateOnlyMatch) {
|
|
460
|
+
const year = Number.parseInt(dateOnlyMatch[1], 10);
|
|
461
|
+
const month = Number.parseInt(dateOnlyMatch[2], 10) - 1;
|
|
462
|
+
const day = Number.parseInt(dateOnlyMatch[3], 10);
|
|
463
|
+
if (role === "from") {
|
|
464
|
+
return Date.UTC(year, month, day);
|
|
465
|
+
}
|
|
466
|
+
return Date.UTC(year, month, day + 1) - 1;
|
|
467
|
+
}
|
|
468
|
+
const parsed = Date.parse(trimmed);
|
|
469
|
+
if (Number.isNaN(parsed)) {
|
|
470
|
+
return void 0;
|
|
471
|
+
}
|
|
472
|
+
return parsed;
|
|
473
|
+
}
|
|
474
|
+
function getCandidateTimestamp(metadata) {
|
|
475
|
+
for (const key of ["timestamp", "createdAt", "time"]) {
|
|
476
|
+
const value = metadata[key];
|
|
477
|
+
if (typeof value === "number") {
|
|
478
|
+
return value;
|
|
479
|
+
}
|
|
480
|
+
if (typeof value === "string") {
|
|
481
|
+
const parsed = Date.parse(value);
|
|
482
|
+
if (!Number.isNaN(parsed)) {
|
|
483
|
+
return parsed;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return void 0;
|
|
488
|
+
}
|
|
489
|
+
function filterByDateRange(items, dateFrom, dateTo) {
|
|
490
|
+
if (!dateFrom && !dateTo) {
|
|
491
|
+
return items;
|
|
492
|
+
}
|
|
493
|
+
const fromMs = dateFrom ? parseDateBoundary(dateFrom, "from") : void 0;
|
|
494
|
+
const toMs = dateTo ? parseDateBoundary(dateTo, "to") : void 0;
|
|
495
|
+
if (fromMs === void 0 && toMs === void 0) {
|
|
496
|
+
return items;
|
|
497
|
+
}
|
|
498
|
+
return items.filter((item) => {
|
|
499
|
+
const ts = getCandidateTimestamp(item.metadata);
|
|
500
|
+
if (ts === void 0) {
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
if (fromMs !== void 0 && ts < fromMs) {
|
|
504
|
+
return false;
|
|
505
|
+
}
|
|
506
|
+
if (toMs !== void 0 && ts > toMs) {
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
return true;
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
async function embedQueryVariant(embedQuery, input, query) {
|
|
513
|
+
return embedQuery({
|
|
514
|
+
userId: input.userId,
|
|
515
|
+
query,
|
|
516
|
+
authToken: input.authToken
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
async function runSemanticSearchForEmbedding(deps, input, queryEmbedding, limit, threshold, logger) {
|
|
520
|
+
const filters = input.botIds && input.botIds.length > 0 ? input.botIds.map((botId) => ({ botId })) : [{}];
|
|
521
|
+
let semantic = [];
|
|
522
|
+
if (isRawMessageChromaEnabled()) {
|
|
523
|
+
try {
|
|
524
|
+
semantic = (await Promise.all(
|
|
525
|
+
filters.map((filter) => {
|
|
526
|
+
const botId = "botId" in filter ? filter.botId : void 0;
|
|
527
|
+
return searchRawMessagesWithChroma({
|
|
528
|
+
userId: input.userId,
|
|
529
|
+
queryEmbedding,
|
|
530
|
+
limit,
|
|
531
|
+
threshold,
|
|
532
|
+
botId
|
|
533
|
+
});
|
|
534
|
+
})
|
|
535
|
+
)).flat().map(toMemoryResult);
|
|
536
|
+
logger.log?.("[memory-store] Raw message semantic search completed", {
|
|
537
|
+
backend: "chroma",
|
|
538
|
+
dimensions: queryEmbedding.length,
|
|
539
|
+
count: semantic.length
|
|
540
|
+
});
|
|
541
|
+
} catch (error) {
|
|
542
|
+
logger.warn?.(
|
|
543
|
+
"[memory-store] Chroma raw message search failed; falling back to database search:",
|
|
544
|
+
error
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
} else if (typeof deps.searchRawMessagesAnn === "function") {
|
|
548
|
+
const searchRawMessagesAnn = deps.searchRawMessagesAnn;
|
|
549
|
+
semantic = (await Promise.all(
|
|
550
|
+
filters.map(
|
|
551
|
+
(filter) => searchRawMessagesAnn({
|
|
552
|
+
userId: input.userId,
|
|
553
|
+
queryEmbedding,
|
|
554
|
+
limit,
|
|
555
|
+
threshold,
|
|
556
|
+
botId: "botId" in filter ? filter.botId : void 0
|
|
557
|
+
})
|
|
558
|
+
)
|
|
559
|
+
)).flat().filter(isRawMemorySemanticResult).map(toMemoryResult);
|
|
560
|
+
}
|
|
561
|
+
if (semantic.length === 0) {
|
|
562
|
+
try {
|
|
563
|
+
const { getRawMessageManager: getRawMessageManager2 } = await Promise.resolve().then(() => (init_raw_message_store(), raw_message_store_exports));
|
|
564
|
+
const manager2 = await getRawMessageManager2();
|
|
565
|
+
if (typeof manager2.searchMessagesSemantically === "function") {
|
|
566
|
+
const results = await manager2.searchMessagesSemantically({
|
|
567
|
+
userId: input.userId,
|
|
568
|
+
queryEmbedding,
|
|
569
|
+
limit,
|
|
570
|
+
threshold
|
|
571
|
+
});
|
|
572
|
+
semantic = results.map((r) => ({
|
|
573
|
+
type: "memory",
|
|
574
|
+
id: r.id,
|
|
575
|
+
content: r.content,
|
|
576
|
+
similarity: r.similarity,
|
|
577
|
+
metadata: r.metadata ?? {}
|
|
578
|
+
}));
|
|
579
|
+
}
|
|
580
|
+
} catch (error) {
|
|
581
|
+
logger.warn?.("[memory-store] SQLite semantic search failed:", error);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
return semantic;
|
|
585
|
+
}
|
|
586
|
+
function mergeByMaxScore(lists) {
|
|
587
|
+
const best = /* @__PURE__ */ new Map();
|
|
588
|
+
for (const list of lists) {
|
|
589
|
+
for (const hit of list) {
|
|
590
|
+
const existing = best.get(hit.id);
|
|
591
|
+
if (!existing || hit.similarity > existing.similarity) {
|
|
592
|
+
best.set(hit.id, hit);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
return Array.from(best.values()).sort((a, b) => b.similarity - a.similarity);
|
|
597
|
+
}
|
|
598
|
+
function toIterativeRecallCandidate(hit) {
|
|
599
|
+
return {
|
|
600
|
+
id: hit.id,
|
|
601
|
+
content: hit.content,
|
|
602
|
+
similarity: hit.similarity,
|
|
603
|
+
metadata: hit.metadata
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
async function runLexicalSearchForKeywords(deps, input, keywords, limit, logger) {
|
|
607
|
+
if (keywords.length === 0) {
|
|
608
|
+
return [];
|
|
609
|
+
}
|
|
610
|
+
if (typeof deps.searchRawMessagesLexical === "function") {
|
|
611
|
+
try {
|
|
612
|
+
const filters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
|
|
613
|
+
const searchRawMessagesLexical = deps.searchRawMessagesLexical;
|
|
614
|
+
return (await Promise.all(
|
|
615
|
+
filters.map(
|
|
616
|
+
(botId) => searchRawMessagesLexical({
|
|
617
|
+
userId: input.userId,
|
|
618
|
+
keywords,
|
|
619
|
+
limit: Math.ceil(limit / filters.length),
|
|
620
|
+
botId
|
|
621
|
+
})
|
|
622
|
+
)
|
|
623
|
+
)).flat().filter(isRawMemorySemanticResult).map((hit) => ({
|
|
624
|
+
...toMemoryResult(hit),
|
|
625
|
+
metadata: { ...hit.metadata, scoring: "bm25" }
|
|
626
|
+
}));
|
|
627
|
+
} catch (error) {
|
|
628
|
+
logger.warn?.("[memory-store] lexical memory search failed:", error);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
try {
|
|
632
|
+
const { lexicalSearchRawMessages: lexicalSearchRawMessages2 } = await Promise.resolve().then(() => (init_sqlite_raw_message_store(), sqlite_raw_message_store_exports));
|
|
633
|
+
const filters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
|
|
634
|
+
return (await Promise.all(
|
|
635
|
+
filters.map(
|
|
636
|
+
(botId) => lexicalSearchRawMessages2({
|
|
637
|
+
userId: input.userId,
|
|
638
|
+
keywords,
|
|
639
|
+
limit: Math.ceil(limit / filters.length),
|
|
640
|
+
botId
|
|
641
|
+
})
|
|
642
|
+
)
|
|
643
|
+
)).flat().filter(Boolean).map(
|
|
644
|
+
(r) => toMemoryResult(
|
|
645
|
+
r
|
|
646
|
+
)
|
|
647
|
+
);
|
|
648
|
+
} catch (error) {
|
|
649
|
+
logger.warn?.("[memory-store] SQLite lexical search failed:", error);
|
|
650
|
+
}
|
|
651
|
+
return [];
|
|
652
|
+
}
|
|
442
653
|
function createUnifiedSearch(deps = {}) {
|
|
443
654
|
const logger = deps.logger ?? console;
|
|
655
|
+
function resolveReasoningStrategy(input) {
|
|
656
|
+
const requested = normalizeUnifiedMemoryReasoningStrategy(input.reasoningStrategy);
|
|
657
|
+
if (requested !== "none") {
|
|
658
|
+
return requested;
|
|
659
|
+
}
|
|
660
|
+
return normalizeUnifiedMemoryReasoningStrategy(deps.reasoning?.defaultStrategy, "none");
|
|
661
|
+
}
|
|
444
662
|
async function searchUnifiedMemory(input) {
|
|
445
663
|
const query = input.query.trim();
|
|
446
664
|
const sources = normalizeUnifiedMemorySearchSources(input.sources);
|
|
447
665
|
const limit = clampUnifiedMemorySearchLimit(input.limit);
|
|
448
666
|
const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
|
|
449
667
|
const mergeStrategy = normalizeUnifiedMemoryMergeStrategy(input.mergeStrategy);
|
|
668
|
+
const reasoningStrategy = resolveReasoningStrategy(input);
|
|
450
669
|
const warnings = [];
|
|
451
670
|
if (!query) {
|
|
452
671
|
return {
|
|
@@ -458,10 +677,21 @@ function createUnifiedSearch(deps = {}) {
|
|
|
458
677
|
};
|
|
459
678
|
}
|
|
460
679
|
let memorySubs;
|
|
680
|
+
const reasoningInfo = { strategy: reasoningStrategy };
|
|
681
|
+
if (input.dateFrom || input.dateTo) {
|
|
682
|
+
reasoningInfo.dateRange = { from: input.dateFrom, to: input.dateTo };
|
|
683
|
+
}
|
|
461
684
|
if (sources.includes("memory")) {
|
|
462
685
|
if (isRawMessageStorageAvailable()) {
|
|
463
686
|
try {
|
|
464
|
-
memorySubs = await runMemorySource(
|
|
687
|
+
memorySubs = await runMemorySource(
|
|
688
|
+
input,
|
|
689
|
+
limit,
|
|
690
|
+
threshold,
|
|
691
|
+
warnings,
|
|
692
|
+
reasoningStrategy,
|
|
693
|
+
reasoningInfo
|
|
694
|
+
);
|
|
465
695
|
} catch (error) {
|
|
466
696
|
logger.warn?.("[memory-store] memory source failed:", error);
|
|
467
697
|
warnings.push({
|
|
@@ -540,121 +770,155 @@ function createUnifiedSearch(deps = {}) {
|
|
|
540
770
|
limit,
|
|
541
771
|
strategy: mergeStrategy
|
|
542
772
|
});
|
|
543
|
-
|
|
773
|
+
const output = {
|
|
544
774
|
query: input.query,
|
|
545
775
|
sources,
|
|
546
776
|
results: merged,
|
|
547
777
|
count: merged.length,
|
|
548
778
|
warnings
|
|
549
779
|
};
|
|
780
|
+
if (reasoningInfo && (reasoningInfo.strategy !== "none" || reasoningInfo.dateRange !== void 0)) {
|
|
781
|
+
output.reasoning = reasoningInfo;
|
|
782
|
+
}
|
|
783
|
+
return output;
|
|
550
784
|
}
|
|
551
|
-
async function runMemorySource(input, limit, threshold, warnings) {
|
|
785
|
+
async function runMemorySource(input, limit, threshold, warnings, reasoningStrategy = "none", reasoningInfo) {
|
|
786
|
+
if (reasoningStrategy === "iterative" && !deps.reasoning?.iterativePlanner) {
|
|
787
|
+
warnings.push({
|
|
788
|
+
source: "memory",
|
|
789
|
+
code: "memory_iterative_planner_not_configured",
|
|
790
|
+
message: "Iterative reasoning requested but no iterativePlanner is configured; falling back to default search."
|
|
791
|
+
});
|
|
792
|
+
if (reasoningInfo) reasoningInfo.degraded = true;
|
|
793
|
+
}
|
|
794
|
+
if (reasoningStrategy === "rewrite" && !deps.reasoning?.queryRewriter) {
|
|
795
|
+
warnings.push({
|
|
796
|
+
source: "memory",
|
|
797
|
+
code: "memory_query_rewrite_not_configured",
|
|
798
|
+
message: "Rewrite reasoning requested but no queryRewriter is configured; falling back to default search."
|
|
799
|
+
});
|
|
800
|
+
if (reasoningInfo) reasoningInfo.degraded = true;
|
|
801
|
+
}
|
|
802
|
+
if (reasoningStrategy === "iterative" && deps.reasoning?.iterativePlanner) {
|
|
803
|
+
const planner = deps.reasoning.iterativePlanner;
|
|
804
|
+
const result = await planner.plan({
|
|
805
|
+
query: input.query,
|
|
806
|
+
dateFrom: input.dateFrom,
|
|
807
|
+
dateTo: input.dateTo,
|
|
808
|
+
executor: {
|
|
809
|
+
search: async (request) => {
|
|
810
|
+
const keywords2 = request.keywords.length > 0 ? request.keywords : deriveLexicalKeywords(input.query);
|
|
811
|
+
const lexicalHits = await runLexicalSearchForKeywords(deps, input, keywords2, limit, logger);
|
|
812
|
+
let semanticHits = [];
|
|
813
|
+
if (typeof deps.embedQuery === "function") {
|
|
814
|
+
try {
|
|
815
|
+
const semanticQuery = keywords2.join(" ");
|
|
816
|
+
const queryEmbedding = await embedQueryVariant(deps.embedQuery, input, semanticQuery);
|
|
817
|
+
semanticHits = await runSemanticSearchForEmbedding(
|
|
818
|
+
deps,
|
|
819
|
+
input,
|
|
820
|
+
queryEmbedding,
|
|
821
|
+
limit,
|
|
822
|
+
threshold,
|
|
823
|
+
logger
|
|
824
|
+
);
|
|
825
|
+
} catch (error) {
|
|
826
|
+
logger.warn?.(
|
|
827
|
+
"[memory-store] Iterative semantic search failed; using lexical results only:",
|
|
828
|
+
error
|
|
829
|
+
);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
const candidates = filterByDateRange(
|
|
833
|
+
mergeByMaxScore([
|
|
834
|
+
lexicalHits.map(toIterativeRecallCandidate),
|
|
835
|
+
semanticHits.map(toIterativeRecallCandidate)
|
|
836
|
+
]),
|
|
837
|
+
request.dateFrom,
|
|
838
|
+
request.dateTo
|
|
839
|
+
);
|
|
840
|
+
return { candidates };
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
});
|
|
844
|
+
if (reasoningInfo) {
|
|
845
|
+
reasoningInfo.iterations = result.stats.iterations;
|
|
846
|
+
reasoningInfo.evidenceCount = result.evidence.length;
|
|
847
|
+
if (planner.lastDegraded?.()) {
|
|
848
|
+
reasoningInfo.degraded = true;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
const semantic2 = result.evidence.map((candidate) => ({
|
|
852
|
+
type: "memory",
|
|
853
|
+
id: candidate.id,
|
|
854
|
+
content: candidate.content,
|
|
855
|
+
similarity: candidate.similarity,
|
|
856
|
+
metadata: { ...candidate.metadata, reasoning: "iterative" }
|
|
857
|
+
}));
|
|
858
|
+
return { semantic: semantic2, lexical: [] };
|
|
859
|
+
}
|
|
552
860
|
if (typeof deps.embedQuery !== "function") {
|
|
553
861
|
warnings.push({
|
|
554
862
|
source: "memory",
|
|
555
863
|
code: "memory_lexical_search_fallback",
|
|
556
864
|
message: "Semantic search not configured, using keyword search as fallback"
|
|
557
865
|
});
|
|
558
|
-
const
|
|
559
|
-
const
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
const results = await Promise.all(
|
|
565
|
-
filters2.map(
|
|
566
|
-
(botId) => lexicalSearchRawMessages2({
|
|
567
|
-
userId: input.userId,
|
|
568
|
-
keywords,
|
|
569
|
-
limit: Math.ceil(limit / filters2.length),
|
|
570
|
-
botId
|
|
571
|
-
})
|
|
572
|
-
)
|
|
573
|
-
);
|
|
574
|
-
lexical2 = results.flat().filter(Boolean).map(toMemoryResult);
|
|
575
|
-
} catch (error) {
|
|
576
|
-
logger.warn?.("[memory-store] Default lexical search failed:", error);
|
|
577
|
-
}
|
|
578
|
-
}
|
|
866
|
+
const keywords2 = deriveLexicalKeywords(input.query);
|
|
867
|
+
const lexical2 = filterByDateRange(
|
|
868
|
+
await runLexicalSearchForKeywords(deps, input, keywords2, limit, logger),
|
|
869
|
+
input.dateFrom,
|
|
870
|
+
input.dateTo
|
|
871
|
+
);
|
|
579
872
|
return { semantic: [], lexical: lexical2 };
|
|
580
873
|
}
|
|
581
|
-
const queryEmbedding = await deps.embedQuery({
|
|
582
|
-
userId: input.userId,
|
|
583
|
-
query: input.query,
|
|
584
|
-
authToken: input.authToken
|
|
585
|
-
});
|
|
586
|
-
const filters = input.botIds && input.botIds.length > 0 ? input.botIds.map((botId) => ({ botId })) : [{}];
|
|
587
874
|
let semantic = [];
|
|
588
|
-
|
|
875
|
+
const embedQuery = deps.embedQuery;
|
|
876
|
+
if (reasoningStrategy === "rewrite" && deps.reasoning?.queryRewriter) {
|
|
589
877
|
try {
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
userId: input.userId,
|
|
595
|
-
queryEmbedding,
|
|
596
|
-
limit,
|
|
597
|
-
threshold,
|
|
598
|
-
botId
|
|
599
|
-
});
|
|
600
|
-
})
|
|
601
|
-
)).flat().map(toMemoryResult);
|
|
602
|
-
logger.log?.("[memory-store] Raw message semantic search completed", {
|
|
603
|
-
backend: "chroma",
|
|
604
|
-
dimensions: queryEmbedding.length,
|
|
605
|
-
count: semantic.length
|
|
878
|
+
const variants = await deps.reasoning.queryRewriter.rewrite({
|
|
879
|
+
query: input.query,
|
|
880
|
+
userId: input.userId,
|
|
881
|
+
authToken: input.authToken
|
|
606
882
|
});
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
883
|
+
if (reasoningInfo) {
|
|
884
|
+
reasoningInfo.rewrittenQueries = variants;
|
|
885
|
+
if (deps.reasoning.queryRewriter.lastDegraded?.()) {
|
|
886
|
+
reasoningInfo.degraded = true;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
const embeddings = await Promise.all(
|
|
890
|
+
variants.map((variant) => embedQueryVariant(embedQuery, input, variant))
|
|
891
|
+
);
|
|
892
|
+
const lists = await Promise.all(
|
|
893
|
+
embeddings.map(
|
|
894
|
+
(embedding) => runSemanticSearchForEmbedding(deps, input, embedding, limit, threshold, logger)
|
|
895
|
+
)
|
|
611
896
|
);
|
|
897
|
+
semantic = mergeByMaxScore(lists);
|
|
898
|
+
} catch (error) {
|
|
899
|
+
logger.warn?.("[memory-store] Query rewriting failed; falling back to original query:", error);
|
|
900
|
+
warnings.push({
|
|
901
|
+
source: "memory",
|
|
902
|
+
code: "memory_query_rewrite_failed",
|
|
903
|
+
message: `Query rewriting failed: ${error.message ?? "Unknown error"}. Using original query.`
|
|
904
|
+
});
|
|
905
|
+
if (reasoningInfo) reasoningInfo.degraded = true;
|
|
612
906
|
}
|
|
613
|
-
} else if (typeof deps.searchRawMessagesAnn === "function") {
|
|
614
|
-
semantic = (await Promise.all(
|
|
615
|
-
filters.map(
|
|
616
|
-
(filter) => deps.searchRawMessagesAnn({
|
|
617
|
-
userId: input.userId,
|
|
618
|
-
queryEmbedding,
|
|
619
|
-
limit,
|
|
620
|
-
threshold,
|
|
621
|
-
botId: "botId" in filter ? filter.botId : void 0
|
|
622
|
-
})
|
|
623
|
-
)
|
|
624
|
-
)).flat().filter(isRawMemorySemanticResult).map(toMemoryResult);
|
|
625
907
|
}
|
|
626
908
|
if (semantic.length === 0) {
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
const manager2 = await getRawMessageManager2();
|
|
630
|
-
if (typeof manager2.searchMessagesSemantically === "function") {
|
|
631
|
-
const results = await manager2.searchMessagesSemantically({
|
|
632
|
-
userId: input.userId,
|
|
633
|
-
queryEmbedding,
|
|
634
|
-
limit,
|
|
635
|
-
threshold
|
|
636
|
-
});
|
|
637
|
-
semantic = results.map((r) => ({
|
|
638
|
-
type: "memory",
|
|
639
|
-
id: r.id,
|
|
640
|
-
content: r.content,
|
|
641
|
-
similarity: r.similarity,
|
|
642
|
-
metadata: r.metadata ?? {}
|
|
643
|
-
}));
|
|
644
|
-
}
|
|
645
|
-
} catch (error) {
|
|
646
|
-
logger.warn?.("[memory-store] SQLite semantic search failed:", error);
|
|
647
|
-
}
|
|
909
|
+
const queryEmbedding = await embedQueryVariant(embedQuery, input, input.query);
|
|
910
|
+
semantic = await runSemanticSearchForEmbedding(deps, input, queryEmbedding, limit, threshold, logger);
|
|
648
911
|
}
|
|
649
912
|
let lexical = [];
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
if (
|
|
913
|
+
const keywords = deriveLexicalKeywords(input.query);
|
|
914
|
+
if (keywords.length > 0) {
|
|
915
|
+
if (typeof deps.searchRawMessagesLexical === "function") {
|
|
653
916
|
try {
|
|
654
917
|
const lexFilters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
|
|
918
|
+
const searchRawMessagesLexical = deps.searchRawMessagesLexical;
|
|
655
919
|
lexical = (await Promise.all(
|
|
656
920
|
lexFilters.map(
|
|
657
|
-
(botId) =>
|
|
921
|
+
(botId) => searchRawMessagesLexical({
|
|
658
922
|
userId: input.userId,
|
|
659
923
|
keywords,
|
|
660
924
|
limit,
|
|
@@ -673,20 +937,24 @@ function createUnifiedSearch(deps = {}) {
|
|
|
673
937
|
message: error.message ?? "memory_lexical_search_failed"
|
|
674
938
|
});
|
|
675
939
|
}
|
|
940
|
+
} else if (input.mergeStrategy === "rrf") {
|
|
941
|
+
warnings.push({
|
|
942
|
+
source: "memory",
|
|
943
|
+
code: "memory_lexical_search_not_configured",
|
|
944
|
+
message: "RRF merge requested but lexical search is not configured; falling back to semantic-only RRF."
|
|
945
|
+
});
|
|
676
946
|
}
|
|
677
|
-
} else if (input.mergeStrategy === "rrf") {
|
|
678
|
-
warnings.push({
|
|
679
|
-
source: "memory",
|
|
680
|
-
code: "memory_lexical_search_not_configured",
|
|
681
|
-
message: "RRF merge requested but lexical search is not configured; falling back to semantic-only RRF."
|
|
682
|
-
});
|
|
683
947
|
}
|
|
684
|
-
return {
|
|
948
|
+
return {
|
|
949
|
+
semantic: filterByDateRange(semantic, input.dateFrom, input.dateTo),
|
|
950
|
+
lexical: filterByDateRange(lexical, input.dateFrom, input.dateTo)
|
|
951
|
+
};
|
|
685
952
|
}
|
|
686
953
|
async function searchRawMemorySemantically(input) {
|
|
687
954
|
const limit = clampUnifiedMemorySearchLimit(input.limit);
|
|
688
955
|
const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
|
|
689
|
-
const
|
|
956
|
+
const reasoningStrategy = resolveReasoningStrategy(input);
|
|
957
|
+
const sub = await runMemorySource(input, limit, threshold, [], reasoningStrategy);
|
|
690
958
|
return mergeAcrossSources({
|
|
691
959
|
memorySubs: sub,
|
|
692
960
|
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';
|