@melandlabs/memory-store 1.2.4 → 1.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/README.md +47 -16
- package/dist/{config-DfvVZ--0.d.ts → config-CbE2t4SW.d.ts} +173 -18
- package/dist/http.d.ts +2 -1
- package/dist/http.js +636 -277
- package/dist/index.d.ts +10 -6
- package/dist/index.js +357 -105
- package/dist/mcp.d.ts +2 -1
- package/dist/mcp.js +659 -259
- package/dist/search/derive.d.ts +2 -1
- package/dist/search/distill.d.ts +2 -1
- package/dist/search/unified-search.d.ts +3 -2
- package/dist/search/unified-search.js +357 -105
- package/dist/server/cli-http.js +1008 -327
- package/dist/server/cli-mcp.js +1031 -309
- package/dist/server/cli-shared.d.ts +20 -7
- package/dist/server/cli-shared.js +370 -48
- package/dist/storage/postgres-raw-message-factory.d.ts +2 -1
- package/dist/storage/raw-message-store.d.ts +20 -2
- package/dist/storage/sqlite-raw-message-store.d.ts +8 -1
- package/dist/storage/sqlite-vector-index.d.ts +2 -1
- package/dist/storage/sqlite-vector-index.js +1 -1
- package/dist/{unified-search-A8Q3JHwU.d.ts → unified-search-2YQ6EGuQ.d.ts} +17 -6
- package/package.json +15 -11
package/dist/index.js
CHANGED
|
@@ -260,6 +260,69 @@ var init_raw_message_store = __esm({
|
|
|
260
260
|
init_chroma_memory_index();
|
|
261
261
|
init_raw_message_store();
|
|
262
262
|
|
|
263
|
+
// src/search/applicability.ts
|
|
264
|
+
var APPLICABILITY_SCOPES = /* @__PURE__ */ new Set([
|
|
265
|
+
"global",
|
|
266
|
+
"task",
|
|
267
|
+
"conversation",
|
|
268
|
+
"channel",
|
|
269
|
+
"project",
|
|
270
|
+
"custom"
|
|
271
|
+
]);
|
|
272
|
+
function isRecord(value) {
|
|
273
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
274
|
+
}
|
|
275
|
+
function invalidRuntimeContext(message) {
|
|
276
|
+
throw new TypeError(`memory-store search runtime context: ${message}`);
|
|
277
|
+
}
|
|
278
|
+
function validateApplicabilityContext(value, index) {
|
|
279
|
+
const path = `applicabilityContexts[${index}]`;
|
|
280
|
+
if (!isRecord(value)) {
|
|
281
|
+
invalidRuntimeContext(`${path} must be an object`);
|
|
282
|
+
}
|
|
283
|
+
const scope = value.scope;
|
|
284
|
+
if (typeof scope !== "string" || !APPLICABILITY_SCOPES.has(scope)) {
|
|
285
|
+
invalidRuntimeContext(`${path}.scope is not supported`);
|
|
286
|
+
}
|
|
287
|
+
if (value.key !== void 0 && typeof value.key !== "string") {
|
|
288
|
+
invalidRuntimeContext(`${path}.key must be a string when provided`);
|
|
289
|
+
}
|
|
290
|
+
if (scope !== "global" && (typeof value.key !== "string" || value.key.trim().length === 0)) {
|
|
291
|
+
invalidRuntimeContext(`${path}.key must be non-empty for scope "${scope}"`);
|
|
292
|
+
}
|
|
293
|
+
for (const field of ["validFrom", "validUntil"]) {
|
|
294
|
+
const boundary = value[field];
|
|
295
|
+
if (boundary !== void 0 && (typeof boundary !== "number" || !Number.isFinite(boundary))) {
|
|
296
|
+
invalidRuntimeContext(`${path}.${field} must be a finite epoch-millisecond number`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function resolveSearchRuntimeContext(input, runtimeContext, now = Date.now) {
|
|
301
|
+
if (runtimeContext === void 0) {
|
|
302
|
+
return void 0;
|
|
303
|
+
}
|
|
304
|
+
if (!isRecord(runtimeContext) || !Array.isArray(runtimeContext.applicabilityContexts)) {
|
|
305
|
+
invalidRuntimeContext("applicabilityContexts must be an array");
|
|
306
|
+
}
|
|
307
|
+
for (const [index, context] of runtimeContext.applicabilityContexts.entries()) {
|
|
308
|
+
validateApplicabilityContext(context, index);
|
|
309
|
+
}
|
|
310
|
+
let applicabilityAt;
|
|
311
|
+
if (input.asOf === void 0) {
|
|
312
|
+
applicabilityAt = now();
|
|
313
|
+
} else {
|
|
314
|
+
const parsed = typeof input.asOf === "string" ? Date.parse(input.asOf) : Number.NaN;
|
|
315
|
+
if (!Number.isFinite(parsed)) {
|
|
316
|
+
invalidRuntimeContext("asOf must be a parseable timestamp when a runtime context is supplied");
|
|
317
|
+
}
|
|
318
|
+
applicabilityAt = parsed;
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
applicabilityContexts: runtimeContext.applicabilityContexts,
|
|
322
|
+
applicabilityAt
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
263
326
|
// src/search/utilities.ts
|
|
264
327
|
function deriveLexicalKeywords(query) {
|
|
265
328
|
return query.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((token) => token.length >= 2).slice(0, 16);
|
|
@@ -311,7 +374,7 @@ function normalizeUnifiedMemoryMergeStrategy(value) {
|
|
|
311
374
|
return value === "rrf" ? "rrf" : "similarity";
|
|
312
375
|
}
|
|
313
376
|
function normalizeUnifiedMemoryReasoningStrategy(value, defaultStrategy = "none") {
|
|
314
|
-
if (value === "rewrite" || value === "iterative") {
|
|
377
|
+
if (value === "rewrite" || value === "iterative" || value === "union") {
|
|
315
378
|
return value;
|
|
316
379
|
}
|
|
317
380
|
return defaultStrategy;
|
|
@@ -418,6 +481,7 @@ function materializeSignals(lists, hit) {
|
|
|
418
481
|
function listNameToChannel(name) {
|
|
419
482
|
if (name === "memory-semantic") return "semantic";
|
|
420
483
|
if (name === "memory-bm25" || name === "memory-lexical") return "lexical";
|
|
484
|
+
if (name === "memory-hybrid") return "hybrid";
|
|
421
485
|
if (name === "memory-entity") return "entity";
|
|
422
486
|
return void 0;
|
|
423
487
|
}
|
|
@@ -627,7 +691,6 @@ async function applyReranker(reranker, query, results) {
|
|
|
627
691
|
|
|
628
692
|
// src/search/gather-evidence.ts
|
|
629
693
|
var DEFAULT_TIERS2 = ["summary", "raw", "insight", "knowledge"];
|
|
630
|
-
var MAX_EVIDENCE_CHARS = 600;
|
|
631
694
|
function toSummaryResult(row) {
|
|
632
695
|
const start = typeof row.startTimestamp === "number" ? row.startTimestamp : void 0;
|
|
633
696
|
const end = typeof row.endTimestamp === "number" ? row.endTimestamp : void 0;
|
|
@@ -645,7 +708,7 @@ function toSummaryResult(row) {
|
|
|
645
708
|
}
|
|
646
709
|
};
|
|
647
710
|
}
|
|
648
|
-
async function gatherSummaries(deps, input, limit, threshold, logger, peerPeers) {
|
|
711
|
+
async function gatherSummaries(deps, input, limit, threshold, logger, peerPeers, runtimeContext) {
|
|
649
712
|
if (typeof deps.searchSummaries !== "function") {
|
|
650
713
|
return { hits: [], warnings: [] };
|
|
651
714
|
}
|
|
@@ -658,7 +721,8 @@ async function gatherSummaries(deps, input, limit, threshold, logger, peerPeers)
|
|
|
658
721
|
limit,
|
|
659
722
|
threshold,
|
|
660
723
|
authToken: input.authToken,
|
|
661
|
-
...peerPeers.length > 0 ? { peers: peerPeers } : {}
|
|
724
|
+
...peerPeers.length > 0 ? { peers: peerPeers } : {},
|
|
725
|
+
...runtimeContext ?? {}
|
|
662
726
|
});
|
|
663
727
|
const hits = rows.map(toSummaryResult);
|
|
664
728
|
return { hits, warnings: [] };
|
|
@@ -676,12 +740,6 @@ async function gatherSummaries(deps, input, limit, threshold, logger, peerPeers)
|
|
|
676
740
|
};
|
|
677
741
|
}
|
|
678
742
|
}
|
|
679
|
-
function truncate(input, max = MAX_EVIDENCE_CHARS) {
|
|
680
|
-
if (input.length <= max) {
|
|
681
|
-
return input;
|
|
682
|
-
}
|
|
683
|
-
return `${input.slice(0, max - 1)}\u2026`;
|
|
684
|
-
}
|
|
685
743
|
function buildSynthesisPrompt(input) {
|
|
686
744
|
const grouped = /* @__PURE__ */ new Map();
|
|
687
745
|
for (const item of input.evidence) {
|
|
@@ -695,9 +753,10 @@ function buildSynthesisPrompt(input) {
|
|
|
695
753
|
if (items.length === 0) {
|
|
696
754
|
continue;
|
|
697
755
|
}
|
|
698
|
-
const lines = items.map(
|
|
699
|
-
|
|
700
|
-
|
|
756
|
+
const lines = items.map((item, index) => {
|
|
757
|
+
const factSource = typeof item.metadata?.source === "string" && item.metadata.source.length > 0 ? item.metadata.source : item.source ?? "unknown";
|
|
758
|
+
return ` [${index + 1}] (${item.id}, source=${factSource}, score=${item.score.toFixed(4)}) ${item.snippet}`;
|
|
759
|
+
});
|
|
701
760
|
sections.push(`## ${tier}
|
|
702
761
|
${lines.join("\n")}`);
|
|
703
762
|
}
|
|
@@ -779,12 +838,15 @@ function coerceAnswer(text, schema) {
|
|
|
779
838
|
return text;
|
|
780
839
|
}
|
|
781
840
|
async function synthesizeAnswer(input) {
|
|
782
|
-
const { query,
|
|
783
|
-
const evidence =
|
|
784
|
-
id:
|
|
785
|
-
source:
|
|
786
|
-
snippet:
|
|
787
|
-
score:
|
|
841
|
+
const { query, responseSchema, deps, logger } = input;
|
|
842
|
+
const evidence = input.evidence.map((item) => ({
|
|
843
|
+
id: item.id,
|
|
844
|
+
source: mapEvidenceSourceToTier(item.source),
|
|
845
|
+
snippet: item.snippet,
|
|
846
|
+
score: item.score,
|
|
847
|
+
// Forward per-fact provenance (e.g. `metadata.source` from
|
|
848
|
+
// `opencontext add --source`) so the synthesis prompt can cite it.
|
|
849
|
+
metadata: item.metadata
|
|
788
850
|
}));
|
|
789
851
|
const warnings = [];
|
|
790
852
|
const complete = deps.reasoning?.complete;
|
|
@@ -815,21 +877,10 @@ async function synthesizeAnswer(input) {
|
|
|
815
877
|
return { answer: "", warnings };
|
|
816
878
|
}
|
|
817
879
|
}
|
|
818
|
-
function
|
|
819
|
-
|
|
820
|
-
if (
|
|
821
|
-
|
|
822
|
-
}
|
|
823
|
-
if (hit.type === "memory") {
|
|
824
|
-
return "raw";
|
|
825
|
-
}
|
|
826
|
-
if (hit.type === "insight") {
|
|
827
|
-
return "insight";
|
|
828
|
-
}
|
|
829
|
-
if (hit.type === "knowledge") {
|
|
830
|
-
return "knowledge";
|
|
831
|
-
}
|
|
832
|
-
return hit.type;
|
|
880
|
+
function mapEvidenceSourceToTier(source) {
|
|
881
|
+
if (source === "memory") return "raw";
|
|
882
|
+
if (source === "insights") return "insight";
|
|
883
|
+
return source;
|
|
833
884
|
}
|
|
834
885
|
async function resolveSearchScopePeers(input) {
|
|
835
886
|
return resolveScopePeer({
|
|
@@ -853,7 +904,7 @@ function deriveEntityKeywords(query) {
|
|
|
853
904
|
}
|
|
854
905
|
return out.slice(0, 16);
|
|
855
906
|
}
|
|
856
|
-
async function runEntitySearchForQuery(deps, input, limit, logger, peerPeers = []) {
|
|
907
|
+
async function runEntitySearchForQuery(deps, input, limit, logger, peerPeers = [], runtimeContext) {
|
|
857
908
|
if (typeof deps.entitySearch !== "function") {
|
|
858
909
|
return [];
|
|
859
910
|
}
|
|
@@ -871,7 +922,8 @@ async function runEntitySearchForQuery(deps, input, limit, logger, peerPeers = [
|
|
|
871
922
|
keywords,
|
|
872
923
|
limit: Math.ceil(limit / filters.length),
|
|
873
924
|
botId,
|
|
874
|
-
...peerPeers.length > 0 ? { peers: peerPeers } : {}
|
|
925
|
+
...peerPeers.length > 0 ? { peers: peerPeers } : {},
|
|
926
|
+
...runtimeContext ?? {}
|
|
875
927
|
})
|
|
876
928
|
)
|
|
877
929
|
)).flat();
|
|
@@ -959,6 +1011,16 @@ function filterByDateRange(items, dateFrom, dateTo) {
|
|
|
959
1011
|
return true;
|
|
960
1012
|
});
|
|
961
1013
|
}
|
|
1014
|
+
function warnApplicabilityNotEnforced(warnings) {
|
|
1015
|
+
if (warnings.some((warning) => warning.code === "memory_applicability_not_enforced")) {
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
warnings.push({
|
|
1019
|
+
source: "memory",
|
|
1020
|
+
code: "memory_applicability_not_enforced",
|
|
1021
|
+
message: "Built-in raw-message retrieval was skipped because it cannot enforce the supplied applicability context. Configure an applicability-aware raw-message provider."
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
962
1024
|
async function embedQueryVariant(embedQuery, input, query) {
|
|
963
1025
|
return embedQuery({
|
|
964
1026
|
userId: input.userId,
|
|
@@ -966,10 +1028,29 @@ async function embedQueryVariant(embedQuery, input, query) {
|
|
|
966
1028
|
authToken: input.authToken
|
|
967
1029
|
});
|
|
968
1030
|
}
|
|
969
|
-
async function runSemanticSearchForEmbedding(deps, input, queryEmbedding, limit, threshold, logger, peerPeers
|
|
1031
|
+
async function runSemanticSearchForEmbedding(deps, input, queryEmbedding, limit, threshold, logger, peerPeers, warnings, runtimeContext) {
|
|
970
1032
|
const filters = input.botIds && input.botIds.length > 0 ? input.botIds.map((botId) => ({ botId })) : [{}];
|
|
971
1033
|
let semantic = [];
|
|
972
|
-
if (
|
|
1034
|
+
if (typeof deps.searchRawMessagesAnn === "function") {
|
|
1035
|
+
const searchRawMessagesAnn = deps.searchRawMessagesAnn;
|
|
1036
|
+
const factTypes = input.factTypes?.length ? input.factTypes : void 0;
|
|
1037
|
+
const includeDeprecated = input.includeDeprecated === true;
|
|
1038
|
+
semantic = (await Promise.all(
|
|
1039
|
+
filters.map(
|
|
1040
|
+
(filter) => searchRawMessagesAnn({
|
|
1041
|
+
userId: input.userId,
|
|
1042
|
+
queryEmbedding,
|
|
1043
|
+
limit,
|
|
1044
|
+
threshold,
|
|
1045
|
+
botId: "botId" in filter ? filter.botId : void 0,
|
|
1046
|
+
includeDeprecated,
|
|
1047
|
+
...peerPeers.length > 0 ? { peers: peerPeers } : {},
|
|
1048
|
+
...factTypes ? { factTypes } : {},
|
|
1049
|
+
...runtimeContext ?? {}
|
|
1050
|
+
})
|
|
1051
|
+
)
|
|
1052
|
+
)).flat().filter(isRawMemorySemanticResult).map(toMemoryResult);
|
|
1053
|
+
} else if (runtimeContext === void 0 && isRawMessageChromaEnabled()) {
|
|
973
1054
|
try {
|
|
974
1055
|
semantic = (await Promise.all(
|
|
975
1056
|
filters.map((filter) => {
|
|
@@ -994,24 +1075,12 @@ async function runSemanticSearchForEmbedding(deps, input, queryEmbedding, limit,
|
|
|
994
1075
|
error
|
|
995
1076
|
);
|
|
996
1077
|
}
|
|
997
|
-
} else if (typeof deps.searchRawMessagesAnn === "function") {
|
|
998
|
-
const searchRawMessagesAnn = deps.searchRawMessagesAnn;
|
|
999
|
-
const factTypes = input.factTypes?.length ? input.factTypes : void 0;
|
|
1000
|
-
semantic = (await Promise.all(
|
|
1001
|
-
filters.map(
|
|
1002
|
-
(filter) => searchRawMessagesAnn({
|
|
1003
|
-
userId: input.userId,
|
|
1004
|
-
queryEmbedding,
|
|
1005
|
-
limit,
|
|
1006
|
-
threshold,
|
|
1007
|
-
botId: "botId" in filter ? filter.botId : void 0,
|
|
1008
|
-
...peerPeers.length > 0 ? { peers: peerPeers } : {},
|
|
1009
|
-
...factTypes ? { factTypes } : {}
|
|
1010
|
-
})
|
|
1011
|
-
)
|
|
1012
|
-
)).flat().filter(isRawMemorySemanticResult).map(toMemoryResult);
|
|
1013
1078
|
}
|
|
1014
|
-
if (semantic.length === 0) {
|
|
1079
|
+
if (semantic.length === 0 && runtimeContext !== void 0) {
|
|
1080
|
+
warnApplicabilityNotEnforced(warnings);
|
|
1081
|
+
return [];
|
|
1082
|
+
}
|
|
1083
|
+
if (semantic.length === 0 && typeof deps.searchRawMessagesAnn !== "function") {
|
|
1015
1084
|
try {
|
|
1016
1085
|
const { getRawMessageManager: getRawMessageManager2 } = await Promise.resolve().then(() => (init_raw_message_store(), raw_message_store_exports));
|
|
1017
1086
|
const manager2 = await getRawMessageManager2();
|
|
@@ -1022,6 +1091,7 @@ async function runSemanticSearchForEmbedding(deps, input, queryEmbedding, limit,
|
|
|
1022
1091
|
queryEmbedding,
|
|
1023
1092
|
limit,
|
|
1024
1093
|
threshold,
|
|
1094
|
+
includeDeprecated: input.includeDeprecated === true,
|
|
1025
1095
|
...peerPeers.length > 0 ? { peers: peerPeers } : {},
|
|
1026
1096
|
...factTypes ? { factTypes } : {}
|
|
1027
1097
|
});
|
|
@@ -1051,6 +1121,18 @@ function mergeByMaxScore(lists) {
|
|
|
1051
1121
|
}
|
|
1052
1122
|
return Array.from(best.values()).sort((a, b) => b.similarity - a.similarity);
|
|
1053
1123
|
}
|
|
1124
|
+
function dedupeChannelByParent(hits) {
|
|
1125
|
+
const strongest = /* @__PURE__ */ new Map();
|
|
1126
|
+
for (const hit of hits) {
|
|
1127
|
+
const sourceMessageId = hit.metadata?.sourceMessageId;
|
|
1128
|
+
const parentId = typeof sourceMessageId === "string" && sourceMessageId.length > 0 ? sourceMessageId : hit.id;
|
|
1129
|
+
const normalized = hit.type === "memory" && hit.id !== parentId ? { ...hit, id: parentId } : hit;
|
|
1130
|
+
const key = `${normalized.type}:${parentId}`;
|
|
1131
|
+
const current = strongest.get(key);
|
|
1132
|
+
if (!current || normalized.similarity > current.similarity) strongest.set(key, normalized);
|
|
1133
|
+
}
|
|
1134
|
+
return [...strongest.values()].sort((a, b) => b.similarity - a.similarity);
|
|
1135
|
+
}
|
|
1054
1136
|
function toIterativeRecallCandidate(hit) {
|
|
1055
1137
|
return {
|
|
1056
1138
|
id: hit.id,
|
|
@@ -1059,7 +1141,7 @@ function toIterativeRecallCandidate(hit) {
|
|
|
1059
1141
|
metadata: hit.metadata
|
|
1060
1142
|
};
|
|
1061
1143
|
}
|
|
1062
|
-
async function runLexicalSearchForKeywords(deps, input, keywords, limit, logger, peerPeers
|
|
1144
|
+
async function runLexicalSearchForKeywords(deps, input, keywords, limit, logger, peerPeers, warnings, runtimeContext) {
|
|
1063
1145
|
if (keywords.length === 0) {
|
|
1064
1146
|
return [];
|
|
1065
1147
|
}
|
|
@@ -1068,6 +1150,7 @@ async function runLexicalSearchForKeywords(deps, input, keywords, limit, logger,
|
|
|
1068
1150
|
const filters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
|
|
1069
1151
|
const searchRawMessagesLexical = deps.searchRawMessagesLexical;
|
|
1070
1152
|
const factTypes = input.factTypes?.length ? input.factTypes : void 0;
|
|
1153
|
+
const includeDeprecated = input.includeDeprecated === true;
|
|
1071
1154
|
return (await Promise.all(
|
|
1072
1155
|
filters.map(
|
|
1073
1156
|
(botId) => searchRawMessagesLexical({
|
|
@@ -1075,8 +1158,10 @@ async function runLexicalSearchForKeywords(deps, input, keywords, limit, logger,
|
|
|
1075
1158
|
keywords,
|
|
1076
1159
|
limit: Math.ceil(limit / filters.length),
|
|
1077
1160
|
botId,
|
|
1161
|
+
includeDeprecated,
|
|
1078
1162
|
...peerPeers.length > 0 ? { peers: peerPeers } : {},
|
|
1079
|
-
...factTypes ? { factTypes } : {}
|
|
1163
|
+
...factTypes ? { factTypes } : {},
|
|
1164
|
+
...runtimeContext ?? {}
|
|
1080
1165
|
})
|
|
1081
1166
|
)
|
|
1082
1167
|
)).flat().filter(isRawMemorySemanticResult).map((hit) => ({
|
|
@@ -1087,10 +1172,15 @@ async function runLexicalSearchForKeywords(deps, input, keywords, limit, logger,
|
|
|
1087
1172
|
logger.warn?.("[memory-store] lexical memory search failed:", error);
|
|
1088
1173
|
}
|
|
1089
1174
|
}
|
|
1175
|
+
if (runtimeContext !== void 0) {
|
|
1176
|
+
warnApplicabilityNotEnforced(warnings);
|
|
1177
|
+
return [];
|
|
1178
|
+
}
|
|
1090
1179
|
try {
|
|
1091
1180
|
const { lexicalSearchRawMessages: lexicalSearchRawMessages2 } = await Promise.resolve().then(() => (init_sqlite_raw_message_store(), sqlite_raw_message_store_exports));
|
|
1092
1181
|
const filters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
|
|
1093
1182
|
const factTypes = input.factTypes?.length ? input.factTypes : void 0;
|
|
1183
|
+
const includeDeprecated = input.includeDeprecated === true;
|
|
1094
1184
|
return (await Promise.all(
|
|
1095
1185
|
filters.map(
|
|
1096
1186
|
(botId) => lexicalSearchRawMessages2({
|
|
@@ -1098,6 +1188,7 @@ async function runLexicalSearchForKeywords(deps, input, keywords, limit, logger,
|
|
|
1098
1188
|
keywords,
|
|
1099
1189
|
limit: Math.ceil(limit / filters.length),
|
|
1100
1190
|
botId,
|
|
1191
|
+
includeDeprecated,
|
|
1101
1192
|
...factTypes ? { factTypes } : {}
|
|
1102
1193
|
})
|
|
1103
1194
|
)
|
|
@@ -1128,7 +1219,9 @@ function searchInputToUnified(input) {
|
|
|
1128
1219
|
mergeStrategy: input.mergeStrategy,
|
|
1129
1220
|
peerFilter: input.peerFilter,
|
|
1130
1221
|
reasoningStrategy: input.reasoningStrategy,
|
|
1131
|
-
factTypes: input.factTypes
|
|
1222
|
+
factTypes: input.factTypes,
|
|
1223
|
+
includeRetrievalDiagnostics: input.includeRetrievalDiagnostics,
|
|
1224
|
+
includeDeprecated: input.includeDeprecated
|
|
1132
1225
|
};
|
|
1133
1226
|
}
|
|
1134
1227
|
function mapHitToEvidenceSource(hit) {
|
|
@@ -1156,8 +1249,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1156
1249
|
}
|
|
1157
1250
|
return normalizeUnifiedMemoryReasoningStrategy(deps.reasoning?.defaultStrategy, "none");
|
|
1158
1251
|
}
|
|
1159
|
-
async function runMemorySource(input, limit, threshold, warnings, reasoningStrategy = "none", reasoningInfo, peerPeers = []) {
|
|
1160
|
-
if (reasoningStrategy === "iterative" && !deps.reasoning?.iterativePlanner) {
|
|
1252
|
+
async function runMemorySource(input, limit, threshold, warnings, reasoningStrategy = "none", reasoningInfo, peerPeers = [], runtimeContext) {
|
|
1253
|
+
if ((reasoningStrategy === "iterative" || reasoningStrategy === "union") && !deps.reasoning?.iterativePlanner) {
|
|
1161
1254
|
warnings.push({
|
|
1162
1255
|
source: "memory",
|
|
1163
1256
|
code: "memory_iterative_planner_not_configured",
|
|
@@ -1173,7 +1266,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1173
1266
|
});
|
|
1174
1267
|
if (reasoningInfo) reasoningInfo.degraded = true;
|
|
1175
1268
|
}
|
|
1176
|
-
|
|
1269
|
+
let unionEvidence = null;
|
|
1270
|
+
if ((reasoningStrategy === "iterative" || reasoningStrategy === "union") && deps.reasoning?.iterativePlanner) {
|
|
1177
1271
|
const planner = deps.reasoning.iterativePlanner;
|
|
1178
1272
|
const result = await planner.plan({
|
|
1179
1273
|
query: input.query,
|
|
@@ -1188,7 +1282,9 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1188
1282
|
keywords2,
|
|
1189
1283
|
limit,
|
|
1190
1284
|
logger,
|
|
1191
|
-
peerPeers
|
|
1285
|
+
peerPeers,
|
|
1286
|
+
warnings,
|
|
1287
|
+
runtimeContext
|
|
1192
1288
|
);
|
|
1193
1289
|
let semanticHits = [];
|
|
1194
1290
|
if (typeof deps.embedQuery === "function") {
|
|
@@ -1202,7 +1298,9 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1202
1298
|
limit,
|
|
1203
1299
|
threshold,
|
|
1204
1300
|
logger,
|
|
1205
|
-
peerPeers
|
|
1301
|
+
peerPeers,
|
|
1302
|
+
warnings,
|
|
1303
|
+
runtimeContext
|
|
1206
1304
|
);
|
|
1207
1305
|
} catch (error) {
|
|
1208
1306
|
logger.warn?.(
|
|
@@ -1230,16 +1328,24 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1230
1328
|
reasoningInfo.degraded = true;
|
|
1231
1329
|
}
|
|
1232
1330
|
}
|
|
1233
|
-
const
|
|
1331
|
+
const evidence = result.evidence.map((candidate) => ({
|
|
1234
1332
|
type: "memory",
|
|
1235
1333
|
id: candidate.id,
|
|
1236
1334
|
content: candidate.content,
|
|
1237
1335
|
similarity: candidate.similarity,
|
|
1238
|
-
metadata: { ...candidate.metadata, reasoning:
|
|
1336
|
+
metadata: { ...candidate.metadata, reasoning: reasoningStrategy }
|
|
1239
1337
|
}));
|
|
1240
|
-
|
|
1338
|
+
if (reasoningStrategy === "iterative") {
|
|
1339
|
+
return { semantic: evidence, lexical: [] };
|
|
1340
|
+
}
|
|
1341
|
+
unionEvidence = evidence;
|
|
1241
1342
|
}
|
|
1242
1343
|
if (typeof deps.embedQuery !== "function") {
|
|
1344
|
+
warnings.push({
|
|
1345
|
+
source: "memory",
|
|
1346
|
+
code: "semantic_unavailable",
|
|
1347
|
+
message: "Semantic search is unavailable because no embedding provider is configured."
|
|
1348
|
+
});
|
|
1243
1349
|
warnings.push({
|
|
1244
1350
|
source: "memory",
|
|
1245
1351
|
code: "memory_lexical_search_fallback",
|
|
@@ -1247,7 +1353,16 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1247
1353
|
});
|
|
1248
1354
|
const keywords2 = deriveLexicalKeywords(input.query);
|
|
1249
1355
|
const lexical2 = filterByDateRange(
|
|
1250
|
-
await runLexicalSearchForKeywords(
|
|
1356
|
+
await runLexicalSearchForKeywords(
|
|
1357
|
+
deps,
|
|
1358
|
+
input,
|
|
1359
|
+
keywords2,
|
|
1360
|
+
limit,
|
|
1361
|
+
logger,
|
|
1362
|
+
peerPeers,
|
|
1363
|
+
warnings,
|
|
1364
|
+
runtimeContext
|
|
1365
|
+
),
|
|
1251
1366
|
input.dateFrom,
|
|
1252
1367
|
input.dateTo
|
|
1253
1368
|
);
|
|
@@ -1255,6 +1370,26 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1255
1370
|
}
|
|
1256
1371
|
let semantic = [];
|
|
1257
1372
|
const embedQuery = deps.embedQuery;
|
|
1373
|
+
const activeMergeStrategy = normalizeUnifiedMemoryMergeStrategy(
|
|
1374
|
+
input.mergeStrategy ?? deps.reasoning?.defaultMergeStrategy ?? "rrf"
|
|
1375
|
+
);
|
|
1376
|
+
if (reasoningStrategy === "none" && activeMergeStrategy === "rrf" && input.threshold === void 0 && typeof deps.searchRawMessagesHybrid === "function") {
|
|
1377
|
+
const queryEmbedding = await embedQueryVariant(embedQuery, input, input.query);
|
|
1378
|
+
const filters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
|
|
1379
|
+
const hybrid = (await Promise.all(
|
|
1380
|
+
filters.map(
|
|
1381
|
+
(botId) => deps.searchRawMessagesHybrid?.({
|
|
1382
|
+
userId: input.userId,
|
|
1383
|
+
query: input.query,
|
|
1384
|
+
queryEmbedding,
|
|
1385
|
+
limit: Math.ceil(limit / filters.length),
|
|
1386
|
+
botId,
|
|
1387
|
+
...runtimeContext ?? {}
|
|
1388
|
+
})
|
|
1389
|
+
)
|
|
1390
|
+
)).flatMap((items) => items ?? []).filter(isRawMemorySemanticResult).map(toMemoryResult);
|
|
1391
|
+
return { semantic: [], lexical: [], hybrid: filterByDateRange(hybrid, input.dateFrom, input.dateTo) };
|
|
1392
|
+
}
|
|
1258
1393
|
if (reasoningStrategy === "rewrite" && deps.reasoning?.queryRewriter) {
|
|
1259
1394
|
try {
|
|
1260
1395
|
const variants = await deps.reasoning.queryRewriter.rewrite({
|
|
@@ -1273,7 +1408,17 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1273
1408
|
);
|
|
1274
1409
|
const lists = await Promise.all(
|
|
1275
1410
|
embeddings.map(
|
|
1276
|
-
(embedding) => runSemanticSearchForEmbedding(
|
|
1411
|
+
(embedding) => runSemanticSearchForEmbedding(
|
|
1412
|
+
deps,
|
|
1413
|
+
input,
|
|
1414
|
+
embedding,
|
|
1415
|
+
limit,
|
|
1416
|
+
threshold,
|
|
1417
|
+
logger,
|
|
1418
|
+
peerPeers,
|
|
1419
|
+
warnings,
|
|
1420
|
+
runtimeContext
|
|
1421
|
+
)
|
|
1277
1422
|
)
|
|
1278
1423
|
);
|
|
1279
1424
|
semantic = mergeByMaxScore(lists);
|
|
@@ -1296,7 +1441,9 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1296
1441
|
limit,
|
|
1297
1442
|
threshold,
|
|
1298
1443
|
logger,
|
|
1299
|
-
peerPeers
|
|
1444
|
+
peerPeers,
|
|
1445
|
+
warnings,
|
|
1446
|
+
runtimeContext
|
|
1300
1447
|
);
|
|
1301
1448
|
}
|
|
1302
1449
|
let lexical = [];
|
|
@@ -1315,7 +1462,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1315
1462
|
limit,
|
|
1316
1463
|
botId,
|
|
1317
1464
|
...peerPeers.length > 0 ? { peers: peerPeers } : {},
|
|
1318
|
-
...factTypes ? { factTypes } : {}
|
|
1465
|
+
...factTypes ? { factTypes } : {},
|
|
1466
|
+
...runtimeContext ?? {}
|
|
1319
1467
|
})
|
|
1320
1468
|
)
|
|
1321
1469
|
)).flat().filter(isRawMemorySemanticResult).map((hit) => ({
|
|
@@ -1341,7 +1489,7 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1341
1489
|
let entity;
|
|
1342
1490
|
if (typeof deps.entitySearch === "function") {
|
|
1343
1491
|
entity = filterByDateRange(
|
|
1344
|
-
await runEntitySearchForQuery(deps, input, limit, logger, peerPeers),
|
|
1492
|
+
await runEntitySearchForQuery(deps, input, limit, logger, peerPeers, runtimeContext),
|
|
1345
1493
|
input.dateFrom,
|
|
1346
1494
|
input.dateTo
|
|
1347
1495
|
);
|
|
@@ -1363,6 +1511,37 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1363
1511
|
message: "RRF merge requested but entity search is not configured; falling back to semantic+lexical RRF."
|
|
1364
1512
|
});
|
|
1365
1513
|
}
|
|
1514
|
+
if (unionEvidence) {
|
|
1515
|
+
const plannerHits = unionEvidence;
|
|
1516
|
+
const base = mergeByMaxScore([
|
|
1517
|
+
filterByDateRange(semantic, input.dateFrom, input.dateTo),
|
|
1518
|
+
filterByDateRange(lexical, input.dateFrom, input.dateTo)
|
|
1519
|
+
]);
|
|
1520
|
+
const maxBase = base.length > 0 ? Math.max(...base.map((h) => h.similarity)) : 0;
|
|
1521
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1522
|
+
const merged = [];
|
|
1523
|
+
plannerHits.forEach((hit, i) => {
|
|
1524
|
+
if (seen.has(hit.id)) {
|
|
1525
|
+
return;
|
|
1526
|
+
}
|
|
1527
|
+
seen.add(hit.id);
|
|
1528
|
+
merged.push({
|
|
1529
|
+
...hit,
|
|
1530
|
+
similarity: maxBase + (plannerHits.length - i) * 1e-6
|
|
1531
|
+
});
|
|
1532
|
+
});
|
|
1533
|
+
for (const hit of base) {
|
|
1534
|
+
if (merged.length >= limit) {
|
|
1535
|
+
break;
|
|
1536
|
+
}
|
|
1537
|
+
if (seen.has(hit.id)) {
|
|
1538
|
+
continue;
|
|
1539
|
+
}
|
|
1540
|
+
seen.add(hit.id);
|
|
1541
|
+
merged.push(hit);
|
|
1542
|
+
}
|
|
1543
|
+
return { semantic: merged.slice(0, limit), lexical: [] };
|
|
1544
|
+
}
|
|
1366
1545
|
const out = {
|
|
1367
1546
|
semantic: filterByDateRange(semantic, input.dateFrom, input.dateTo),
|
|
1368
1547
|
lexical: filterByDateRange(lexical, input.dateFrom, input.dateTo)
|
|
@@ -1372,14 +1551,16 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1372
1551
|
}
|
|
1373
1552
|
return out;
|
|
1374
1553
|
}
|
|
1375
|
-
async function
|
|
1554
|
+
async function searchUnifiedMemoryWithRuntime(input, runtimeContext) {
|
|
1376
1555
|
const query = input.query.trim();
|
|
1377
1556
|
const sources = normalizeUnifiedMemorySearchSources(input.sources);
|
|
1378
1557
|
const limit = clampUnifiedMemorySearchLimit(input.limit);
|
|
1379
|
-
const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
|
|
1380
1558
|
const mergeStrategy = normalizeUnifiedMemoryMergeStrategy(
|
|
1381
1559
|
input.mergeStrategy ?? deps.reasoning?.defaultMergeStrategy ?? "rrf"
|
|
1382
1560
|
);
|
|
1561
|
+
const candidateLimit = Math.min(50, Math.max(limit, limit * 4));
|
|
1562
|
+
const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
|
|
1563
|
+
const memoryThreshold = input.threshold === void 0 && mergeStrategy === "rrf" ? Number.NEGATIVE_INFINITY : threshold;
|
|
1383
1564
|
const reasoningStrategy = resolveReasoningStrategy(input);
|
|
1384
1565
|
const warnings = [];
|
|
1385
1566
|
const peerScope = input.peerFilter && input.peerFilter.length > 0 ? await resolveScopePeer({
|
|
@@ -1399,23 +1580,46 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1399
1580
|
};
|
|
1400
1581
|
}
|
|
1401
1582
|
let memorySubs;
|
|
1583
|
+
let retrievalStatus;
|
|
1402
1584
|
const reasoningInfo = { strategy: reasoningStrategy };
|
|
1403
1585
|
if (input.dateFrom || input.dateTo) {
|
|
1404
1586
|
reasoningInfo.dateRange = { from: input.dateFrom, to: input.dateTo };
|
|
1405
1587
|
}
|
|
1406
1588
|
if (sources.includes("memory")) {
|
|
1407
|
-
const hasRawProviders = typeof deps.searchRawMessagesAnn === "function" || typeof deps.searchRawMessagesLexical === "function";
|
|
1589
|
+
const hasRawProviders = typeof deps.searchRawMessagesAnn === "function" || typeof deps.searchRawMessagesHybrid === "function" || typeof deps.searchRawMessagesLexical === "function";
|
|
1408
1590
|
if (isRawMessageStorageAvailable() || hasRawProviders) {
|
|
1409
1591
|
try {
|
|
1410
1592
|
memorySubs = await runMemorySource(
|
|
1411
1593
|
input,
|
|
1412
|
-
|
|
1413
|
-
|
|
1594
|
+
candidateLimit,
|
|
1595
|
+
memoryThreshold,
|
|
1414
1596
|
warnings,
|
|
1415
1597
|
reasoningStrategy,
|
|
1416
1598
|
reasoningInfo,
|
|
1417
|
-
peerPeers
|
|
1599
|
+
peerPeers,
|
|
1600
|
+
runtimeContext
|
|
1418
1601
|
);
|
|
1602
|
+
memorySubs = {
|
|
1603
|
+
semantic: dedupeChannelByParent(memorySubs.semantic),
|
|
1604
|
+
lexical: dedupeChannelByParent(memorySubs.lexical),
|
|
1605
|
+
...memorySubs.hybrid ? { hybrid: dedupeChannelByParent(memorySubs.hybrid) } : {},
|
|
1606
|
+
...memorySubs.entity ? { entity: dedupeChannelByParent(memorySubs.entity) } : {}
|
|
1607
|
+
};
|
|
1608
|
+
if (deps.getRawMessageRetrievalStatus) {
|
|
1609
|
+
retrievalStatus = await deps.getRawMessageRetrievalStatus();
|
|
1610
|
+
}
|
|
1611
|
+
if (memorySubs.semantic.length === 0 && (memorySubs.hybrid?.length ?? 0) === 0 && typeof deps.embedQuery === "function") {
|
|
1612
|
+
let code = "semantic_candidates_empty";
|
|
1613
|
+
let message = "Semantic search was available but returned no candidates.";
|
|
1614
|
+
if (retrievalStatus?.embeddedChildCount === 0) {
|
|
1615
|
+
code = "semantic_unavailable";
|
|
1616
|
+
message = "No embedded RawMessage children are available for semantic search.";
|
|
1617
|
+
} else if (retrievalStatus?.embeddingDimensions && retrievalStatus.indexedDimensions.length > 0 && !retrievalStatus.indexedDimensions.includes(retrievalStatus.embeddingDimensions)) {
|
|
1618
|
+
code = "semantic_dimension_mismatch";
|
|
1619
|
+
message = `Query embedding dimension ${retrievalStatus.embeddingDimensions} does not match indexed child dimensions ${retrievalStatus.indexedDimensions.join(", ")}.`;
|
|
1620
|
+
}
|
|
1621
|
+
warnings.push({ source: "memory", code, message });
|
|
1622
|
+
}
|
|
1419
1623
|
} catch (error) {
|
|
1420
1624
|
logger.warn?.("[memory-store] memory source failed:", error);
|
|
1421
1625
|
warnings.push({
|
|
@@ -1438,12 +1642,13 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1438
1642
|
const hits = await deps.searchInsights({
|
|
1439
1643
|
userId: input.userId,
|
|
1440
1644
|
query: input.query,
|
|
1441
|
-
limit,
|
|
1645
|
+
limit: candidateLimit,
|
|
1442
1646
|
threshold,
|
|
1443
1647
|
botIds: input.botIds,
|
|
1444
1648
|
includeArchived: input.includeArchivedInsights,
|
|
1445
1649
|
authToken: input.authToken,
|
|
1446
|
-
...peerPeers.length > 0 ? { peers: peerPeers } : {}
|
|
1650
|
+
...peerPeers.length > 0 ? { peers: peerPeers } : {},
|
|
1651
|
+
...runtimeContext ?? {}
|
|
1447
1652
|
});
|
|
1448
1653
|
for (const hit of hits) {
|
|
1449
1654
|
insightHits.push({
|
|
@@ -1470,12 +1675,13 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1470
1675
|
userId: input.userId,
|
|
1471
1676
|
query: input.query,
|
|
1472
1677
|
options: {
|
|
1473
|
-
limit,
|
|
1678
|
+
limit: candidateLimit,
|
|
1474
1679
|
threshold,
|
|
1475
1680
|
documentIds: input.documentIds
|
|
1476
1681
|
},
|
|
1477
1682
|
authToken: input.authToken,
|
|
1478
|
-
...peerPeers.length > 0 ? { peers: peerPeers } : {}
|
|
1683
|
+
...peerPeers.length > 0 ? { peers: peerPeers } : {},
|
|
1684
|
+
...runtimeContext ?? {}
|
|
1479
1685
|
});
|
|
1480
1686
|
for (const hit of hits) {
|
|
1481
1687
|
knowledgeHits.push(toKnowledgeResult(hit));
|
|
@@ -1493,10 +1699,14 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1493
1699
|
memorySubs,
|
|
1494
1700
|
insightHits,
|
|
1495
1701
|
knowledgeHits,
|
|
1496
|
-
limit,
|
|
1702
|
+
limit: candidateLimit,
|
|
1497
1703
|
strategy: mergeStrategy
|
|
1498
1704
|
});
|
|
1499
|
-
const
|
|
1705
|
+
const rerankerStartedAt = deps.reranker ? Date.now() : void 0;
|
|
1706
|
+
const reranked = await applyReranker(deps.reranker, input.query, merged);
|
|
1707
|
+
const rerankerLatencyMs = rerankerStartedAt === void 0 ? 0 : Date.now() - rerankerStartedAt;
|
|
1708
|
+
const rerankerOrderChanged = Boolean(deps.reranker) && merged.some((hit, index) => hit.id !== reranked[index]?.id);
|
|
1709
|
+
const ranked = reranked.slice(0, limit);
|
|
1500
1710
|
const output = {
|
|
1501
1711
|
query: input.query,
|
|
1502
1712
|
sources,
|
|
@@ -1504,28 +1714,51 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1504
1714
|
count: ranked.length,
|
|
1505
1715
|
warnings
|
|
1506
1716
|
};
|
|
1717
|
+
if (input.includeRetrievalDiagnostics) {
|
|
1718
|
+
output.retrievalDiagnostics = {
|
|
1719
|
+
mergeStrategy,
|
|
1720
|
+
candidateLimit,
|
|
1721
|
+
backend: retrievalStatus?.backend,
|
|
1722
|
+
semanticDegradedReason: retrievalStatus?.semanticDegradedReason ?? warnings.find((warning) => warning.code.startsWith("semantic_"))?.code,
|
|
1723
|
+
candidateCounts: {
|
|
1724
|
+
semantic: memorySubs?.semantic.length ?? 0,
|
|
1725
|
+
lexical: memorySubs?.lexical.length ?? 0,
|
|
1726
|
+
hybrid: memorySubs?.hybrid?.length ?? 0,
|
|
1727
|
+
entity: memorySubs?.entity?.length ?? 0,
|
|
1728
|
+
fused: merged.length,
|
|
1729
|
+
final: ranked.length
|
|
1730
|
+
},
|
|
1731
|
+
channels: {
|
|
1732
|
+
semantic: memorySubs?.semantic ?? [],
|
|
1733
|
+
lexical: memorySubs?.lexical ?? [],
|
|
1734
|
+
...memorySubs?.hybrid ? { hybrid: memorySubs.hybrid } : {},
|
|
1735
|
+
...memorySubs?.entity ? { entity: memorySubs.entity } : {}
|
|
1736
|
+
},
|
|
1737
|
+
fusedBeforeRerank: merged,
|
|
1738
|
+
reranker: {
|
|
1739
|
+
enabled: Boolean(deps.reranker),
|
|
1740
|
+
provider: deps.rerankerInfo?.provider,
|
|
1741
|
+
model: deps.rerankerInfo?.model,
|
|
1742
|
+
inputCount: merged.length,
|
|
1743
|
+
outputCount: reranked.length,
|
|
1744
|
+
latencyMs: rerankerLatencyMs,
|
|
1745
|
+
orderChanged: rerankerOrderChanged
|
|
1746
|
+
},
|
|
1747
|
+
final: ranked
|
|
1748
|
+
};
|
|
1749
|
+
}
|
|
1507
1750
|
if (reasoningInfo && (reasoningInfo.strategy !== "none" || reasoningInfo.dateRange !== void 0)) {
|
|
1508
1751
|
output.reasoning = reasoningInfo;
|
|
1509
1752
|
}
|
|
1510
1753
|
return output;
|
|
1511
1754
|
}
|
|
1755
|
+
async function searchUnifiedMemory(input) {
|
|
1756
|
+
return searchUnifiedMemoryWithRuntime(input);
|
|
1757
|
+
}
|
|
1512
1758
|
async function searchRawMemorySemantically(input) {
|
|
1513
|
-
|
|
1514
|
-
const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
|
|
1515
|
-
const reasoningStrategy = resolveReasoningStrategy(input);
|
|
1516
|
-
const mergeStrategy = normalizeUnifiedMemoryMergeStrategy(
|
|
1517
|
-
input.mergeStrategy ?? deps.reasoning?.defaultMergeStrategy ?? "rrf"
|
|
1518
|
-
);
|
|
1519
|
-
const sub = await runMemorySource(input, limit, threshold, [], reasoningStrategy);
|
|
1520
|
-
return mergeAcrossSources({
|
|
1521
|
-
memorySubs: sub,
|
|
1522
|
-
insightHits: [],
|
|
1523
|
-
knowledgeHits: [],
|
|
1524
|
-
limit,
|
|
1525
|
-
strategy: mergeStrategy
|
|
1526
|
-
});
|
|
1759
|
+
return (await searchUnifiedMemory({ ...input, sources: ["memory"] })).results;
|
|
1527
1760
|
}
|
|
1528
|
-
async function
|
|
1761
|
+
async function searchWithResolvedRuntime(input, runtimeContext) {
|
|
1529
1762
|
const query = input.query.trim();
|
|
1530
1763
|
const wantsSynthesis = typeof input.synthesize === "boolean" ? input.synthesize : typeof input.synthesize === "object" && input.synthesize !== null;
|
|
1531
1764
|
const synthesisSchema = typeof input.synthesize === "object" && input.synthesize !== null ? input.synthesize.responseSchema ?? input.responseSchema : input.responseSchema;
|
|
@@ -1557,19 +1790,24 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1557
1790
|
let unifiedHits = [];
|
|
1558
1791
|
let sources = input.sources ? [...input.sources] : [];
|
|
1559
1792
|
let unifiedReasoning;
|
|
1793
|
+
let retrievalDiagnostics;
|
|
1560
1794
|
if (wantsRawTier || wantsInsightTier || wantsKnowledgeTier) {
|
|
1561
1795
|
const inferredSources = sources.length > 0 ? sources : [
|
|
1562
1796
|
...wantsRawTier ? ["memory"] : [],
|
|
1563
1797
|
...wantsInsightTier ? ["insights"] : [],
|
|
1564
1798
|
...wantsKnowledgeTier ? ["knowledge"] : []
|
|
1565
1799
|
];
|
|
1566
|
-
const unified = await
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1800
|
+
const unified = await searchUnifiedMemoryWithRuntime(
|
|
1801
|
+
{
|
|
1802
|
+
...searchInputToUnified(input),
|
|
1803
|
+
sources: inferredSources.length > 0 ? inferredSources : void 0
|
|
1804
|
+
},
|
|
1805
|
+
runtimeContext
|
|
1806
|
+
);
|
|
1570
1807
|
unifiedHits = unified.results;
|
|
1571
1808
|
sources = unified.sources;
|
|
1572
1809
|
unifiedReasoning = unified.reasoning;
|
|
1810
|
+
retrievalDiagnostics = unified.retrievalDiagnostics;
|
|
1573
1811
|
warnings.push(...unified.warnings);
|
|
1574
1812
|
}
|
|
1575
1813
|
let summaryHits = [];
|
|
@@ -1580,7 +1818,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1580
1818
|
limit,
|
|
1581
1819
|
threshold,
|
|
1582
1820
|
logger,
|
|
1583
|
-
peerScope.peers
|
|
1821
|
+
peerScope.peers,
|
|
1822
|
+
runtimeContext
|
|
1584
1823
|
);
|
|
1585
1824
|
warnings.push(...summaryBucket.warnings);
|
|
1586
1825
|
summaryHits = summaryBucket.hits;
|
|
@@ -1605,7 +1844,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1605
1844
|
source: mapHitToEvidenceSource(hit),
|
|
1606
1845
|
snippet: hit.content,
|
|
1607
1846
|
score: hit.similarity,
|
|
1608
|
-
timestamp: getCandidateTimestamp(hit.metadata)
|
|
1847
|
+
timestamp: getCandidateTimestamp(hit.metadata),
|
|
1848
|
+
metadata: hit.metadata
|
|
1609
1849
|
}));
|
|
1610
1850
|
const base = {
|
|
1611
1851
|
query,
|
|
@@ -1618,12 +1858,15 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1618
1858
|
if (unifiedReasoning) {
|
|
1619
1859
|
base.reasoning = unifiedReasoning;
|
|
1620
1860
|
}
|
|
1861
|
+
if (retrievalDiagnostics) {
|
|
1862
|
+
base.retrievalDiagnostics = retrievalDiagnostics;
|
|
1863
|
+
}
|
|
1621
1864
|
if (!wantsSynthesis) {
|
|
1622
1865
|
return base;
|
|
1623
1866
|
}
|
|
1624
1867
|
const { answer, warnings: synthWarnings } = await synthesizeAnswer({
|
|
1625
1868
|
query,
|
|
1626
|
-
|
|
1869
|
+
evidence,
|
|
1627
1870
|
responseSchema: synthesisSchema,
|
|
1628
1871
|
deps,
|
|
1629
1872
|
logger
|
|
@@ -1632,6 +1875,10 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1632
1875
|
base.warnings = [...base.warnings, ...synthWarnings];
|
|
1633
1876
|
return base;
|
|
1634
1877
|
}
|
|
1878
|
+
async function search(input, runtimeContext) {
|
|
1879
|
+
const resolvedRuntime = resolveSearchRuntimeContext(input, runtimeContext);
|
|
1880
|
+
return searchWithResolvedRuntime(input, resolvedRuntime);
|
|
1881
|
+
}
|
|
1635
1882
|
return {
|
|
1636
1883
|
search,
|
|
1637
1884
|
searchUnifiedMemory,
|
|
@@ -1647,6 +1894,7 @@ function mergeAcrossSources(input) {
|
|
|
1647
1894
|
for (const hit of [
|
|
1648
1895
|
...memorySubs.semantic,
|
|
1649
1896
|
...memorySubs.lexical,
|
|
1897
|
+
...memorySubs.hybrid ?? [],
|
|
1650
1898
|
...input.insightHits,
|
|
1651
1899
|
...input.knowledgeHits
|
|
1652
1900
|
]) {
|
|
@@ -1662,6 +1910,7 @@ function mergeAcrossSources(input) {
|
|
|
1662
1910
|
const all = [
|
|
1663
1911
|
...memorySubs.semantic,
|
|
1664
1912
|
...memorySubs.lexical,
|
|
1913
|
+
...memorySubs.hybrid ?? [],
|
|
1665
1914
|
...memorySubs.entity ?? [],
|
|
1666
1915
|
...input.insightHits,
|
|
1667
1916
|
...input.knowledgeHits
|
|
@@ -1673,6 +1922,9 @@ function buildChannelLists(memorySubs, insightHits, knowledgeHits) {
|
|
|
1673
1922
|
const lists = [];
|
|
1674
1923
|
if (memorySubs.semantic.length > 0) lists.push({ name: "memory-semantic", hits: memorySubs.semantic });
|
|
1675
1924
|
if (memorySubs.lexical.length > 0) lists.push({ name: "memory-bm25", hits: memorySubs.lexical });
|
|
1925
|
+
if (memorySubs.hybrid && memorySubs.hybrid.length > 0) {
|
|
1926
|
+
lists.push({ name: "memory-hybrid", hits: memorySubs.hybrid });
|
|
1927
|
+
}
|
|
1676
1928
|
if (memorySubs.entity && memorySubs.entity.length > 0) {
|
|
1677
1929
|
lists.push({ name: "memory-entity", hits: memorySubs.entity });
|
|
1678
1930
|
}
|