@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
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);
|
|
@@ -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,7 +1249,7 @@ 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 = []) {
|
|
1252
|
+
async function runMemorySource(input, limit, threshold, warnings, reasoningStrategy = "none", reasoningInfo, peerPeers = [], runtimeContext) {
|
|
1160
1253
|
if ((reasoningStrategy === "iterative" || reasoningStrategy === "union") && !deps.reasoning?.iterativePlanner) {
|
|
1161
1254
|
warnings.push({
|
|
1162
1255
|
source: "memory",
|
|
@@ -1189,7 +1282,9 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1189
1282
|
keywords2,
|
|
1190
1283
|
limit,
|
|
1191
1284
|
logger,
|
|
1192
|
-
peerPeers
|
|
1285
|
+
peerPeers,
|
|
1286
|
+
warnings,
|
|
1287
|
+
runtimeContext
|
|
1193
1288
|
);
|
|
1194
1289
|
let semanticHits = [];
|
|
1195
1290
|
if (typeof deps.embedQuery === "function") {
|
|
@@ -1203,7 +1298,9 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1203
1298
|
limit,
|
|
1204
1299
|
threshold,
|
|
1205
1300
|
logger,
|
|
1206
|
-
peerPeers
|
|
1301
|
+
peerPeers,
|
|
1302
|
+
warnings,
|
|
1303
|
+
runtimeContext
|
|
1207
1304
|
);
|
|
1208
1305
|
} catch (error) {
|
|
1209
1306
|
logger.warn?.(
|
|
@@ -1244,6 +1341,11 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1244
1341
|
unionEvidence = evidence;
|
|
1245
1342
|
}
|
|
1246
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
|
+
});
|
|
1247
1349
|
warnings.push({
|
|
1248
1350
|
source: "memory",
|
|
1249
1351
|
code: "memory_lexical_search_fallback",
|
|
@@ -1251,7 +1353,16 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1251
1353
|
});
|
|
1252
1354
|
const keywords2 = deriveLexicalKeywords(input.query);
|
|
1253
1355
|
const lexical2 = filterByDateRange(
|
|
1254
|
-
await runLexicalSearchForKeywords(
|
|
1356
|
+
await runLexicalSearchForKeywords(
|
|
1357
|
+
deps,
|
|
1358
|
+
input,
|
|
1359
|
+
keywords2,
|
|
1360
|
+
limit,
|
|
1361
|
+
logger,
|
|
1362
|
+
peerPeers,
|
|
1363
|
+
warnings,
|
|
1364
|
+
runtimeContext
|
|
1365
|
+
),
|
|
1255
1366
|
input.dateFrom,
|
|
1256
1367
|
input.dateTo
|
|
1257
1368
|
);
|
|
@@ -1259,6 +1370,26 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1259
1370
|
}
|
|
1260
1371
|
let semantic = [];
|
|
1261
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
|
+
}
|
|
1262
1393
|
if (reasoningStrategy === "rewrite" && deps.reasoning?.queryRewriter) {
|
|
1263
1394
|
try {
|
|
1264
1395
|
const variants = await deps.reasoning.queryRewriter.rewrite({
|
|
@@ -1277,7 +1408,17 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1277
1408
|
);
|
|
1278
1409
|
const lists = await Promise.all(
|
|
1279
1410
|
embeddings.map(
|
|
1280
|
-
(embedding) => runSemanticSearchForEmbedding(
|
|
1411
|
+
(embedding) => runSemanticSearchForEmbedding(
|
|
1412
|
+
deps,
|
|
1413
|
+
input,
|
|
1414
|
+
embedding,
|
|
1415
|
+
limit,
|
|
1416
|
+
threshold,
|
|
1417
|
+
logger,
|
|
1418
|
+
peerPeers,
|
|
1419
|
+
warnings,
|
|
1420
|
+
runtimeContext
|
|
1421
|
+
)
|
|
1281
1422
|
)
|
|
1282
1423
|
);
|
|
1283
1424
|
semantic = mergeByMaxScore(lists);
|
|
@@ -1300,7 +1441,9 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1300
1441
|
limit,
|
|
1301
1442
|
threshold,
|
|
1302
1443
|
logger,
|
|
1303
|
-
peerPeers
|
|
1444
|
+
peerPeers,
|
|
1445
|
+
warnings,
|
|
1446
|
+
runtimeContext
|
|
1304
1447
|
);
|
|
1305
1448
|
}
|
|
1306
1449
|
let lexical = [];
|
|
@@ -1319,7 +1462,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1319
1462
|
limit,
|
|
1320
1463
|
botId,
|
|
1321
1464
|
...peerPeers.length > 0 ? { peers: peerPeers } : {},
|
|
1322
|
-
...factTypes ? { factTypes } : {}
|
|
1465
|
+
...factTypes ? { factTypes } : {},
|
|
1466
|
+
...runtimeContext ?? {}
|
|
1323
1467
|
})
|
|
1324
1468
|
)
|
|
1325
1469
|
)).flat().filter(isRawMemorySemanticResult).map((hit) => ({
|
|
@@ -1345,7 +1489,7 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1345
1489
|
let entity;
|
|
1346
1490
|
if (typeof deps.entitySearch === "function") {
|
|
1347
1491
|
entity = filterByDateRange(
|
|
1348
|
-
await runEntitySearchForQuery(deps, input, limit, logger, peerPeers),
|
|
1492
|
+
await runEntitySearchForQuery(deps, input, limit, logger, peerPeers, runtimeContext),
|
|
1349
1493
|
input.dateFrom,
|
|
1350
1494
|
input.dateTo
|
|
1351
1495
|
);
|
|
@@ -1407,14 +1551,16 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1407
1551
|
}
|
|
1408
1552
|
return out;
|
|
1409
1553
|
}
|
|
1410
|
-
async function
|
|
1554
|
+
async function searchUnifiedMemoryWithRuntime(input, runtimeContext) {
|
|
1411
1555
|
const query = input.query.trim();
|
|
1412
1556
|
const sources = normalizeUnifiedMemorySearchSources(input.sources);
|
|
1413
1557
|
const limit = clampUnifiedMemorySearchLimit(input.limit);
|
|
1414
|
-
const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
|
|
1415
1558
|
const mergeStrategy = normalizeUnifiedMemoryMergeStrategy(
|
|
1416
1559
|
input.mergeStrategy ?? deps.reasoning?.defaultMergeStrategy ?? "rrf"
|
|
1417
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;
|
|
1418
1564
|
const reasoningStrategy = resolveReasoningStrategy(input);
|
|
1419
1565
|
const warnings = [];
|
|
1420
1566
|
const peerScope = input.peerFilter && input.peerFilter.length > 0 ? await resolveScopePeer({
|
|
@@ -1434,23 +1580,46 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1434
1580
|
};
|
|
1435
1581
|
}
|
|
1436
1582
|
let memorySubs;
|
|
1583
|
+
let retrievalStatus;
|
|
1437
1584
|
const reasoningInfo = { strategy: reasoningStrategy };
|
|
1438
1585
|
if (input.dateFrom || input.dateTo) {
|
|
1439
1586
|
reasoningInfo.dateRange = { from: input.dateFrom, to: input.dateTo };
|
|
1440
1587
|
}
|
|
1441
1588
|
if (sources.includes("memory")) {
|
|
1442
|
-
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";
|
|
1443
1590
|
if (isRawMessageStorageAvailable() || hasRawProviders) {
|
|
1444
1591
|
try {
|
|
1445
1592
|
memorySubs = await runMemorySource(
|
|
1446
1593
|
input,
|
|
1447
|
-
|
|
1448
|
-
|
|
1594
|
+
candidateLimit,
|
|
1595
|
+
memoryThreshold,
|
|
1449
1596
|
warnings,
|
|
1450
1597
|
reasoningStrategy,
|
|
1451
1598
|
reasoningInfo,
|
|
1452
|
-
peerPeers
|
|
1599
|
+
peerPeers,
|
|
1600
|
+
runtimeContext
|
|
1453
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
|
+
}
|
|
1454
1623
|
} catch (error) {
|
|
1455
1624
|
logger.warn?.("[memory-store] memory source failed:", error);
|
|
1456
1625
|
warnings.push({
|
|
@@ -1473,12 +1642,13 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1473
1642
|
const hits = await deps.searchInsights({
|
|
1474
1643
|
userId: input.userId,
|
|
1475
1644
|
query: input.query,
|
|
1476
|
-
limit,
|
|
1645
|
+
limit: candidateLimit,
|
|
1477
1646
|
threshold,
|
|
1478
1647
|
botIds: input.botIds,
|
|
1479
1648
|
includeArchived: input.includeArchivedInsights,
|
|
1480
1649
|
authToken: input.authToken,
|
|
1481
|
-
...peerPeers.length > 0 ? { peers: peerPeers } : {}
|
|
1650
|
+
...peerPeers.length > 0 ? { peers: peerPeers } : {},
|
|
1651
|
+
...runtimeContext ?? {}
|
|
1482
1652
|
});
|
|
1483
1653
|
for (const hit of hits) {
|
|
1484
1654
|
insightHits.push({
|
|
@@ -1505,12 +1675,13 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1505
1675
|
userId: input.userId,
|
|
1506
1676
|
query: input.query,
|
|
1507
1677
|
options: {
|
|
1508
|
-
limit,
|
|
1678
|
+
limit: candidateLimit,
|
|
1509
1679
|
threshold,
|
|
1510
1680
|
documentIds: input.documentIds
|
|
1511
1681
|
},
|
|
1512
1682
|
authToken: input.authToken,
|
|
1513
|
-
...peerPeers.length > 0 ? { peers: peerPeers } : {}
|
|
1683
|
+
...peerPeers.length > 0 ? { peers: peerPeers } : {},
|
|
1684
|
+
...runtimeContext ?? {}
|
|
1514
1685
|
});
|
|
1515
1686
|
for (const hit of hits) {
|
|
1516
1687
|
knowledgeHits.push(toKnowledgeResult(hit));
|
|
@@ -1528,10 +1699,14 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1528
1699
|
memorySubs,
|
|
1529
1700
|
insightHits,
|
|
1530
1701
|
knowledgeHits,
|
|
1531
|
-
limit,
|
|
1702
|
+
limit: candidateLimit,
|
|
1532
1703
|
strategy: mergeStrategy
|
|
1533
1704
|
});
|
|
1534
|
-
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);
|
|
1535
1710
|
const output = {
|
|
1536
1711
|
query: input.query,
|
|
1537
1712
|
sources,
|
|
@@ -1539,28 +1714,51 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1539
1714
|
count: ranked.length,
|
|
1540
1715
|
warnings
|
|
1541
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
|
+
}
|
|
1542
1750
|
if (reasoningInfo && (reasoningInfo.strategy !== "none" || reasoningInfo.dateRange !== void 0)) {
|
|
1543
1751
|
output.reasoning = reasoningInfo;
|
|
1544
1752
|
}
|
|
1545
1753
|
return output;
|
|
1546
1754
|
}
|
|
1755
|
+
async function searchUnifiedMemory(input) {
|
|
1756
|
+
return searchUnifiedMemoryWithRuntime(input);
|
|
1757
|
+
}
|
|
1547
1758
|
async function searchRawMemorySemantically(input) {
|
|
1548
|
-
|
|
1549
|
-
const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
|
|
1550
|
-
const reasoningStrategy = resolveReasoningStrategy(input);
|
|
1551
|
-
const mergeStrategy = normalizeUnifiedMemoryMergeStrategy(
|
|
1552
|
-
input.mergeStrategy ?? deps.reasoning?.defaultMergeStrategy ?? "rrf"
|
|
1553
|
-
);
|
|
1554
|
-
const sub = await runMemorySource(input, limit, threshold, [], reasoningStrategy);
|
|
1555
|
-
return mergeAcrossSources({
|
|
1556
|
-
memorySubs: sub,
|
|
1557
|
-
insightHits: [],
|
|
1558
|
-
knowledgeHits: [],
|
|
1559
|
-
limit,
|
|
1560
|
-
strategy: mergeStrategy
|
|
1561
|
-
});
|
|
1759
|
+
return (await searchUnifiedMemory({ ...input, sources: ["memory"] })).results;
|
|
1562
1760
|
}
|
|
1563
|
-
async function
|
|
1761
|
+
async function searchWithResolvedRuntime(input, runtimeContext) {
|
|
1564
1762
|
const query = input.query.trim();
|
|
1565
1763
|
const wantsSynthesis = typeof input.synthesize === "boolean" ? input.synthesize : typeof input.synthesize === "object" && input.synthesize !== null;
|
|
1566
1764
|
const synthesisSchema = typeof input.synthesize === "object" && input.synthesize !== null ? input.synthesize.responseSchema ?? input.responseSchema : input.responseSchema;
|
|
@@ -1592,19 +1790,24 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1592
1790
|
let unifiedHits = [];
|
|
1593
1791
|
let sources = input.sources ? [...input.sources] : [];
|
|
1594
1792
|
let unifiedReasoning;
|
|
1793
|
+
let retrievalDiagnostics;
|
|
1595
1794
|
if (wantsRawTier || wantsInsightTier || wantsKnowledgeTier) {
|
|
1596
1795
|
const inferredSources = sources.length > 0 ? sources : [
|
|
1597
1796
|
...wantsRawTier ? ["memory"] : [],
|
|
1598
1797
|
...wantsInsightTier ? ["insights"] : [],
|
|
1599
1798
|
...wantsKnowledgeTier ? ["knowledge"] : []
|
|
1600
1799
|
];
|
|
1601
|
-
const unified = await
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1800
|
+
const unified = await searchUnifiedMemoryWithRuntime(
|
|
1801
|
+
{
|
|
1802
|
+
...searchInputToUnified(input),
|
|
1803
|
+
sources: inferredSources.length > 0 ? inferredSources : void 0
|
|
1804
|
+
},
|
|
1805
|
+
runtimeContext
|
|
1806
|
+
);
|
|
1605
1807
|
unifiedHits = unified.results;
|
|
1606
1808
|
sources = unified.sources;
|
|
1607
1809
|
unifiedReasoning = unified.reasoning;
|
|
1810
|
+
retrievalDiagnostics = unified.retrievalDiagnostics;
|
|
1608
1811
|
warnings.push(...unified.warnings);
|
|
1609
1812
|
}
|
|
1610
1813
|
let summaryHits = [];
|
|
@@ -1615,7 +1818,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1615
1818
|
limit,
|
|
1616
1819
|
threshold,
|
|
1617
1820
|
logger,
|
|
1618
|
-
peerScope.peers
|
|
1821
|
+
peerScope.peers,
|
|
1822
|
+
runtimeContext
|
|
1619
1823
|
);
|
|
1620
1824
|
warnings.push(...summaryBucket.warnings);
|
|
1621
1825
|
summaryHits = summaryBucket.hits;
|
|
@@ -1640,7 +1844,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1640
1844
|
source: mapHitToEvidenceSource(hit),
|
|
1641
1845
|
snippet: hit.content,
|
|
1642
1846
|
score: hit.similarity,
|
|
1643
|
-
timestamp: getCandidateTimestamp(hit.metadata)
|
|
1847
|
+
timestamp: getCandidateTimestamp(hit.metadata),
|
|
1848
|
+
metadata: hit.metadata
|
|
1644
1849
|
}));
|
|
1645
1850
|
const base = {
|
|
1646
1851
|
query,
|
|
@@ -1653,12 +1858,15 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1653
1858
|
if (unifiedReasoning) {
|
|
1654
1859
|
base.reasoning = unifiedReasoning;
|
|
1655
1860
|
}
|
|
1861
|
+
if (retrievalDiagnostics) {
|
|
1862
|
+
base.retrievalDiagnostics = retrievalDiagnostics;
|
|
1863
|
+
}
|
|
1656
1864
|
if (!wantsSynthesis) {
|
|
1657
1865
|
return base;
|
|
1658
1866
|
}
|
|
1659
1867
|
const { answer, warnings: synthWarnings } = await synthesizeAnswer({
|
|
1660
1868
|
query,
|
|
1661
|
-
|
|
1869
|
+
evidence,
|
|
1662
1870
|
responseSchema: synthesisSchema,
|
|
1663
1871
|
deps,
|
|
1664
1872
|
logger
|
|
@@ -1667,6 +1875,10 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1667
1875
|
base.warnings = [...base.warnings, ...synthWarnings];
|
|
1668
1876
|
return base;
|
|
1669
1877
|
}
|
|
1878
|
+
async function search(input, runtimeContext) {
|
|
1879
|
+
const resolvedRuntime = resolveSearchRuntimeContext(input, runtimeContext);
|
|
1880
|
+
return searchWithResolvedRuntime(input, resolvedRuntime);
|
|
1881
|
+
}
|
|
1670
1882
|
return {
|
|
1671
1883
|
search,
|
|
1672
1884
|
searchUnifiedMemory,
|
|
@@ -1682,6 +1894,7 @@ function mergeAcrossSources(input) {
|
|
|
1682
1894
|
for (const hit of [
|
|
1683
1895
|
...memorySubs.semantic,
|
|
1684
1896
|
...memorySubs.lexical,
|
|
1897
|
+
...memorySubs.hybrid ?? [],
|
|
1685
1898
|
...input.insightHits,
|
|
1686
1899
|
...input.knowledgeHits
|
|
1687
1900
|
]) {
|
|
@@ -1697,6 +1910,7 @@ function mergeAcrossSources(input) {
|
|
|
1697
1910
|
const all = [
|
|
1698
1911
|
...memorySubs.semantic,
|
|
1699
1912
|
...memorySubs.lexical,
|
|
1913
|
+
...memorySubs.hybrid ?? [],
|
|
1700
1914
|
...memorySubs.entity ?? [],
|
|
1701
1915
|
...input.insightHits,
|
|
1702
1916
|
...input.knowledgeHits
|
|
@@ -1708,6 +1922,9 @@ function buildChannelLists(memorySubs, insightHits, knowledgeHits) {
|
|
|
1708
1922
|
const lists = [];
|
|
1709
1923
|
if (memorySubs.semantic.length > 0) lists.push({ name: "memory-semantic", hits: memorySubs.semantic });
|
|
1710
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
|
+
}
|
|
1711
1928
|
if (memorySubs.entity && memorySubs.entity.length > 0) {
|
|
1712
1929
|
lists.push({ name: "memory-entity", hits: memorySubs.entity });
|
|
1713
1930
|
}
|