@melandlabs/memory-store 1.2.5 → 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-Dw4aXj2a.d.ts → config-CbE2t4SW.d.ts} +172 -17
- package/dist/http.d.ts +2 -1
- package/dist/http.js +594 -270
- package/dist/index.d.ts +10 -6
- package/dist/index.js +316 -99
- package/dist/mcp.d.ts +2 -1
- package/dist/mcp.js +615 -250
- 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 +316 -99
- package/dist/server/cli-http.js +966 -320
- package/dist/server/cli-mcp.js +987 -300
- 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-DfE8ek7W.d.ts → unified-search-2YQ6EGuQ.d.ts} +17 -6
- package/package.json +16 -12
|
@@ -249,6 +249,69 @@ var init_raw_message_store = __esm({
|
|
|
249
249
|
init_chroma_memory_index();
|
|
250
250
|
init_raw_message_store();
|
|
251
251
|
|
|
252
|
+
// src/search/applicability.ts
|
|
253
|
+
var APPLICABILITY_SCOPES = /* @__PURE__ */ new Set([
|
|
254
|
+
"global",
|
|
255
|
+
"task",
|
|
256
|
+
"conversation",
|
|
257
|
+
"channel",
|
|
258
|
+
"project",
|
|
259
|
+
"custom"
|
|
260
|
+
]);
|
|
261
|
+
function isRecord(value) {
|
|
262
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
263
|
+
}
|
|
264
|
+
function invalidRuntimeContext(message) {
|
|
265
|
+
throw new TypeError(`memory-store search runtime context: ${message}`);
|
|
266
|
+
}
|
|
267
|
+
function validateApplicabilityContext(value, index) {
|
|
268
|
+
const path = `applicabilityContexts[${index}]`;
|
|
269
|
+
if (!isRecord(value)) {
|
|
270
|
+
invalidRuntimeContext(`${path} must be an object`);
|
|
271
|
+
}
|
|
272
|
+
const scope = value.scope;
|
|
273
|
+
if (typeof scope !== "string" || !APPLICABILITY_SCOPES.has(scope)) {
|
|
274
|
+
invalidRuntimeContext(`${path}.scope is not supported`);
|
|
275
|
+
}
|
|
276
|
+
if (value.key !== void 0 && typeof value.key !== "string") {
|
|
277
|
+
invalidRuntimeContext(`${path}.key must be a string when provided`);
|
|
278
|
+
}
|
|
279
|
+
if (scope !== "global" && (typeof value.key !== "string" || value.key.trim().length === 0)) {
|
|
280
|
+
invalidRuntimeContext(`${path}.key must be non-empty for scope "${scope}"`);
|
|
281
|
+
}
|
|
282
|
+
for (const field of ["validFrom", "validUntil"]) {
|
|
283
|
+
const boundary = value[field];
|
|
284
|
+
if (boundary !== void 0 && (typeof boundary !== "number" || !Number.isFinite(boundary))) {
|
|
285
|
+
invalidRuntimeContext(`${path}.${field} must be a finite epoch-millisecond number`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function resolveSearchRuntimeContext(input, runtimeContext, now = Date.now) {
|
|
290
|
+
if (runtimeContext === void 0) {
|
|
291
|
+
return void 0;
|
|
292
|
+
}
|
|
293
|
+
if (!isRecord(runtimeContext) || !Array.isArray(runtimeContext.applicabilityContexts)) {
|
|
294
|
+
invalidRuntimeContext("applicabilityContexts must be an array");
|
|
295
|
+
}
|
|
296
|
+
for (const [index, context] of runtimeContext.applicabilityContexts.entries()) {
|
|
297
|
+
validateApplicabilityContext(context, index);
|
|
298
|
+
}
|
|
299
|
+
let applicabilityAt;
|
|
300
|
+
if (input.asOf === void 0) {
|
|
301
|
+
applicabilityAt = now();
|
|
302
|
+
} else {
|
|
303
|
+
const parsed = typeof input.asOf === "string" ? Date.parse(input.asOf) : Number.NaN;
|
|
304
|
+
if (!Number.isFinite(parsed)) {
|
|
305
|
+
invalidRuntimeContext("asOf must be a parseable timestamp when a runtime context is supplied");
|
|
306
|
+
}
|
|
307
|
+
applicabilityAt = parsed;
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
applicabilityContexts: runtimeContext.applicabilityContexts,
|
|
311
|
+
applicabilityAt
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
252
315
|
// src/search/utilities.ts
|
|
253
316
|
function deriveLexicalKeywords(query) {
|
|
254
317
|
return query.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((token) => token.length >= 2).slice(0, 16);
|
|
@@ -407,6 +470,7 @@ function materializeSignals(lists, hit) {
|
|
|
407
470
|
function listNameToChannel(name) {
|
|
408
471
|
if (name === "memory-semantic") return "semantic";
|
|
409
472
|
if (name === "memory-bm25" || name === "memory-lexical") return "lexical";
|
|
473
|
+
if (name === "memory-hybrid") return "hybrid";
|
|
410
474
|
if (name === "memory-entity") return "entity";
|
|
411
475
|
return void 0;
|
|
412
476
|
}
|
|
@@ -616,7 +680,6 @@ async function applyReranker(reranker, query, results) {
|
|
|
616
680
|
|
|
617
681
|
// src/search/gather-evidence.ts
|
|
618
682
|
var DEFAULT_TIERS2 = ["summary", "raw", "insight", "knowledge"];
|
|
619
|
-
var MAX_EVIDENCE_CHARS = 600;
|
|
620
683
|
function toSummaryResult(row) {
|
|
621
684
|
const start = typeof row.startTimestamp === "number" ? row.startTimestamp : void 0;
|
|
622
685
|
const end = typeof row.endTimestamp === "number" ? row.endTimestamp : void 0;
|
|
@@ -634,7 +697,7 @@ function toSummaryResult(row) {
|
|
|
634
697
|
}
|
|
635
698
|
};
|
|
636
699
|
}
|
|
637
|
-
async function gatherSummaries(deps, input, limit, threshold, logger, peerPeers) {
|
|
700
|
+
async function gatherSummaries(deps, input, limit, threshold, logger, peerPeers, runtimeContext) {
|
|
638
701
|
if (typeof deps.searchSummaries !== "function") {
|
|
639
702
|
return { hits: [], warnings: [] };
|
|
640
703
|
}
|
|
@@ -647,7 +710,8 @@ async function gatherSummaries(deps, input, limit, threshold, logger, peerPeers)
|
|
|
647
710
|
limit,
|
|
648
711
|
threshold,
|
|
649
712
|
authToken: input.authToken,
|
|
650
|
-
...peerPeers.length > 0 ? { peers: peerPeers } : {}
|
|
713
|
+
...peerPeers.length > 0 ? { peers: peerPeers } : {},
|
|
714
|
+
...runtimeContext ?? {}
|
|
651
715
|
});
|
|
652
716
|
const hits = rows.map(toSummaryResult);
|
|
653
717
|
return { hits, warnings: [] };
|
|
@@ -665,12 +729,6 @@ async function gatherSummaries(deps, input, limit, threshold, logger, peerPeers)
|
|
|
665
729
|
};
|
|
666
730
|
}
|
|
667
731
|
}
|
|
668
|
-
function truncate(input, max = MAX_EVIDENCE_CHARS) {
|
|
669
|
-
if (input.length <= max) {
|
|
670
|
-
return input;
|
|
671
|
-
}
|
|
672
|
-
return `${input.slice(0, max - 1)}\u2026`;
|
|
673
|
-
}
|
|
674
732
|
function buildSynthesisPrompt(input) {
|
|
675
733
|
const grouped = /* @__PURE__ */ new Map();
|
|
676
734
|
for (const item of input.evidence) {
|
|
@@ -684,9 +742,10 @@ function buildSynthesisPrompt(input) {
|
|
|
684
742
|
if (items.length === 0) {
|
|
685
743
|
continue;
|
|
686
744
|
}
|
|
687
|
-
const lines = items.map(
|
|
688
|
-
|
|
689
|
-
|
|
745
|
+
const lines = items.map((item, index) => {
|
|
746
|
+
const factSource = typeof item.metadata?.source === "string" && item.metadata.source.length > 0 ? item.metadata.source : item.source ?? "unknown";
|
|
747
|
+
return ` [${index + 1}] (${item.id}, source=${factSource}, score=${item.score.toFixed(4)}) ${item.snippet}`;
|
|
748
|
+
});
|
|
690
749
|
sections.push(`## ${tier}
|
|
691
750
|
${lines.join("\n")}`);
|
|
692
751
|
}
|
|
@@ -768,12 +827,15 @@ function coerceAnswer(text, schema) {
|
|
|
768
827
|
return text;
|
|
769
828
|
}
|
|
770
829
|
async function synthesizeAnswer(input) {
|
|
771
|
-
const { query,
|
|
772
|
-
const evidence =
|
|
773
|
-
id:
|
|
774
|
-
source:
|
|
775
|
-
snippet:
|
|
776
|
-
score:
|
|
830
|
+
const { query, responseSchema, deps, logger } = input;
|
|
831
|
+
const evidence = input.evidence.map((item) => ({
|
|
832
|
+
id: item.id,
|
|
833
|
+
source: mapEvidenceSourceToTier(item.source),
|
|
834
|
+
snippet: item.snippet,
|
|
835
|
+
score: item.score,
|
|
836
|
+
// Forward per-fact provenance (e.g. `metadata.source` from
|
|
837
|
+
// `opencontext add --source`) so the synthesis prompt can cite it.
|
|
838
|
+
metadata: item.metadata
|
|
777
839
|
}));
|
|
778
840
|
const warnings = [];
|
|
779
841
|
const complete = deps.reasoning?.complete;
|
|
@@ -804,21 +866,10 @@ async function synthesizeAnswer(input) {
|
|
|
804
866
|
return { answer: "", warnings };
|
|
805
867
|
}
|
|
806
868
|
}
|
|
807
|
-
function
|
|
808
|
-
|
|
809
|
-
if (
|
|
810
|
-
|
|
811
|
-
}
|
|
812
|
-
if (hit.type === "memory") {
|
|
813
|
-
return "raw";
|
|
814
|
-
}
|
|
815
|
-
if (hit.type === "insight") {
|
|
816
|
-
return "insight";
|
|
817
|
-
}
|
|
818
|
-
if (hit.type === "knowledge") {
|
|
819
|
-
return "knowledge";
|
|
820
|
-
}
|
|
821
|
-
return hit.type;
|
|
869
|
+
function mapEvidenceSourceToTier(source) {
|
|
870
|
+
if (source === "memory") return "raw";
|
|
871
|
+
if (source === "insights") return "insight";
|
|
872
|
+
return source;
|
|
822
873
|
}
|
|
823
874
|
async function resolveSearchScopePeers(input) {
|
|
824
875
|
return resolveScopePeer({
|
|
@@ -842,7 +893,7 @@ function deriveEntityKeywords(query) {
|
|
|
842
893
|
}
|
|
843
894
|
return out.slice(0, 16);
|
|
844
895
|
}
|
|
845
|
-
async function runEntitySearchForQuery(deps, input, limit, logger, peerPeers = []) {
|
|
896
|
+
async function runEntitySearchForQuery(deps, input, limit, logger, peerPeers = [], runtimeContext) {
|
|
846
897
|
if (typeof deps.entitySearch !== "function") {
|
|
847
898
|
return [];
|
|
848
899
|
}
|
|
@@ -860,7 +911,8 @@ async function runEntitySearchForQuery(deps, input, limit, logger, peerPeers = [
|
|
|
860
911
|
keywords,
|
|
861
912
|
limit: Math.ceil(limit / filters.length),
|
|
862
913
|
botId,
|
|
863
|
-
...peerPeers.length > 0 ? { peers: peerPeers } : {}
|
|
914
|
+
...peerPeers.length > 0 ? { peers: peerPeers } : {},
|
|
915
|
+
...runtimeContext ?? {}
|
|
864
916
|
})
|
|
865
917
|
)
|
|
866
918
|
)).flat();
|
|
@@ -948,6 +1000,16 @@ function filterByDateRange(items, dateFrom, dateTo) {
|
|
|
948
1000
|
return true;
|
|
949
1001
|
});
|
|
950
1002
|
}
|
|
1003
|
+
function warnApplicabilityNotEnforced(warnings) {
|
|
1004
|
+
if (warnings.some((warning) => warning.code === "memory_applicability_not_enforced")) {
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
warnings.push({
|
|
1008
|
+
source: "memory",
|
|
1009
|
+
code: "memory_applicability_not_enforced",
|
|
1010
|
+
message: "Built-in raw-message retrieval was skipped because it cannot enforce the supplied applicability context. Configure an applicability-aware raw-message provider."
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
951
1013
|
async function embedQueryVariant(embedQuery, input, query) {
|
|
952
1014
|
return embedQuery({
|
|
953
1015
|
userId: input.userId,
|
|
@@ -955,10 +1017,29 @@ async function embedQueryVariant(embedQuery, input, query) {
|
|
|
955
1017
|
authToken: input.authToken
|
|
956
1018
|
});
|
|
957
1019
|
}
|
|
958
|
-
async function runSemanticSearchForEmbedding(deps, input, queryEmbedding, limit, threshold, logger, peerPeers
|
|
1020
|
+
async function runSemanticSearchForEmbedding(deps, input, queryEmbedding, limit, threshold, logger, peerPeers, warnings, runtimeContext) {
|
|
959
1021
|
const filters = input.botIds && input.botIds.length > 0 ? input.botIds.map((botId) => ({ botId })) : [{}];
|
|
960
1022
|
let semantic = [];
|
|
961
|
-
if (
|
|
1023
|
+
if (typeof deps.searchRawMessagesAnn === "function") {
|
|
1024
|
+
const searchRawMessagesAnn = deps.searchRawMessagesAnn;
|
|
1025
|
+
const factTypes = input.factTypes?.length ? input.factTypes : void 0;
|
|
1026
|
+
const includeDeprecated = input.includeDeprecated === true;
|
|
1027
|
+
semantic = (await Promise.all(
|
|
1028
|
+
filters.map(
|
|
1029
|
+
(filter) => searchRawMessagesAnn({
|
|
1030
|
+
userId: input.userId,
|
|
1031
|
+
queryEmbedding,
|
|
1032
|
+
limit,
|
|
1033
|
+
threshold,
|
|
1034
|
+
botId: "botId" in filter ? filter.botId : void 0,
|
|
1035
|
+
includeDeprecated,
|
|
1036
|
+
...peerPeers.length > 0 ? { peers: peerPeers } : {},
|
|
1037
|
+
...factTypes ? { factTypes } : {},
|
|
1038
|
+
...runtimeContext ?? {}
|
|
1039
|
+
})
|
|
1040
|
+
)
|
|
1041
|
+
)).flat().filter(isRawMemorySemanticResult).map(toMemoryResult);
|
|
1042
|
+
} else if (runtimeContext === void 0 && isRawMessageChromaEnabled()) {
|
|
962
1043
|
try {
|
|
963
1044
|
semantic = (await Promise.all(
|
|
964
1045
|
filters.map((filter) => {
|
|
@@ -983,24 +1064,12 @@ async function runSemanticSearchForEmbedding(deps, input, queryEmbedding, limit,
|
|
|
983
1064
|
error
|
|
984
1065
|
);
|
|
985
1066
|
}
|
|
986
|
-
} else if (typeof deps.searchRawMessagesAnn === "function") {
|
|
987
|
-
const searchRawMessagesAnn = deps.searchRawMessagesAnn;
|
|
988
|
-
const factTypes = input.factTypes?.length ? input.factTypes : void 0;
|
|
989
|
-
semantic = (await Promise.all(
|
|
990
|
-
filters.map(
|
|
991
|
-
(filter) => searchRawMessagesAnn({
|
|
992
|
-
userId: input.userId,
|
|
993
|
-
queryEmbedding,
|
|
994
|
-
limit,
|
|
995
|
-
threshold,
|
|
996
|
-
botId: "botId" in filter ? filter.botId : void 0,
|
|
997
|
-
...peerPeers.length > 0 ? { peers: peerPeers } : {},
|
|
998
|
-
...factTypes ? { factTypes } : {}
|
|
999
|
-
})
|
|
1000
|
-
)
|
|
1001
|
-
)).flat().filter(isRawMemorySemanticResult).map(toMemoryResult);
|
|
1002
1067
|
}
|
|
1003
|
-
if (semantic.length === 0) {
|
|
1068
|
+
if (semantic.length === 0 && runtimeContext !== void 0) {
|
|
1069
|
+
warnApplicabilityNotEnforced(warnings);
|
|
1070
|
+
return [];
|
|
1071
|
+
}
|
|
1072
|
+
if (semantic.length === 0 && typeof deps.searchRawMessagesAnn !== "function") {
|
|
1004
1073
|
try {
|
|
1005
1074
|
const { getRawMessageManager: getRawMessageManager2 } = await Promise.resolve().then(() => (init_raw_message_store(), raw_message_store_exports));
|
|
1006
1075
|
const manager2 = await getRawMessageManager2();
|
|
@@ -1011,6 +1080,7 @@ async function runSemanticSearchForEmbedding(deps, input, queryEmbedding, limit,
|
|
|
1011
1080
|
queryEmbedding,
|
|
1012
1081
|
limit,
|
|
1013
1082
|
threshold,
|
|
1083
|
+
includeDeprecated: input.includeDeprecated === true,
|
|
1014
1084
|
...peerPeers.length > 0 ? { peers: peerPeers } : {},
|
|
1015
1085
|
...factTypes ? { factTypes } : {}
|
|
1016
1086
|
});
|
|
@@ -1040,6 +1110,18 @@ function mergeByMaxScore(lists) {
|
|
|
1040
1110
|
}
|
|
1041
1111
|
return Array.from(best.values()).sort((a, b) => b.similarity - a.similarity);
|
|
1042
1112
|
}
|
|
1113
|
+
function dedupeChannelByParent(hits) {
|
|
1114
|
+
const strongest = /* @__PURE__ */ new Map();
|
|
1115
|
+
for (const hit of hits) {
|
|
1116
|
+
const sourceMessageId = hit.metadata?.sourceMessageId;
|
|
1117
|
+
const parentId = typeof sourceMessageId === "string" && sourceMessageId.length > 0 ? sourceMessageId : hit.id;
|
|
1118
|
+
const normalized = hit.type === "memory" && hit.id !== parentId ? { ...hit, id: parentId } : hit;
|
|
1119
|
+
const key = `${normalized.type}:${parentId}`;
|
|
1120
|
+
const current = strongest.get(key);
|
|
1121
|
+
if (!current || normalized.similarity > current.similarity) strongest.set(key, normalized);
|
|
1122
|
+
}
|
|
1123
|
+
return [...strongest.values()].sort((a, b) => b.similarity - a.similarity);
|
|
1124
|
+
}
|
|
1043
1125
|
function toIterativeRecallCandidate(hit) {
|
|
1044
1126
|
return {
|
|
1045
1127
|
id: hit.id,
|
|
@@ -1048,7 +1130,7 @@ function toIterativeRecallCandidate(hit) {
|
|
|
1048
1130
|
metadata: hit.metadata
|
|
1049
1131
|
};
|
|
1050
1132
|
}
|
|
1051
|
-
async function runLexicalSearchForKeywords(deps, input, keywords, limit, logger, peerPeers
|
|
1133
|
+
async function runLexicalSearchForKeywords(deps, input, keywords, limit, logger, peerPeers, warnings, runtimeContext) {
|
|
1052
1134
|
if (keywords.length === 0) {
|
|
1053
1135
|
return [];
|
|
1054
1136
|
}
|
|
@@ -1057,6 +1139,7 @@ async function runLexicalSearchForKeywords(deps, input, keywords, limit, logger,
|
|
|
1057
1139
|
const filters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
|
|
1058
1140
|
const searchRawMessagesLexical = deps.searchRawMessagesLexical;
|
|
1059
1141
|
const factTypes = input.factTypes?.length ? input.factTypes : void 0;
|
|
1142
|
+
const includeDeprecated = input.includeDeprecated === true;
|
|
1060
1143
|
return (await Promise.all(
|
|
1061
1144
|
filters.map(
|
|
1062
1145
|
(botId) => searchRawMessagesLexical({
|
|
@@ -1064,8 +1147,10 @@ async function runLexicalSearchForKeywords(deps, input, keywords, limit, logger,
|
|
|
1064
1147
|
keywords,
|
|
1065
1148
|
limit: Math.ceil(limit / filters.length),
|
|
1066
1149
|
botId,
|
|
1150
|
+
includeDeprecated,
|
|
1067
1151
|
...peerPeers.length > 0 ? { peers: peerPeers } : {},
|
|
1068
|
-
...factTypes ? { factTypes } : {}
|
|
1152
|
+
...factTypes ? { factTypes } : {},
|
|
1153
|
+
...runtimeContext ?? {}
|
|
1069
1154
|
})
|
|
1070
1155
|
)
|
|
1071
1156
|
)).flat().filter(isRawMemorySemanticResult).map((hit) => ({
|
|
@@ -1076,10 +1161,15 @@ async function runLexicalSearchForKeywords(deps, input, keywords, limit, logger,
|
|
|
1076
1161
|
logger.warn?.("[memory-store] lexical memory search failed:", error);
|
|
1077
1162
|
}
|
|
1078
1163
|
}
|
|
1164
|
+
if (runtimeContext !== void 0) {
|
|
1165
|
+
warnApplicabilityNotEnforced(warnings);
|
|
1166
|
+
return [];
|
|
1167
|
+
}
|
|
1079
1168
|
try {
|
|
1080
1169
|
const { lexicalSearchRawMessages: lexicalSearchRawMessages2 } = await Promise.resolve().then(() => (init_sqlite_raw_message_store(), sqlite_raw_message_store_exports));
|
|
1081
1170
|
const filters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
|
|
1082
1171
|
const factTypes = input.factTypes?.length ? input.factTypes : void 0;
|
|
1172
|
+
const includeDeprecated = input.includeDeprecated === true;
|
|
1083
1173
|
return (await Promise.all(
|
|
1084
1174
|
filters.map(
|
|
1085
1175
|
(botId) => lexicalSearchRawMessages2({
|
|
@@ -1087,6 +1177,7 @@ async function runLexicalSearchForKeywords(deps, input, keywords, limit, logger,
|
|
|
1087
1177
|
keywords,
|
|
1088
1178
|
limit: Math.ceil(limit / filters.length),
|
|
1089
1179
|
botId,
|
|
1180
|
+
includeDeprecated,
|
|
1090
1181
|
...factTypes ? { factTypes } : {}
|
|
1091
1182
|
})
|
|
1092
1183
|
)
|
|
@@ -1117,7 +1208,9 @@ function searchInputToUnified(input) {
|
|
|
1117
1208
|
mergeStrategy: input.mergeStrategy,
|
|
1118
1209
|
peerFilter: input.peerFilter,
|
|
1119
1210
|
reasoningStrategy: input.reasoningStrategy,
|
|
1120
|
-
factTypes: input.factTypes
|
|
1211
|
+
factTypes: input.factTypes,
|
|
1212
|
+
includeRetrievalDiagnostics: input.includeRetrievalDiagnostics,
|
|
1213
|
+
includeDeprecated: input.includeDeprecated
|
|
1121
1214
|
};
|
|
1122
1215
|
}
|
|
1123
1216
|
function mapHitToEvidenceSource(hit) {
|
|
@@ -1145,7 +1238,7 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1145
1238
|
}
|
|
1146
1239
|
return normalizeUnifiedMemoryReasoningStrategy(deps.reasoning?.defaultStrategy, "none");
|
|
1147
1240
|
}
|
|
1148
|
-
async function runMemorySource(input, limit, threshold, warnings, reasoningStrategy = "none", reasoningInfo, peerPeers = []) {
|
|
1241
|
+
async function runMemorySource(input, limit, threshold, warnings, reasoningStrategy = "none", reasoningInfo, peerPeers = [], runtimeContext) {
|
|
1149
1242
|
if ((reasoningStrategy === "iterative" || reasoningStrategy === "union") && !deps.reasoning?.iterativePlanner) {
|
|
1150
1243
|
warnings.push({
|
|
1151
1244
|
source: "memory",
|
|
@@ -1178,7 +1271,9 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1178
1271
|
keywords2,
|
|
1179
1272
|
limit,
|
|
1180
1273
|
logger,
|
|
1181
|
-
peerPeers
|
|
1274
|
+
peerPeers,
|
|
1275
|
+
warnings,
|
|
1276
|
+
runtimeContext
|
|
1182
1277
|
);
|
|
1183
1278
|
let semanticHits = [];
|
|
1184
1279
|
if (typeof deps.embedQuery === "function") {
|
|
@@ -1192,7 +1287,9 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1192
1287
|
limit,
|
|
1193
1288
|
threshold,
|
|
1194
1289
|
logger,
|
|
1195
|
-
peerPeers
|
|
1290
|
+
peerPeers,
|
|
1291
|
+
warnings,
|
|
1292
|
+
runtimeContext
|
|
1196
1293
|
);
|
|
1197
1294
|
} catch (error) {
|
|
1198
1295
|
logger.warn?.(
|
|
@@ -1233,6 +1330,11 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1233
1330
|
unionEvidence = evidence;
|
|
1234
1331
|
}
|
|
1235
1332
|
if (typeof deps.embedQuery !== "function") {
|
|
1333
|
+
warnings.push({
|
|
1334
|
+
source: "memory",
|
|
1335
|
+
code: "semantic_unavailable",
|
|
1336
|
+
message: "Semantic search is unavailable because no embedding provider is configured."
|
|
1337
|
+
});
|
|
1236
1338
|
warnings.push({
|
|
1237
1339
|
source: "memory",
|
|
1238
1340
|
code: "memory_lexical_search_fallback",
|
|
@@ -1240,7 +1342,16 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1240
1342
|
});
|
|
1241
1343
|
const keywords2 = deriveLexicalKeywords(input.query);
|
|
1242
1344
|
const lexical2 = filterByDateRange(
|
|
1243
|
-
await runLexicalSearchForKeywords(
|
|
1345
|
+
await runLexicalSearchForKeywords(
|
|
1346
|
+
deps,
|
|
1347
|
+
input,
|
|
1348
|
+
keywords2,
|
|
1349
|
+
limit,
|
|
1350
|
+
logger,
|
|
1351
|
+
peerPeers,
|
|
1352
|
+
warnings,
|
|
1353
|
+
runtimeContext
|
|
1354
|
+
),
|
|
1244
1355
|
input.dateFrom,
|
|
1245
1356
|
input.dateTo
|
|
1246
1357
|
);
|
|
@@ -1248,6 +1359,26 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1248
1359
|
}
|
|
1249
1360
|
let semantic = [];
|
|
1250
1361
|
const embedQuery = deps.embedQuery;
|
|
1362
|
+
const activeMergeStrategy = normalizeUnifiedMemoryMergeStrategy(
|
|
1363
|
+
input.mergeStrategy ?? deps.reasoning?.defaultMergeStrategy ?? "rrf"
|
|
1364
|
+
);
|
|
1365
|
+
if (reasoningStrategy === "none" && activeMergeStrategy === "rrf" && input.threshold === void 0 && typeof deps.searchRawMessagesHybrid === "function") {
|
|
1366
|
+
const queryEmbedding = await embedQueryVariant(embedQuery, input, input.query);
|
|
1367
|
+
const filters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
|
|
1368
|
+
const hybrid = (await Promise.all(
|
|
1369
|
+
filters.map(
|
|
1370
|
+
(botId) => deps.searchRawMessagesHybrid?.({
|
|
1371
|
+
userId: input.userId,
|
|
1372
|
+
query: input.query,
|
|
1373
|
+
queryEmbedding,
|
|
1374
|
+
limit: Math.ceil(limit / filters.length),
|
|
1375
|
+
botId,
|
|
1376
|
+
...runtimeContext ?? {}
|
|
1377
|
+
})
|
|
1378
|
+
)
|
|
1379
|
+
)).flatMap((items) => items ?? []).filter(isRawMemorySemanticResult).map(toMemoryResult);
|
|
1380
|
+
return { semantic: [], lexical: [], hybrid: filterByDateRange(hybrid, input.dateFrom, input.dateTo) };
|
|
1381
|
+
}
|
|
1251
1382
|
if (reasoningStrategy === "rewrite" && deps.reasoning?.queryRewriter) {
|
|
1252
1383
|
try {
|
|
1253
1384
|
const variants = await deps.reasoning.queryRewriter.rewrite({
|
|
@@ -1266,7 +1397,17 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1266
1397
|
);
|
|
1267
1398
|
const lists = await Promise.all(
|
|
1268
1399
|
embeddings.map(
|
|
1269
|
-
(embedding) => runSemanticSearchForEmbedding(
|
|
1400
|
+
(embedding) => runSemanticSearchForEmbedding(
|
|
1401
|
+
deps,
|
|
1402
|
+
input,
|
|
1403
|
+
embedding,
|
|
1404
|
+
limit,
|
|
1405
|
+
threshold,
|
|
1406
|
+
logger,
|
|
1407
|
+
peerPeers,
|
|
1408
|
+
warnings,
|
|
1409
|
+
runtimeContext
|
|
1410
|
+
)
|
|
1270
1411
|
)
|
|
1271
1412
|
);
|
|
1272
1413
|
semantic = mergeByMaxScore(lists);
|
|
@@ -1289,7 +1430,9 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1289
1430
|
limit,
|
|
1290
1431
|
threshold,
|
|
1291
1432
|
logger,
|
|
1292
|
-
peerPeers
|
|
1433
|
+
peerPeers,
|
|
1434
|
+
warnings,
|
|
1435
|
+
runtimeContext
|
|
1293
1436
|
);
|
|
1294
1437
|
}
|
|
1295
1438
|
let lexical = [];
|
|
@@ -1308,7 +1451,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1308
1451
|
limit,
|
|
1309
1452
|
botId,
|
|
1310
1453
|
...peerPeers.length > 0 ? { peers: peerPeers } : {},
|
|
1311
|
-
...factTypes ? { factTypes } : {}
|
|
1454
|
+
...factTypes ? { factTypes } : {},
|
|
1455
|
+
...runtimeContext ?? {}
|
|
1312
1456
|
})
|
|
1313
1457
|
)
|
|
1314
1458
|
)).flat().filter(isRawMemorySemanticResult).map((hit) => ({
|
|
@@ -1334,7 +1478,7 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1334
1478
|
let entity;
|
|
1335
1479
|
if (typeof deps.entitySearch === "function") {
|
|
1336
1480
|
entity = filterByDateRange(
|
|
1337
|
-
await runEntitySearchForQuery(deps, input, limit, logger, peerPeers),
|
|
1481
|
+
await runEntitySearchForQuery(deps, input, limit, logger, peerPeers, runtimeContext),
|
|
1338
1482
|
input.dateFrom,
|
|
1339
1483
|
input.dateTo
|
|
1340
1484
|
);
|
|
@@ -1396,14 +1540,16 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1396
1540
|
}
|
|
1397
1541
|
return out;
|
|
1398
1542
|
}
|
|
1399
|
-
async function
|
|
1543
|
+
async function searchUnifiedMemoryWithRuntime(input, runtimeContext) {
|
|
1400
1544
|
const query = input.query.trim();
|
|
1401
1545
|
const sources = normalizeUnifiedMemorySearchSources(input.sources);
|
|
1402
1546
|
const limit = clampUnifiedMemorySearchLimit(input.limit);
|
|
1403
|
-
const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
|
|
1404
1547
|
const mergeStrategy = normalizeUnifiedMemoryMergeStrategy(
|
|
1405
1548
|
input.mergeStrategy ?? deps.reasoning?.defaultMergeStrategy ?? "rrf"
|
|
1406
1549
|
);
|
|
1550
|
+
const candidateLimit = Math.min(50, Math.max(limit, limit * 4));
|
|
1551
|
+
const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
|
|
1552
|
+
const memoryThreshold = input.threshold === void 0 && mergeStrategy === "rrf" ? Number.NEGATIVE_INFINITY : threshold;
|
|
1407
1553
|
const reasoningStrategy = resolveReasoningStrategy(input);
|
|
1408
1554
|
const warnings = [];
|
|
1409
1555
|
const peerScope = input.peerFilter && input.peerFilter.length > 0 ? await resolveScopePeer({
|
|
@@ -1423,23 +1569,46 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1423
1569
|
};
|
|
1424
1570
|
}
|
|
1425
1571
|
let memorySubs;
|
|
1572
|
+
let retrievalStatus;
|
|
1426
1573
|
const reasoningInfo = { strategy: reasoningStrategy };
|
|
1427
1574
|
if (input.dateFrom || input.dateTo) {
|
|
1428
1575
|
reasoningInfo.dateRange = { from: input.dateFrom, to: input.dateTo };
|
|
1429
1576
|
}
|
|
1430
1577
|
if (sources.includes("memory")) {
|
|
1431
|
-
const hasRawProviders = typeof deps.searchRawMessagesAnn === "function" || typeof deps.searchRawMessagesLexical === "function";
|
|
1578
|
+
const hasRawProviders = typeof deps.searchRawMessagesAnn === "function" || typeof deps.searchRawMessagesHybrid === "function" || typeof deps.searchRawMessagesLexical === "function";
|
|
1432
1579
|
if (isRawMessageStorageAvailable() || hasRawProviders) {
|
|
1433
1580
|
try {
|
|
1434
1581
|
memorySubs = await runMemorySource(
|
|
1435
1582
|
input,
|
|
1436
|
-
|
|
1437
|
-
|
|
1583
|
+
candidateLimit,
|
|
1584
|
+
memoryThreshold,
|
|
1438
1585
|
warnings,
|
|
1439
1586
|
reasoningStrategy,
|
|
1440
1587
|
reasoningInfo,
|
|
1441
|
-
peerPeers
|
|
1588
|
+
peerPeers,
|
|
1589
|
+
runtimeContext
|
|
1442
1590
|
);
|
|
1591
|
+
memorySubs = {
|
|
1592
|
+
semantic: dedupeChannelByParent(memorySubs.semantic),
|
|
1593
|
+
lexical: dedupeChannelByParent(memorySubs.lexical),
|
|
1594
|
+
...memorySubs.hybrid ? { hybrid: dedupeChannelByParent(memorySubs.hybrid) } : {},
|
|
1595
|
+
...memorySubs.entity ? { entity: dedupeChannelByParent(memorySubs.entity) } : {}
|
|
1596
|
+
};
|
|
1597
|
+
if (deps.getRawMessageRetrievalStatus) {
|
|
1598
|
+
retrievalStatus = await deps.getRawMessageRetrievalStatus();
|
|
1599
|
+
}
|
|
1600
|
+
if (memorySubs.semantic.length === 0 && (memorySubs.hybrid?.length ?? 0) === 0 && typeof deps.embedQuery === "function") {
|
|
1601
|
+
let code = "semantic_candidates_empty";
|
|
1602
|
+
let message = "Semantic search was available but returned no candidates.";
|
|
1603
|
+
if (retrievalStatus?.embeddedChildCount === 0) {
|
|
1604
|
+
code = "semantic_unavailable";
|
|
1605
|
+
message = "No embedded RawMessage children are available for semantic search.";
|
|
1606
|
+
} else if (retrievalStatus?.embeddingDimensions && retrievalStatus.indexedDimensions.length > 0 && !retrievalStatus.indexedDimensions.includes(retrievalStatus.embeddingDimensions)) {
|
|
1607
|
+
code = "semantic_dimension_mismatch";
|
|
1608
|
+
message = `Query embedding dimension ${retrievalStatus.embeddingDimensions} does not match indexed child dimensions ${retrievalStatus.indexedDimensions.join(", ")}.`;
|
|
1609
|
+
}
|
|
1610
|
+
warnings.push({ source: "memory", code, message });
|
|
1611
|
+
}
|
|
1443
1612
|
} catch (error) {
|
|
1444
1613
|
logger.warn?.("[memory-store] memory source failed:", error);
|
|
1445
1614
|
warnings.push({
|
|
@@ -1462,12 +1631,13 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1462
1631
|
const hits = await deps.searchInsights({
|
|
1463
1632
|
userId: input.userId,
|
|
1464
1633
|
query: input.query,
|
|
1465
|
-
limit,
|
|
1634
|
+
limit: candidateLimit,
|
|
1466
1635
|
threshold,
|
|
1467
1636
|
botIds: input.botIds,
|
|
1468
1637
|
includeArchived: input.includeArchivedInsights,
|
|
1469
1638
|
authToken: input.authToken,
|
|
1470
|
-
...peerPeers.length > 0 ? { peers: peerPeers } : {}
|
|
1639
|
+
...peerPeers.length > 0 ? { peers: peerPeers } : {},
|
|
1640
|
+
...runtimeContext ?? {}
|
|
1471
1641
|
});
|
|
1472
1642
|
for (const hit of hits) {
|
|
1473
1643
|
insightHits.push({
|
|
@@ -1494,12 +1664,13 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1494
1664
|
userId: input.userId,
|
|
1495
1665
|
query: input.query,
|
|
1496
1666
|
options: {
|
|
1497
|
-
limit,
|
|
1667
|
+
limit: candidateLimit,
|
|
1498
1668
|
threshold,
|
|
1499
1669
|
documentIds: input.documentIds
|
|
1500
1670
|
},
|
|
1501
1671
|
authToken: input.authToken,
|
|
1502
|
-
...peerPeers.length > 0 ? { peers: peerPeers } : {}
|
|
1672
|
+
...peerPeers.length > 0 ? { peers: peerPeers } : {},
|
|
1673
|
+
...runtimeContext ?? {}
|
|
1503
1674
|
});
|
|
1504
1675
|
for (const hit of hits) {
|
|
1505
1676
|
knowledgeHits.push(toKnowledgeResult(hit));
|
|
@@ -1517,10 +1688,14 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1517
1688
|
memorySubs,
|
|
1518
1689
|
insightHits,
|
|
1519
1690
|
knowledgeHits,
|
|
1520
|
-
limit,
|
|
1691
|
+
limit: candidateLimit,
|
|
1521
1692
|
strategy: mergeStrategy
|
|
1522
1693
|
});
|
|
1523
|
-
const
|
|
1694
|
+
const rerankerStartedAt = deps.reranker ? Date.now() : void 0;
|
|
1695
|
+
const reranked = await applyReranker(deps.reranker, input.query, merged);
|
|
1696
|
+
const rerankerLatencyMs = rerankerStartedAt === void 0 ? 0 : Date.now() - rerankerStartedAt;
|
|
1697
|
+
const rerankerOrderChanged = Boolean(deps.reranker) && merged.some((hit, index) => hit.id !== reranked[index]?.id);
|
|
1698
|
+
const ranked = reranked.slice(0, limit);
|
|
1524
1699
|
const output = {
|
|
1525
1700
|
query: input.query,
|
|
1526
1701
|
sources,
|
|
@@ -1528,28 +1703,51 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1528
1703
|
count: ranked.length,
|
|
1529
1704
|
warnings
|
|
1530
1705
|
};
|
|
1706
|
+
if (input.includeRetrievalDiagnostics) {
|
|
1707
|
+
output.retrievalDiagnostics = {
|
|
1708
|
+
mergeStrategy,
|
|
1709
|
+
candidateLimit,
|
|
1710
|
+
backend: retrievalStatus?.backend,
|
|
1711
|
+
semanticDegradedReason: retrievalStatus?.semanticDegradedReason ?? warnings.find((warning) => warning.code.startsWith("semantic_"))?.code,
|
|
1712
|
+
candidateCounts: {
|
|
1713
|
+
semantic: memorySubs?.semantic.length ?? 0,
|
|
1714
|
+
lexical: memorySubs?.lexical.length ?? 0,
|
|
1715
|
+
hybrid: memorySubs?.hybrid?.length ?? 0,
|
|
1716
|
+
entity: memorySubs?.entity?.length ?? 0,
|
|
1717
|
+
fused: merged.length,
|
|
1718
|
+
final: ranked.length
|
|
1719
|
+
},
|
|
1720
|
+
channels: {
|
|
1721
|
+
semantic: memorySubs?.semantic ?? [],
|
|
1722
|
+
lexical: memorySubs?.lexical ?? [],
|
|
1723
|
+
...memorySubs?.hybrid ? { hybrid: memorySubs.hybrid } : {},
|
|
1724
|
+
...memorySubs?.entity ? { entity: memorySubs.entity } : {}
|
|
1725
|
+
},
|
|
1726
|
+
fusedBeforeRerank: merged,
|
|
1727
|
+
reranker: {
|
|
1728
|
+
enabled: Boolean(deps.reranker),
|
|
1729
|
+
provider: deps.rerankerInfo?.provider,
|
|
1730
|
+
model: deps.rerankerInfo?.model,
|
|
1731
|
+
inputCount: merged.length,
|
|
1732
|
+
outputCount: reranked.length,
|
|
1733
|
+
latencyMs: rerankerLatencyMs,
|
|
1734
|
+
orderChanged: rerankerOrderChanged
|
|
1735
|
+
},
|
|
1736
|
+
final: ranked
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1531
1739
|
if (reasoningInfo && (reasoningInfo.strategy !== "none" || reasoningInfo.dateRange !== void 0)) {
|
|
1532
1740
|
output.reasoning = reasoningInfo;
|
|
1533
1741
|
}
|
|
1534
1742
|
return output;
|
|
1535
1743
|
}
|
|
1744
|
+
async function searchUnifiedMemory(input) {
|
|
1745
|
+
return searchUnifiedMemoryWithRuntime(input);
|
|
1746
|
+
}
|
|
1536
1747
|
async function searchRawMemorySemantically(input) {
|
|
1537
|
-
|
|
1538
|
-
const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
|
|
1539
|
-
const reasoningStrategy = resolveReasoningStrategy(input);
|
|
1540
|
-
const mergeStrategy = normalizeUnifiedMemoryMergeStrategy(
|
|
1541
|
-
input.mergeStrategy ?? deps.reasoning?.defaultMergeStrategy ?? "rrf"
|
|
1542
|
-
);
|
|
1543
|
-
const sub = await runMemorySource(input, limit, threshold, [], reasoningStrategy);
|
|
1544
|
-
return mergeAcrossSources({
|
|
1545
|
-
memorySubs: sub,
|
|
1546
|
-
insightHits: [],
|
|
1547
|
-
knowledgeHits: [],
|
|
1548
|
-
limit,
|
|
1549
|
-
strategy: mergeStrategy
|
|
1550
|
-
});
|
|
1748
|
+
return (await searchUnifiedMemory({ ...input, sources: ["memory"] })).results;
|
|
1551
1749
|
}
|
|
1552
|
-
async function
|
|
1750
|
+
async function searchWithResolvedRuntime(input, runtimeContext) {
|
|
1553
1751
|
const query = input.query.trim();
|
|
1554
1752
|
const wantsSynthesis = typeof input.synthesize === "boolean" ? input.synthesize : typeof input.synthesize === "object" && input.synthesize !== null;
|
|
1555
1753
|
const synthesisSchema = typeof input.synthesize === "object" && input.synthesize !== null ? input.synthesize.responseSchema ?? input.responseSchema : input.responseSchema;
|
|
@@ -1581,19 +1779,24 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1581
1779
|
let unifiedHits = [];
|
|
1582
1780
|
let sources = input.sources ? [...input.sources] : [];
|
|
1583
1781
|
let unifiedReasoning;
|
|
1782
|
+
let retrievalDiagnostics;
|
|
1584
1783
|
if (wantsRawTier || wantsInsightTier || wantsKnowledgeTier) {
|
|
1585
1784
|
const inferredSources = sources.length > 0 ? sources : [
|
|
1586
1785
|
...wantsRawTier ? ["memory"] : [],
|
|
1587
1786
|
...wantsInsightTier ? ["insights"] : [],
|
|
1588
1787
|
...wantsKnowledgeTier ? ["knowledge"] : []
|
|
1589
1788
|
];
|
|
1590
|
-
const unified = await
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1789
|
+
const unified = await searchUnifiedMemoryWithRuntime(
|
|
1790
|
+
{
|
|
1791
|
+
...searchInputToUnified(input),
|
|
1792
|
+
sources: inferredSources.length > 0 ? inferredSources : void 0
|
|
1793
|
+
},
|
|
1794
|
+
runtimeContext
|
|
1795
|
+
);
|
|
1594
1796
|
unifiedHits = unified.results;
|
|
1595
1797
|
sources = unified.sources;
|
|
1596
1798
|
unifiedReasoning = unified.reasoning;
|
|
1799
|
+
retrievalDiagnostics = unified.retrievalDiagnostics;
|
|
1597
1800
|
warnings.push(...unified.warnings);
|
|
1598
1801
|
}
|
|
1599
1802
|
let summaryHits = [];
|
|
@@ -1604,7 +1807,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1604
1807
|
limit,
|
|
1605
1808
|
threshold,
|
|
1606
1809
|
logger,
|
|
1607
|
-
peerScope.peers
|
|
1810
|
+
peerScope.peers,
|
|
1811
|
+
runtimeContext
|
|
1608
1812
|
);
|
|
1609
1813
|
warnings.push(...summaryBucket.warnings);
|
|
1610
1814
|
summaryHits = summaryBucket.hits;
|
|
@@ -1629,7 +1833,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1629
1833
|
source: mapHitToEvidenceSource(hit),
|
|
1630
1834
|
snippet: hit.content,
|
|
1631
1835
|
score: hit.similarity,
|
|
1632
|
-
timestamp: getCandidateTimestamp(hit.metadata)
|
|
1836
|
+
timestamp: getCandidateTimestamp(hit.metadata),
|
|
1837
|
+
metadata: hit.metadata
|
|
1633
1838
|
}));
|
|
1634
1839
|
const base = {
|
|
1635
1840
|
query,
|
|
@@ -1642,12 +1847,15 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1642
1847
|
if (unifiedReasoning) {
|
|
1643
1848
|
base.reasoning = unifiedReasoning;
|
|
1644
1849
|
}
|
|
1850
|
+
if (retrievalDiagnostics) {
|
|
1851
|
+
base.retrievalDiagnostics = retrievalDiagnostics;
|
|
1852
|
+
}
|
|
1645
1853
|
if (!wantsSynthesis) {
|
|
1646
1854
|
return base;
|
|
1647
1855
|
}
|
|
1648
1856
|
const { answer, warnings: synthWarnings } = await synthesizeAnswer({
|
|
1649
1857
|
query,
|
|
1650
|
-
|
|
1858
|
+
evidence,
|
|
1651
1859
|
responseSchema: synthesisSchema,
|
|
1652
1860
|
deps,
|
|
1653
1861
|
logger
|
|
@@ -1656,6 +1864,10 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1656
1864
|
base.warnings = [...base.warnings, ...synthWarnings];
|
|
1657
1865
|
return base;
|
|
1658
1866
|
}
|
|
1867
|
+
async function search(input, runtimeContext) {
|
|
1868
|
+
const resolvedRuntime = resolveSearchRuntimeContext(input, runtimeContext);
|
|
1869
|
+
return searchWithResolvedRuntime(input, resolvedRuntime);
|
|
1870
|
+
}
|
|
1659
1871
|
return {
|
|
1660
1872
|
search,
|
|
1661
1873
|
searchUnifiedMemory,
|
|
@@ -1671,6 +1883,7 @@ function mergeAcrossSources(input) {
|
|
|
1671
1883
|
for (const hit of [
|
|
1672
1884
|
...memorySubs.semantic,
|
|
1673
1885
|
...memorySubs.lexical,
|
|
1886
|
+
...memorySubs.hybrid ?? [],
|
|
1674
1887
|
...input.insightHits,
|
|
1675
1888
|
...input.knowledgeHits
|
|
1676
1889
|
]) {
|
|
@@ -1686,6 +1899,7 @@ function mergeAcrossSources(input) {
|
|
|
1686
1899
|
const all = [
|
|
1687
1900
|
...memorySubs.semantic,
|
|
1688
1901
|
...memorySubs.lexical,
|
|
1902
|
+
...memorySubs.hybrid ?? [],
|
|
1689
1903
|
...memorySubs.entity ?? [],
|
|
1690
1904
|
...input.insightHits,
|
|
1691
1905
|
...input.knowledgeHits
|
|
@@ -1697,6 +1911,9 @@ function buildChannelLists(memorySubs, insightHits, knowledgeHits) {
|
|
|
1697
1911
|
const lists = [];
|
|
1698
1912
|
if (memorySubs.semantic.length > 0) lists.push({ name: "memory-semantic", hits: memorySubs.semantic });
|
|
1699
1913
|
if (memorySubs.lexical.length > 0) lists.push({ name: "memory-bm25", hits: memorySubs.lexical });
|
|
1914
|
+
if (memorySubs.hybrid && memorySubs.hybrid.length > 0) {
|
|
1915
|
+
lists.push({ name: "memory-hybrid", hits: memorySubs.hybrid });
|
|
1916
|
+
}
|
|
1700
1917
|
if (memorySubs.entity && memorySubs.entity.length > 0) {
|
|
1701
1918
|
lists.push({ name: "memory-entity", hits: memorySubs.entity });
|
|
1702
1919
|
}
|