@anuma/sdk 7.0.0 → 7.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/expo/index.cjs +421 -111
- package/dist/expo/index.d.mts +25 -24
- package/dist/expo/index.d.ts +25 -24
- package/dist/expo/index.mjs +420 -111
- package/dist/index.cjs +216 -192
- package/dist/index.mjs +216 -192
- package/dist/memory/context.d.mts +8 -0
- package/dist/memory/context.d.ts +8 -0
- package/dist/react/index.cjs +442 -132
- package/dist/react/index.d.mts +30 -44
- package/dist/react/index.d.ts +30 -44
- package/dist/react/index.mjs +441 -132
- package/dist/server/index.cjs +431 -106
- package/dist/server/index.d.mts +30 -21
- package/dist/server/index.d.ts +30 -21
- package/dist/server/index.mjs +430 -106
- package/dist/telemetry/index.d.mts +6 -0
- package/dist/telemetry/index.d.ts +6 -0
- package/dist/tools/selection/index.cjs +28 -10
- package/dist/tools/selection/index.mjs +28 -10
- package/package.json +1 -1
package/dist/expo/index.cjs
CHANGED
|
@@ -1495,6 +1495,7 @@ __export(index_exports, {
|
|
|
1495
1495
|
embedAllMessages: () => embedAllMessages,
|
|
1496
1496
|
embedMessage: () => embedMessage,
|
|
1497
1497
|
encodeChunkVector: () => encodeChunkVector,
|
|
1498
|
+
encryptToolCallEventsOp: () => encryptToolCallEventsOp,
|
|
1498
1499
|
ensureDefaultFoldersOp: () => ensureDefaultFoldersOp,
|
|
1499
1500
|
exchangeNotionCode: () => exchangeNotionCode,
|
|
1500
1501
|
extractAndLinkEntitiesForMemoriesOp: () => extractAndLinkEntitiesForMemoriesOp,
|
|
@@ -2883,6 +2884,35 @@ function decodeChunkVector(vector, onMalformed) {
|
|
|
2883
2884
|
}
|
|
2884
2885
|
}
|
|
2885
2886
|
|
|
2887
|
+
// src/lib/scheduling.ts
|
|
2888
|
+
function nowMs() {
|
|
2889
|
+
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
2890
|
+
}
|
|
2891
|
+
function yieldToEventLoop() {
|
|
2892
|
+
const scheduler = globalThis.scheduler;
|
|
2893
|
+
if (typeof scheduler?.yield === "function") return scheduler.yield();
|
|
2894
|
+
if (typeof MessageChannel === "function") {
|
|
2895
|
+
return new Promise((resolve) => {
|
|
2896
|
+
const channel = new MessageChannel();
|
|
2897
|
+
channel.port1.onmessage = () => {
|
|
2898
|
+
channel.port1.close();
|
|
2899
|
+
channel.port2.close();
|
|
2900
|
+
resolve();
|
|
2901
|
+
};
|
|
2902
|
+
channel.port2.postMessage(null);
|
|
2903
|
+
});
|
|
2904
|
+
}
|
|
2905
|
+
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
2906
|
+
}
|
|
2907
|
+
function createTimeSliceYielder(budgetMs) {
|
|
2908
|
+
let sliceStart = nowMs();
|
|
2909
|
+
return async () => {
|
|
2910
|
+
if (nowMs() - sliceStart < budgetMs) return;
|
|
2911
|
+
await yieldToEventLoop();
|
|
2912
|
+
sliceStart = nowMs();
|
|
2913
|
+
};
|
|
2914
|
+
}
|
|
2915
|
+
|
|
2886
2916
|
// src/lib/db/encryption-utils.ts
|
|
2887
2917
|
init_logger();
|
|
2888
2918
|
var ENCRYPTION_PREFIX = "enc:v3:";
|
|
@@ -2892,6 +2922,11 @@ function isEncrypted(value) {
|
|
|
2892
2922
|
const payload = value.slice(prefix.length);
|
|
2893
2923
|
return payload.length >= 56 && /^[0-9a-f]+$/i.test(payload);
|
|
2894
2924
|
}
|
|
2925
|
+
function hasEncryptedToolPayload(event) {
|
|
2926
|
+
return [event.arguments, event.output].some(
|
|
2927
|
+
(value) => typeof value === "string" && isEncrypted(value)
|
|
2928
|
+
);
|
|
2929
|
+
}
|
|
2895
2930
|
function detectEncryptionVersion(value) {
|
|
2896
2931
|
if (value.startsWith("enc:v3:")) {
|
|
2897
2932
|
return { version: "v3", encryptedData: value.slice("enc:v3:".length) };
|
|
@@ -3029,6 +3064,12 @@ async function encryptMessageFields(message, address, signMessage, embeddedWalle
|
|
|
3029
3064
|
const encryptedChunks = msg.chunks ? await encryptJsonField(msg.chunks, address, signMessage, embeddedWalletSigner, true) : void 0;
|
|
3030
3065
|
const encryptedSources = msg.sources ? await encryptJsonField(msg.sources, address, signMessage, embeddedWalletSigner, true) : void 0;
|
|
3031
3066
|
const encryptedThoughtProcess = msg.thoughtProcess ? await encryptJsonField(msg.thoughtProcess, address, signMessage, embeddedWalletSigner, true) : void 0;
|
|
3067
|
+
const encryptedToolCallEvents = Array.isArray(msg.toolCallEvents) ? await encryptToolCallEvents(
|
|
3068
|
+
msg.toolCallEvents,
|
|
3069
|
+
address,
|
|
3070
|
+
signMessage,
|
|
3071
|
+
embeddedWalletSigner
|
|
3072
|
+
) : void 0;
|
|
3032
3073
|
return {
|
|
3033
3074
|
...message,
|
|
3034
3075
|
...encryptedContent !== void 0 && { content: encryptedContent },
|
|
@@ -3036,13 +3077,31 @@ async function encryptMessageFields(message, address, signMessage, embeddedWalle
|
|
|
3036
3077
|
...encryptedVector !== void 0 && { vector: encryptedVector },
|
|
3037
3078
|
...encryptedChunks !== void 0 && { chunks: encryptedChunks },
|
|
3038
3079
|
...encryptedSources !== void 0 && { sources: encryptedSources },
|
|
3039
|
-
...encryptedThoughtProcess !== void 0 && { thoughtProcess: encryptedThoughtProcess }
|
|
3080
|
+
...encryptedThoughtProcess !== void 0 && { thoughtProcess: encryptedThoughtProcess },
|
|
3081
|
+
...encryptedToolCallEvents !== void 0 && { toolCallEvents: encryptedToolCallEvents }
|
|
3040
3082
|
};
|
|
3041
3083
|
} catch (error) {
|
|
3042
3084
|
getLogger().warn("Failed to encrypt message fields:", error);
|
|
3043
3085
|
throw error;
|
|
3044
3086
|
}
|
|
3045
3087
|
}
|
|
3088
|
+
function mapToolCallPayloads(events, transform) {
|
|
3089
|
+
return Promise.all(
|
|
3090
|
+
events.map(async (event) => {
|
|
3091
|
+
if (!event || typeof event !== "object") return event;
|
|
3092
|
+
const mapped = { ...event };
|
|
3093
|
+
if (typeof event.arguments === "string") mapped.arguments = await transform(event.arguments);
|
|
3094
|
+
if (typeof event.output === "string") mapped.output = await transform(event.output);
|
|
3095
|
+
return mapped;
|
|
3096
|
+
})
|
|
3097
|
+
);
|
|
3098
|
+
}
|
|
3099
|
+
function encryptToolCallEvents(events, address, signMessage, embeddedWalletSigner) {
|
|
3100
|
+
return mapToolCallPayloads(
|
|
3101
|
+
events,
|
|
3102
|
+
(value) => encryptField(value, address, signMessage, embeddedWalletSigner, true)
|
|
3103
|
+
);
|
|
3104
|
+
}
|
|
3046
3105
|
async function decryptMaybeJsonFieldDetailed(value, address) {
|
|
3047
3106
|
if (value === void 0) return { value: void 0, status: "plaintext" };
|
|
3048
3107
|
if (value === null) return { value, status: "plaintext" };
|
|
@@ -3128,6 +3187,10 @@ async function assembleDecryptedMessage(message, address) {
|
|
|
3128
3187
|
message.thoughtProcess,
|
|
3129
3188
|
address
|
|
3130
3189
|
);
|
|
3190
|
+
const toolCallEventsResult = await decryptMaybeJsonFieldDetailed(
|
|
3191
|
+
message.toolCallEvents,
|
|
3192
|
+
address
|
|
3193
|
+
);
|
|
3131
3194
|
let decryptionStatus;
|
|
3132
3195
|
let refreshProbe;
|
|
3133
3196
|
const consider = (status, probe) => {
|
|
@@ -3146,6 +3209,14 @@ async function assembleDecryptedMessage(message, address) {
|
|
|
3146
3209
|
if (thoughtProcessResult.probe) {
|
|
3147
3210
|
consider(thoughtProcessResult.status, thoughtProcessResult.probe);
|
|
3148
3211
|
}
|
|
3212
|
+
if (toolCallEventsResult.probe) {
|
|
3213
|
+
consider(toolCallEventsResult.status, toolCallEventsResult.probe);
|
|
3214
|
+
}
|
|
3215
|
+
const toolCallEvents = Array.isArray(toolCallEventsResult.value) ? await mapToolCallPayloads(toolCallEventsResult.value, async (value) => {
|
|
3216
|
+
const detailed = await decryptFieldDetailed(value, address);
|
|
3217
|
+
consider(detailed.status, isEncrypted(value) ? value : void 0);
|
|
3218
|
+
return detailed.value;
|
|
3219
|
+
}) : void 0;
|
|
3149
3220
|
const statusForConsumer = decryptionStatus === "ok" || decryptionStatus === "plaintext" || !decryptionStatus ? void 0 : decryptionStatus;
|
|
3150
3221
|
const result = {
|
|
3151
3222
|
...message,
|
|
@@ -3154,7 +3225,8 @@ async function assembleDecryptedMessage(message, address) {
|
|
|
3154
3225
|
vector: vectorResult.value,
|
|
3155
3226
|
chunks: chunksResult.value,
|
|
3156
3227
|
sources: sourcesResult.value,
|
|
3157
|
-
thoughtProcess: thoughtProcessResult.value
|
|
3228
|
+
thoughtProcess: thoughtProcessResult.value,
|
|
3229
|
+
toolCallEvents
|
|
3158
3230
|
};
|
|
3159
3231
|
if (statusForConsumer) {
|
|
3160
3232
|
result.decryptionStatus = statusForConsumer;
|
|
@@ -3950,7 +4022,7 @@ async function updateMessageChunksOp(ctx, uniqueId, chunks3, embeddingModel) {
|
|
|
3950
4022
|
});
|
|
3951
4023
|
return messageToStored(message, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
|
|
3952
4024
|
}
|
|
3953
|
-
var
|
|
4025
|
+
var RAW_REPAIR_PAGE_SIZE = 100;
|
|
3954
4026
|
function chunksWithoutText(raw) {
|
|
3955
4027
|
if (typeof raw !== "string") return null;
|
|
3956
4028
|
let parsed;
|
|
@@ -3973,8 +4045,8 @@ function chunksWithoutText(raw) {
|
|
|
3973
4045
|
async function stripLegacyChunkTextOp(ctx) {
|
|
3974
4046
|
const candidateIds = await ctx.messagesCollection.query(import_watermelondb2.Q.where("chunks", import_watermelondb2.Q.like(`%${import_watermelondb2.Q.sanitizeLikeString('"text":')}%`))).fetchIds();
|
|
3975
4047
|
let stripped = 0;
|
|
3976
|
-
for (let start = 0; start < candidateIds.length; start +=
|
|
3977
|
-
const pageIds = candidateIds.slice(start, start +
|
|
4048
|
+
for (let start = 0; start < candidateIds.length; start += RAW_REPAIR_PAGE_SIZE) {
|
|
4049
|
+
const pageIds = candidateIds.slice(start, start + RAW_REPAIR_PAGE_SIZE);
|
|
3978
4050
|
const rows = await ctx.messagesCollection.query(import_watermelondb2.Q.where("id", import_watermelondb2.Q.oneOf(pageIds))).unsafeFetchRaw();
|
|
3979
4051
|
const changedIds = rows.filter((row) => chunksWithoutText(row.chunks) !== null).map((row) => String(row.id));
|
|
3980
4052
|
if (changedIds.length === 0) continue;
|
|
@@ -3998,6 +4070,88 @@ async function stripLegacyChunkTextOp(ctx) {
|
|
|
3998
4070
|
}
|
|
3999
4071
|
return stripped;
|
|
4000
4072
|
}
|
|
4073
|
+
async function encryptedToolCallEventsColumn(raw, address, signMessage, embeddedWalletSigner) {
|
|
4074
|
+
if (typeof raw !== "string") return null;
|
|
4075
|
+
let parsed;
|
|
4076
|
+
try {
|
|
4077
|
+
parsed = JSON.parse(raw);
|
|
4078
|
+
} catch {
|
|
4079
|
+
return null;
|
|
4080
|
+
}
|
|
4081
|
+
if (!Array.isArray(parsed)) return null;
|
|
4082
|
+
const column = JSON.stringify(
|
|
4083
|
+
await encryptToolCallEvents(
|
|
4084
|
+
parsed,
|
|
4085
|
+
address,
|
|
4086
|
+
signMessage,
|
|
4087
|
+
embeddedWalletSigner
|
|
4088
|
+
)
|
|
4089
|
+
);
|
|
4090
|
+
return column === JSON.stringify(parsed) ? null : column;
|
|
4091
|
+
}
|
|
4092
|
+
async function encryptToolCallEventsOp(ctx) {
|
|
4093
|
+
const { walletAddress: address, signMessage, embeddedWalletSigner } = ctx;
|
|
4094
|
+
if (!address || !signMessage) return 0;
|
|
4095
|
+
if (!await requestEncryptionKey(address, signMessage, embeddedWalletSigner)) return 0;
|
|
4096
|
+
if (!hasEncryptionKey(address)) return 0;
|
|
4097
|
+
if (!await savedKeyOpensExistingData(ctx, address, signMessage, embeddedWalletSigner)) {
|
|
4098
|
+
return 0;
|
|
4099
|
+
}
|
|
4100
|
+
const candidateIds = await ctx.messagesCollection.query(
|
|
4101
|
+
import_watermelondb2.Q.where("tool_call_events", import_watermelondb2.Q.notEq(null)),
|
|
4102
|
+
import_watermelondb2.Q.where("tool_call_events", import_watermelondb2.Q.notIn(["", "[]"]))
|
|
4103
|
+
).fetchIds();
|
|
4104
|
+
let encrypted = 0;
|
|
4105
|
+
for (let start = 0; start < candidateIds.length; start += RAW_REPAIR_PAGE_SIZE) {
|
|
4106
|
+
const pageIds = candidateIds.slice(start, start + RAW_REPAIR_PAGE_SIZE);
|
|
4107
|
+
const rows = await ctx.messagesCollection.query(import_watermelondb2.Q.where("id", import_watermelondb2.Q.oneOf(pageIds))).unsafeFetchRaw();
|
|
4108
|
+
const repairs = /* @__PURE__ */ new Map();
|
|
4109
|
+
for (const row of rows) {
|
|
4110
|
+
const column = await encryptedToolCallEventsColumn(
|
|
4111
|
+
row.tool_call_events,
|
|
4112
|
+
address,
|
|
4113
|
+
signMessage,
|
|
4114
|
+
embeddedWalletSigner
|
|
4115
|
+
);
|
|
4116
|
+
if (column !== null) repairs.set(String(row.id), { raw: row.tool_call_events, column });
|
|
4117
|
+
}
|
|
4118
|
+
if (repairs.size === 0) continue;
|
|
4119
|
+
await ctx.database.write(async () => {
|
|
4120
|
+
const messages = (await ctx.messagesCollection.query(import_watermelondb2.Q.where("id", import_watermelondb2.Q.oneOf([...repairs.keys()]))).fetch()).filter(
|
|
4121
|
+
(message) => message._raw.tool_call_events === repairs.get(message.id)?.raw
|
|
4122
|
+
);
|
|
4123
|
+
if (messages.length === 0) return;
|
|
4124
|
+
const conversationIds = [...new Set(messages.map((message) => message.conversationId))];
|
|
4125
|
+
const conversations = await ctx.conversationsCollection.query(import_watermelondb2.Q.where("conversation_id", import_watermelondb2.Q.oneOf(conversationIds))).fetch();
|
|
4126
|
+
await ctx.database.batch(
|
|
4127
|
+
...messages.map(
|
|
4128
|
+
(message) => message.prepareUpdate((msg) => {
|
|
4129
|
+
msg._setRaw("tool_call_events", repairs.get(message.id).column);
|
|
4130
|
+
})
|
|
4131
|
+
),
|
|
4132
|
+
...conversations.map((conversation) => conversation.prepareUpdate(() => {
|
|
4133
|
+
}))
|
|
4134
|
+
);
|
|
4135
|
+
encrypted += messages.length;
|
|
4136
|
+
});
|
|
4137
|
+
}
|
|
4138
|
+
return encrypted;
|
|
4139
|
+
}
|
|
4140
|
+
async function savedKeyOpensExistingData(ctx, address, signMessage, embeddedWalletSigner) {
|
|
4141
|
+
const v3Probe = await findEncryptedContent(ctx, "enc:v3:%");
|
|
4142
|
+
if (v3Probe) {
|
|
4143
|
+
const { status } = await decryptFieldDetailed(v3Probe, address);
|
|
4144
|
+
if (status !== "auth_mismatch" && status !== "key_missing") return true;
|
|
4145
|
+
return refreshEncryptionKeyIfMatches(address, v3Probe, signMessage, embeddedWalletSigner);
|
|
4146
|
+
}
|
|
4147
|
+
const v2Probe = await findEncryptedContent(ctx, "enc:v2:%");
|
|
4148
|
+
if (!v2Probe) return true;
|
|
4149
|
+
return refreshEncryptionKeyIfMatches(address, v2Probe, signMessage, embeddedWalletSigner);
|
|
4150
|
+
}
|
|
4151
|
+
async function findEncryptedContent(ctx, pattern) {
|
|
4152
|
+
const [row] = await ctx.messagesCollection.query(import_watermelondb2.Q.where("content", import_watermelondb2.Q.like(pattern)), import_watermelondb2.Q.take(1)).unsafeFetchRaw();
|
|
4153
|
+
return row ? String(row.content) : null;
|
|
4154
|
+
}
|
|
4001
4155
|
async function updateMessageErrorOp(ctx, uniqueId, error) {
|
|
4002
4156
|
let message;
|
|
4003
4157
|
try {
|
|
@@ -4095,6 +4249,95 @@ async function readRawJsonField(raw, column, walletAddress) {
|
|
|
4095
4249
|
return void 0;
|
|
4096
4250
|
}
|
|
4097
4251
|
}
|
|
4252
|
+
var CHUNK_VECTOR_BATCH_SIZE = 50;
|
|
4253
|
+
var CHUNK_SCAN_YIELD_BUDGET_MS = 12;
|
|
4254
|
+
var hasEmbeddingSql = `(coalesce("chunks", '') != '' or coalesce("vector", '') != '')`;
|
|
4255
|
+
function toChunkCandidateKey(raw) {
|
|
4256
|
+
return {
|
|
4257
|
+
id: raw.id,
|
|
4258
|
+
conversationId: String(raw.conversation_id),
|
|
4259
|
+
version: Number(raw.updated_at) || 0,
|
|
4260
|
+
embeddingModel: raw.embedding_model ?? null
|
|
4261
|
+
};
|
|
4262
|
+
}
|
|
4263
|
+
async function fetchRowsByIds(ctx, ids) {
|
|
4264
|
+
if (ids.length === 0) return [];
|
|
4265
|
+
return await ctx.messagesCollection.query(import_watermelondb2.Q.where("id", import_watermelondb2.Q.oneOf(ids))).unsafeFetchRaw();
|
|
4266
|
+
}
|
|
4267
|
+
async function projectedChunkRowSource(ctx, filter) {
|
|
4268
|
+
const clauses = [
|
|
4269
|
+
`"history"."_status" is not 'deleted'`,
|
|
4270
|
+
hasEmbeddingSql,
|
|
4271
|
+
`exists (select 1 from "conversations" where "conversations"."conversation_id" = "history"."conversation_id" and "conversations"."is_deleted" = 0 and "conversations"."_status" is not 'deleted')`
|
|
4272
|
+
];
|
|
4273
|
+
const args = [];
|
|
4274
|
+
if (filter.conversationId !== void 0) {
|
|
4275
|
+
clauses.push(`"history"."conversation_id" = ?`);
|
|
4276
|
+
args.push(filter.conversationId);
|
|
4277
|
+
}
|
|
4278
|
+
if (filter.excludeConversationId !== void 0) {
|
|
4279
|
+
clauses.push(`"history"."conversation_id" != ?`);
|
|
4280
|
+
args.push(filter.excludeConversationId);
|
|
4281
|
+
}
|
|
4282
|
+
const keyRows = await ctx.messagesCollection.query(
|
|
4283
|
+
import_watermelondb2.Q.unsafeSqlQuery(
|
|
4284
|
+
`select "id", "conversation_id", "updated_at", "embedding_model" from "history" where ${clauses.join(" and ")}`,
|
|
4285
|
+
args
|
|
4286
|
+
)
|
|
4287
|
+
).unsafeFetchRaw();
|
|
4288
|
+
return {
|
|
4289
|
+
keys: keyRows.map(toChunkCandidateKey),
|
|
4290
|
+
loadVectorRows: async (ids) => {
|
|
4291
|
+
if (ids.length === 0) return [];
|
|
4292
|
+
return await ctx.messagesCollection.query(
|
|
4293
|
+
import_watermelondb2.Q.unsafeSqlQuery(
|
|
4294
|
+
`select "id", "updated_at", "chunks", "vector" from "history" where "_status" is not 'deleted' and "id" in (${ids.map(() => "?").join(",")})`,
|
|
4295
|
+
ids
|
|
4296
|
+
)
|
|
4297
|
+
).unsafeFetchRaw();
|
|
4298
|
+
},
|
|
4299
|
+
loadFullRows: (ids) => fetchRowsByIds(ctx, ids)
|
|
4300
|
+
};
|
|
4301
|
+
}
|
|
4302
|
+
async function fullScanChunkRowSource(ctx, filter) {
|
|
4303
|
+
const activeConversations = await ctx.conversationsCollection.query(import_watermelondb2.Q.where("is_deleted", false)).fetch();
|
|
4304
|
+
const activeConversationIds = new Set(
|
|
4305
|
+
activeConversations.map((c) => String(c._getRaw("conversation_id")))
|
|
4306
|
+
);
|
|
4307
|
+
const queryConditions = filter.conversationId !== void 0 ? [import_watermelondb2.Q.where("conversation_id", filter.conversationId)] : [];
|
|
4308
|
+
const rows = await ctx.messagesCollection.query(...queryConditions).unsafeFetchRaw();
|
|
4309
|
+
const rowsById = /* @__PURE__ */ new Map();
|
|
4310
|
+
for (const row of rows) {
|
|
4311
|
+
const conversationId = String(row.conversation_id);
|
|
4312
|
+
if (!activeConversationIds.has(conversationId)) continue;
|
|
4313
|
+
if (filter.excludeConversationId !== void 0 && conversationId === filter.excludeConversationId) {
|
|
4314
|
+
continue;
|
|
4315
|
+
}
|
|
4316
|
+
if (!row.chunks && !row.vector) continue;
|
|
4317
|
+
rowsById.set(row.id, row);
|
|
4318
|
+
}
|
|
4319
|
+
const pick = (ids) => Promise.resolve(
|
|
4320
|
+
ids.flatMap((id) => {
|
|
4321
|
+
const row = rowsById.get(id);
|
|
4322
|
+
return row ? [row] : [];
|
|
4323
|
+
})
|
|
4324
|
+
);
|
|
4325
|
+
return {
|
|
4326
|
+
keys: Array.from(rowsById.values(), toChunkCandidateKey),
|
|
4327
|
+
loadVectorRows: pick,
|
|
4328
|
+
loadFullRows: pick
|
|
4329
|
+
};
|
|
4330
|
+
}
|
|
4331
|
+
async function chunkRowSource(ctx, filter) {
|
|
4332
|
+
try {
|
|
4333
|
+
return await projectedChunkRowSource(ctx, filter);
|
|
4334
|
+
} catch (err) {
|
|
4335
|
+
getLogger().debug(
|
|
4336
|
+
"searchChunksOp: projected SQL unavailable, using full-load fallback: " + (err instanceof Error ? err.message : String(err))
|
|
4337
|
+
);
|
|
4338
|
+
return fullScanChunkRowSource(ctx, filter);
|
|
4339
|
+
}
|
|
4340
|
+
}
|
|
4098
4341
|
async function searchChunksOp(ctx, queryVector, options) {
|
|
4099
4342
|
const {
|
|
4100
4343
|
limit = 10,
|
|
@@ -4102,83 +4345,111 @@ async function searchChunksOp(ctx, queryVector, options) {
|
|
|
4102
4345
|
conversationId,
|
|
4103
4346
|
excludeConversationId,
|
|
4104
4347
|
embeddingModel,
|
|
4105
|
-
chunkCache
|
|
4348
|
+
chunkCache,
|
|
4349
|
+
onStats
|
|
4106
4350
|
} = options || {};
|
|
4107
|
-
const
|
|
4108
|
-
const
|
|
4109
|
-
activeConversations.map((c) => String(c._getRaw("conversation_id")))
|
|
4110
|
-
);
|
|
4111
|
-
const queryConditions = conversationId ? [import_watermelondb2.Q.where("conversation_id", conversationId)] : [];
|
|
4112
|
-
const messages = await ctx.messagesCollection.query(...queryConditions).unsafeFetchRaw();
|
|
4351
|
+
const source = await chunkRowSource(ctx, { conversationId, excludeConversationId });
|
|
4352
|
+
const yieldIfDue = createTimeSliceYielder(CHUNK_SCAN_YIELD_BUDGET_MS);
|
|
4113
4353
|
const candidates = [];
|
|
4114
4354
|
let staleSkipped = 0;
|
|
4115
4355
|
let unreadableVectors = 0;
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
}
|
|
4356
|
+
let cacheHits = 0;
|
|
4357
|
+
const missIds = [];
|
|
4358
|
+
const orderById = /* @__PURE__ */ new Map();
|
|
4359
|
+
for (const [order, key] of source.keys.entries()) {
|
|
4360
|
+
await yieldIfDue();
|
|
4361
|
+
orderById.set(key.id, order);
|
|
4362
|
+
if (embeddingModel && key.embeddingModel !== null && key.embeddingModel !== embeddingModel) {
|
|
4363
|
+
staleSkipped++;
|
|
4364
|
+
continue;
|
|
4126
4365
|
}
|
|
4127
|
-
const
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
if (cached.chunks.length > 0) {
|
|
4131
|
-
for (let ci = 0; ci < cached.chunks.length; ci++) {
|
|
4132
|
-
const vec = cached.chunks[ci];
|
|
4133
|
-
if (vec.length === 0) continue;
|
|
4134
|
-
const similarity = cosineSimilarity(queryVector, vec);
|
|
4135
|
-
if (similarity >= minSimilarity) {
|
|
4136
|
-
candidates.push({
|
|
4137
|
-
message,
|
|
4138
|
-
similarity,
|
|
4139
|
-
chunkTextSource: { kind: "chunk-cached", chunkIndex: ci, version }
|
|
4140
|
-
});
|
|
4141
|
-
}
|
|
4142
|
-
}
|
|
4143
|
-
} else if (cached.fallback) {
|
|
4144
|
-
const similarity = cosineSimilarity(queryVector, cached.fallback);
|
|
4145
|
-
if (similarity >= minSimilarity) {
|
|
4146
|
-
candidates.push({ message, similarity, chunkTextSource: { kind: "message" } });
|
|
4147
|
-
}
|
|
4148
|
-
}
|
|
4366
|
+
const cached = chunkCache?.get(key.id);
|
|
4367
|
+
if (!cached || cached.version !== key.version) {
|
|
4368
|
+
missIds.push(key.id);
|
|
4149
4369
|
continue;
|
|
4150
4370
|
}
|
|
4151
|
-
|
|
4152
|
-
if (
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
unreadableVectors++;
|
|
4156
|
-
})
|
|
4157
|
-
);
|
|
4158
|
-
chunkCache?.set(message.id, { version, chunks: vectors });
|
|
4159
|
-
for (let ci = 0; ci < chunks3.length; ci++) {
|
|
4160
|
-
const vec = vectors[ci];
|
|
4371
|
+
cacheHits++;
|
|
4372
|
+
if (cached.chunks.length > 0) {
|
|
4373
|
+
for (let ci = 0; ci < cached.chunks.length; ci++) {
|
|
4374
|
+
const vec = cached.chunks[ci];
|
|
4161
4375
|
if (vec.length === 0) continue;
|
|
4162
4376
|
const similarity = cosineSimilarity(queryVector, vec);
|
|
4163
4377
|
if (similarity >= minSimilarity) {
|
|
4164
4378
|
candidates.push({
|
|
4165
|
-
|
|
4379
|
+
id: key.id,
|
|
4380
|
+
version: key.version,
|
|
4381
|
+
order,
|
|
4166
4382
|
similarity,
|
|
4167
|
-
chunkTextSource: { kind: "chunk",
|
|
4383
|
+
chunkTextSource: { kind: "chunk-cached", chunkIndex: ci, version: key.version }
|
|
4168
4384
|
});
|
|
4169
4385
|
}
|
|
4170
4386
|
}
|
|
4171
|
-
} else {
|
|
4172
|
-
const
|
|
4173
|
-
if (!messageVector || messageVector.length === 0) continue;
|
|
4174
|
-
const fallback = Float32Array.from(messageVector);
|
|
4175
|
-
chunkCache?.set(message.id, { version, chunks: [], fallback });
|
|
4176
|
-
const similarity = cosineSimilarity(queryVector, fallback);
|
|
4387
|
+
} else if (cached.fallback) {
|
|
4388
|
+
const similarity = cosineSimilarity(queryVector, cached.fallback);
|
|
4177
4389
|
if (similarity >= minSimilarity) {
|
|
4178
|
-
candidates.push({
|
|
4390
|
+
candidates.push({
|
|
4391
|
+
id: key.id,
|
|
4392
|
+
version: key.version,
|
|
4393
|
+
order,
|
|
4394
|
+
similarity,
|
|
4395
|
+
chunkTextSource: { kind: "message" }
|
|
4396
|
+
});
|
|
4179
4397
|
}
|
|
4180
4398
|
}
|
|
4181
4399
|
}
|
|
4400
|
+
let rowsFetched = 0;
|
|
4401
|
+
for (let offset = 0; offset < missIds.length; offset += CHUNK_VECTOR_BATCH_SIZE) {
|
|
4402
|
+
const rows = await source.loadVectorRows(
|
|
4403
|
+
missIds.slice(offset, offset + CHUNK_VECTOR_BATCH_SIZE)
|
|
4404
|
+
);
|
|
4405
|
+
rowsFetched += rows.length;
|
|
4406
|
+
for (const row of rows) {
|
|
4407
|
+
await yieldIfDue();
|
|
4408
|
+
const id = row.id;
|
|
4409
|
+
const version = Number(row.updated_at) || 0;
|
|
4410
|
+
const order = orderById.get(id) ?? Number.MAX_SAFE_INTEGER;
|
|
4411
|
+
const chunks3 = await readRawJsonField(row, "chunks", ctx.walletAddress);
|
|
4412
|
+
if (chunks3 && chunks3.length > 0) {
|
|
4413
|
+
const vectors = chunks3.map(
|
|
4414
|
+
(chunk) => decodeChunkVector(chunk.vector, () => {
|
|
4415
|
+
unreadableVectors++;
|
|
4416
|
+
})
|
|
4417
|
+
);
|
|
4418
|
+
chunkCache?.set(id, { version, chunks: vectors });
|
|
4419
|
+
for (let ci = 0; ci < chunks3.length; ci++) {
|
|
4420
|
+
const vec = vectors[ci];
|
|
4421
|
+
if (vec.length === 0) continue;
|
|
4422
|
+
const similarity = cosineSimilarity(queryVector, vec);
|
|
4423
|
+
if (similarity >= minSimilarity) {
|
|
4424
|
+
candidates.push({
|
|
4425
|
+
id,
|
|
4426
|
+
version,
|
|
4427
|
+
order,
|
|
4428
|
+
similarity,
|
|
4429
|
+
chunkTextSource: { kind: "chunk", chunk: chunks3[ci], siblings: chunks3 }
|
|
4430
|
+
});
|
|
4431
|
+
}
|
|
4432
|
+
}
|
|
4433
|
+
} else {
|
|
4434
|
+
const messageVector = await readRawJsonField(row, "vector", ctx.walletAddress);
|
|
4435
|
+
if (messageVector && messageVector.length > 0) {
|
|
4436
|
+
const fallback = Float32Array.from(messageVector);
|
|
4437
|
+
chunkCache?.set(id, { version, chunks: [], fallback });
|
|
4438
|
+
const similarity = cosineSimilarity(queryVector, fallback);
|
|
4439
|
+
if (similarity >= minSimilarity) {
|
|
4440
|
+
candidates.push({
|
|
4441
|
+
id,
|
|
4442
|
+
version,
|
|
4443
|
+
order,
|
|
4444
|
+
similarity,
|
|
4445
|
+
chunkTextSource: { kind: "message" }
|
|
4446
|
+
});
|
|
4447
|
+
}
|
|
4448
|
+
}
|
|
4449
|
+
}
|
|
4450
|
+
}
|
|
4451
|
+
}
|
|
4452
|
+
onStats?.({ rowsScanned: source.keys.length, rowsFetched, cacheHits });
|
|
4182
4453
|
if (staleSkipped > 0) {
|
|
4183
4454
|
getLogger().warn(
|
|
4184
4455
|
`searchChunksOp: skipped ${staleSkipped} messages whose embedding model differs from the current model (${embeddingModel}) \u2014 re-embed via chunkAndEmbedAllMessages`
|
|
@@ -4189,12 +4460,17 @@ async function searchChunksOp(ctx, queryVector, options) {
|
|
|
4189
4460
|
`searchChunksOp: could not read ${unreadableVectors} chunk vectors and left them out of ranking \u2014 the stored value is corrupt, so those chunks stay unsearchable until the message is re-embedded via chunkAndEmbedAllMessages`
|
|
4190
4461
|
);
|
|
4191
4462
|
}
|
|
4192
|
-
const topK = candidates.sort((a, b) => b.similarity - a.similarity).slice(0, limit);
|
|
4193
|
-
const
|
|
4194
|
-
|
|
4463
|
+
const topK = candidates.sort((a, b) => b.similarity - a.similarity || a.order - b.order).slice(0, limit);
|
|
4464
|
+
const topIds = Array.from(new Set(topK.map((c) => c.id)));
|
|
4465
|
+
const fullRows = await source.loadFullRows(topIds);
|
|
4466
|
+
const rowById = new Map(fullRows.map((row) => [row.id, row]));
|
|
4467
|
+
const liveTopK = topK.filter((c) => {
|
|
4468
|
+
const row = rowById.get(c.id);
|
|
4469
|
+
return row !== void 0 && (Number(row.updated_at) || 0) === c.version;
|
|
4470
|
+
});
|
|
4195
4471
|
const storedById = new Map(
|
|
4196
4472
|
await Promise.all(
|
|
4197
|
-
Array.from(
|
|
4473
|
+
Array.from(rowById, async ([id, m]) => {
|
|
4198
4474
|
const stored2 = await messageRawToStored(
|
|
4199
4475
|
m,
|
|
4200
4476
|
ctx.walletAddress,
|
|
@@ -4207,24 +4483,24 @@ async function searchChunksOp(ctx, queryVector, options) {
|
|
|
4207
4483
|
);
|
|
4208
4484
|
const chunkStateByMsgId = /* @__PURE__ */ new Map();
|
|
4209
4485
|
const needsChunkText = /* @__PURE__ */ new Set();
|
|
4210
|
-
for (const c of
|
|
4211
|
-
if (c.chunkTextSource.kind === "chunk-cached") needsChunkText.add(c.
|
|
4486
|
+
for (const c of liveTopK) {
|
|
4487
|
+
if (c.chunkTextSource.kind === "chunk-cached") needsChunkText.add(c.id);
|
|
4212
4488
|
}
|
|
4213
4489
|
await Promise.all(
|
|
4214
4490
|
Array.from(needsChunkText, async (id) => {
|
|
4215
|
-
const m =
|
|
4491
|
+
const m = rowById.get(id);
|
|
4216
4492
|
const parsed = await readRawJsonField(m, "chunks", ctx.walletAddress);
|
|
4217
4493
|
const version = Number(m.updated_at) || 0;
|
|
4218
4494
|
if (parsed) chunkStateByMsgId.set(id, { version, chunks: parsed });
|
|
4219
4495
|
})
|
|
4220
4496
|
);
|
|
4221
|
-
return
|
|
4222
|
-
const stored2 = { ...storedById.get(
|
|
4497
|
+
return liveTopK.map(({ id, similarity, chunkTextSource }) => {
|
|
4498
|
+
const stored2 = { ...storedById.get(id) };
|
|
4223
4499
|
let chunkText2;
|
|
4224
4500
|
if (chunkTextSource.kind === "chunk") {
|
|
4225
4501
|
chunkText2 = resolveChunkText(stored2.content, chunkTextSource.chunk, chunkTextSource.siblings);
|
|
4226
4502
|
} else if (chunkTextSource.kind === "chunk-cached") {
|
|
4227
|
-
const state = chunkStateByMsgId.get(
|
|
4503
|
+
const state = chunkStateByMsgId.get(id);
|
|
4228
4504
|
const chunk = state && state.version === chunkTextSource.version ? state.chunks[chunkTextSource.chunkIndex] : void 0;
|
|
4229
4505
|
chunkText2 = resolveChunkText(stored2.content, chunk, state?.chunks);
|
|
4230
4506
|
} else {
|
|
@@ -5309,13 +5585,35 @@ async function generateEmbedding(text14, options) {
|
|
|
5309
5585
|
if (total === void 0) return embedOne(text14, options, void 0);
|
|
5310
5586
|
return withDeadline((signal) => embedOne(text14, options, signal), total, "embedding");
|
|
5311
5587
|
}
|
|
5588
|
+
var inFlightByCache = /* @__PURE__ */ new WeakMap();
|
|
5312
5589
|
async function embedOne(text14, options, outer) {
|
|
5313
|
-
const {
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5590
|
+
const { cache } = options;
|
|
5591
|
+
if (!cache) return Array.from(await requestEmbedding(text14, options, outer));
|
|
5592
|
+
const cached = cache.get(text14);
|
|
5593
|
+
if (cached) return Array.from(cached);
|
|
5594
|
+
const inFlight = inFlightByCache.get(cache) ?? /* @__PURE__ */ new Map();
|
|
5595
|
+
inFlightByCache.set(cache, inFlight);
|
|
5596
|
+
const pending = inFlight.get(text14);
|
|
5597
|
+
if (pending) {
|
|
5598
|
+
try {
|
|
5599
|
+
return Array.from(await pending);
|
|
5600
|
+
} catch (err) {
|
|
5601
|
+
if (outer?.aborted) throw err;
|
|
5602
|
+
}
|
|
5603
|
+
}
|
|
5604
|
+
const request = requestEmbedding(text14, options, outer);
|
|
5605
|
+
inFlight.set(text14, request);
|
|
5606
|
+
try {
|
|
5607
|
+
const embedding2 = await request;
|
|
5608
|
+
cache.set(text14, embedding2);
|
|
5609
|
+
return Array.from(embedding2);
|
|
5610
|
+
} finally {
|
|
5611
|
+
if (inFlight.get(text14) === request) inFlight.delete(text14);
|
|
5318
5612
|
}
|
|
5613
|
+
}
|
|
5614
|
+
async function requestEmbedding(text14, options, outer) {
|
|
5615
|
+
const { baseUrl = BASE_URL, model } = options;
|
|
5616
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_EMBEDDING_REQUEST_TIMEOUT_MS;
|
|
5319
5617
|
const headers = {
|
|
5320
5618
|
...await resolveAuthHeaders(options, outer),
|
|
5321
5619
|
...taskTypeHeader(options.taskType)
|
|
@@ -5350,11 +5648,7 @@ async function embedOne(text14, options, outer) {
|
|
|
5350
5648
|
totalTokens: response.data.usage.total_tokens ?? 0
|
|
5351
5649
|
});
|
|
5352
5650
|
}
|
|
5353
|
-
|
|
5354
|
-
if (cache) {
|
|
5355
|
-
cache.set(text14, f32Embedding);
|
|
5356
|
-
}
|
|
5357
|
-
return Array.from(f32Embedding);
|
|
5651
|
+
return Float32Array.from(embedding2);
|
|
5358
5652
|
}
|
|
5359
5653
|
var DEFAULT_EMBEDDING_BATCH_SIZE = 100;
|
|
5360
5654
|
var DEFAULT_EMBEDDING_BATCH_CONCURRENCY = 3;
|
|
@@ -5752,7 +6046,8 @@ function extractReservationReceipts(toolCallEvents) {
|
|
|
5752
6046
|
for (const event of toolCallEvents ?? []) {
|
|
5753
6047
|
const action = ACTIONS.find((a) => event.name?.endsWith(a.tool));
|
|
5754
6048
|
const output = event.output ?? "";
|
|
5755
|
-
if (!action || !output || NOT_AN_ATTEMPT.test(output))
|
|
6049
|
+
if (!action || !output || hasEncryptedToolPayload(event) || NOT_AN_ATTEMPT.test(output))
|
|
6050
|
+
continue;
|
|
5756
6051
|
const kept = latest.get(action.kind)?.status;
|
|
5757
6052
|
if (kept === "made") continue;
|
|
5758
6053
|
const receipt = toReceipt(action, event.arguments ?? "", output);
|
|
@@ -10849,8 +11144,12 @@ var MaskScopedEmbeddingCache = class extends Map {
|
|
|
10849
11144
|
return this;
|
|
10850
11145
|
}
|
|
10851
11146
|
};
|
|
11147
|
+
var scopedViews = /* @__PURE__ */ new WeakMap();
|
|
10852
11148
|
function maskScopedEmbeddingCache(cache, masked) {
|
|
10853
|
-
|
|
11149
|
+
const views = scopedViews.get(cache) ?? {};
|
|
11150
|
+
scopedViews.set(cache, views);
|
|
11151
|
+
const slot = masked ? "masked" : "raw";
|
|
11152
|
+
return views[slot] ?? (views[slot] = new MaskScopedEmbeddingCache(cache, masked));
|
|
10854
11153
|
}
|
|
10855
11154
|
|
|
10856
11155
|
// src/lib/db/conversationMemory/models.ts
|
|
@@ -15211,9 +15510,9 @@ function recencyMultiplier(updatedAt, opts = {}) {
|
|
|
15211
15510
|
const updatedMs = updatedAt.getTime();
|
|
15212
15511
|
if (!Number.isFinite(updatedMs)) return noDate;
|
|
15213
15512
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
15214
|
-
const
|
|
15215
|
-
if (!Number.isFinite(
|
|
15216
|
-
const ageDays2 = Math.max(0, (
|
|
15513
|
+
const nowMs2 = now.getTime();
|
|
15514
|
+
if (!Number.isFinite(nowMs2)) return noDate;
|
|
15515
|
+
const ageDays2 = Math.max(0, (nowMs2 - updatedMs) / DAY_MS2);
|
|
15217
15516
|
const perYearDecay = opts.perYearDecay ?? DEFAULT_PER_YEAR_DECAY;
|
|
15218
15517
|
const floor = opts.floor ?? DEFAULT_FLOOR;
|
|
15219
15518
|
const decay = perYearDecay * (ageDays2 / 365);
|
|
@@ -15725,9 +16024,6 @@ function createVaultEmbeddingCache(maxSize = DEFAULT_VAULT_CACHE_SIZE) {
|
|
|
15725
16024
|
|
|
15726
16025
|
// src/lib/memoryVault/searchTool.ts
|
|
15727
16026
|
var DEFAULT_RERANK_TOP_N = 5;
|
|
15728
|
-
function nowMs() {
|
|
15729
|
-
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
15730
|
-
}
|
|
15731
16027
|
var warnedProgrammaticDecomposeIgnored = false;
|
|
15732
16028
|
function normalizeEventTimeKind(kind) {
|
|
15733
16029
|
return kind === "point" || kind === "range" || kind === "ongoing" ? kind : null;
|
|
@@ -18839,9 +19135,6 @@ function scoreEventTimeOverlap(memoryStart, memoryEnd, memoryKind, window2) {
|
|
|
18839
19135
|
var DEFAULT_LIMIT = 8;
|
|
18840
19136
|
var DEFAULT_BUDGET = "low";
|
|
18841
19137
|
var DEFAULT_FACT_MIN_SCORE = 0.1;
|
|
18842
|
-
function nowMs2() {
|
|
18843
|
-
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
18844
|
-
}
|
|
18845
19138
|
var DEFAULT_CHUNK_MIN_SCORE = 0.5;
|
|
18846
19139
|
var DEFAULT_QUERY_EMBED_TOTAL_TIMEOUT_MS = 8e3;
|
|
18847
19140
|
function flagsForBudget(budget) {
|
|
@@ -18879,12 +19172,19 @@ async function recall(query, ctx, options = {}) {
|
|
|
18879
19172
|
const subQueries = normalizedFacets.length >= 2 ? normalizedFacets : void 0;
|
|
18880
19173
|
const embeddingOptions = {
|
|
18881
19174
|
...ctx.embeddingOptions,
|
|
18882
|
-
taskType: "memory_recall_embed"
|
|
19175
|
+
taskType: "memory_recall_embed",
|
|
19176
|
+
...options.embeddingCache && !ctx.embeddingOptions.cache && {
|
|
19177
|
+
cache: maskScopedEmbeddingCache(
|
|
19178
|
+
options.embeddingCache,
|
|
19179
|
+
Boolean(ctx.embeddingOptions.maskInput)
|
|
19180
|
+
)
|
|
19181
|
+
}
|
|
18883
19182
|
};
|
|
18884
|
-
const t0 =
|
|
19183
|
+
const t0 = nowMs();
|
|
18885
19184
|
let prepMs = 0;
|
|
18886
19185
|
let factLaneMs = 0;
|
|
18887
19186
|
let chunkLaneMs = 0;
|
|
19187
|
+
let chunkStats;
|
|
18888
19188
|
let fuseMs = 0;
|
|
18889
19189
|
const factResults = [];
|
|
18890
19190
|
const chunkResults = [];
|
|
@@ -18939,6 +19239,11 @@ async function recall(query, ctx, options = {}) {
|
|
|
18939
19239
|
...decryptLastRan !== void 0 && { decryptLast: decryptLastRan },
|
|
18940
19240
|
...vaultRowsDecrypted !== void 0 && { vaultRowsDecrypted },
|
|
18941
19241
|
...vaultRowsEmbedded !== void 0 && { vaultRowsEmbedded },
|
|
19242
|
+
...chunkStats && {
|
|
19243
|
+
chunkRowsScanned: chunkStats.rowsScanned,
|
|
19244
|
+
chunkRowsFetched: chunkStats.rowsFetched,
|
|
19245
|
+
chunkCacheHits: chunkStats.cacheHits
|
|
19246
|
+
},
|
|
18942
19247
|
factCount: factResults.length,
|
|
18943
19248
|
chunkCount: chunkResults.length,
|
|
18944
19249
|
admittedCount: admitted.length,
|
|
@@ -18950,7 +19255,7 @@ async function recall(query, ctx, options = {}) {
|
|
|
18950
19255
|
temporalLaneCount,
|
|
18951
19256
|
emptyReason: emptyReasonFor(admitted.length, laneRan),
|
|
18952
19257
|
timings: {
|
|
18953
|
-
total:
|
|
19258
|
+
total: nowMs() - t0,
|
|
18954
19259
|
prep: prepMs,
|
|
18955
19260
|
factLane: factLaneMs,
|
|
18956
19261
|
rerank: rerankMs,
|
|
@@ -18972,14 +19277,14 @@ async function recall(query, ctx, options = {}) {
|
|
|
18972
19277
|
const needsChunkEmbedding = types.includes("chunk") && ctx.storageCtx;
|
|
18973
19278
|
const wantsTemporal = types.includes("fact") && (ctx.factSource || ctx.vaultCtx);
|
|
18974
19279
|
const queryEmbedTotalTimeoutMs = options.queryEmbedTotalTimeoutMs ?? DEFAULT_QUERY_EMBED_TOTAL_TIMEOUT_MS;
|
|
18975
|
-
const prepStart =
|
|
19280
|
+
const prepStart = nowMs();
|
|
18976
19281
|
let sharedEmbedMs = 0;
|
|
18977
19282
|
const [queryEmbedding, entityRanking, temporalRanking] = await Promise.all([
|
|
18978
19283
|
needsChunkEmbedding ? generateEmbedding(query, {
|
|
18979
19284
|
...embeddingOptions,
|
|
18980
19285
|
totalTimeoutMs: queryEmbedTotalTimeoutMs
|
|
18981
19286
|
}).finally(() => {
|
|
18982
|
-
sharedEmbedMs =
|
|
19287
|
+
sharedEmbedMs = nowMs() - prepStart;
|
|
18983
19288
|
}).then((vec) => {
|
|
18984
19289
|
if (vec.length === 0) {
|
|
18985
19290
|
getLogger().warn(
|
|
@@ -19012,11 +19317,11 @@ async function recall(query, ctx, options = {}) {
|
|
|
19012
19317
|
() => ctx.factSource ? ctx.factSource.temporalRanking(query, options.now) : buildTemporalLaneRanking(query, ctx.vaultCtx, options.now)
|
|
19013
19318
|
) : Promise.resolve([])
|
|
19014
19319
|
]);
|
|
19015
|
-
prepMs =
|
|
19320
|
+
prepMs = nowMs() - prepStart;
|
|
19016
19321
|
graphLaneCount = entityRanking.length;
|
|
19017
19322
|
temporalLaneCount = temporalRanking.length;
|
|
19018
19323
|
if (types.includes("fact") && (ctx.factSource || ctx.vaultCtx && ctx.vaultCache)) {
|
|
19019
|
-
const factStart =
|
|
19324
|
+
const factStart = nowMs();
|
|
19020
19325
|
const vaultMinScore = options.minScore ?? DEFAULT_FACT_MIN_SCORE;
|
|
19021
19326
|
factFloor = vaultMinScore;
|
|
19022
19327
|
const {
|
|
@@ -19092,11 +19397,11 @@ async function recall(query, ctx, options = {}) {
|
|
|
19092
19397
|
factLaneRankedOnCosine = factRankedOnCosine;
|
|
19093
19398
|
decryptLastRan = factDecryptLast;
|
|
19094
19399
|
vaultRowsDecrypted = factRowsDecrypted;
|
|
19095
|
-
factLaneMs =
|
|
19400
|
+
factLaneMs = nowMs() - factStart;
|
|
19096
19401
|
}
|
|
19097
19402
|
if (chunkEmbedFailed && !factLaneRankedOnCosine) embeddingsUnavailable = true;
|
|
19098
19403
|
if (types.includes("chunk") && ctx.storageCtx && queryEmbedding) {
|
|
19099
|
-
const chunkStart =
|
|
19404
|
+
const chunkStart = nowMs();
|
|
19100
19405
|
const chunkMinScore = options.minScore ?? DEFAULT_CHUNK_MIN_SCORE;
|
|
19101
19406
|
chunkFloor = chunkMinScore;
|
|
19102
19407
|
const results = await searchChunksOp(ctx.storageCtx, queryEmbedding, {
|
|
@@ -19107,12 +19412,15 @@ async function recall(query, ctx, options = {}) {
|
|
|
19107
19412
|
...options.excludeConversationId && {
|
|
19108
19413
|
excludeConversationId: options.excludeConversationId
|
|
19109
19414
|
},
|
|
19110
|
-
...ctx.chunkCache && { chunkCache: ctx.chunkCache }
|
|
19415
|
+
...ctx.chunkCache && { chunkCache: ctx.chunkCache },
|
|
19416
|
+
onStats: (stats) => {
|
|
19417
|
+
chunkStats = stats;
|
|
19418
|
+
}
|
|
19111
19419
|
});
|
|
19112
19420
|
chunkResults.push(...dedupeBy(results, (r) => r.chunkText.trim()));
|
|
19113
|
-
chunkLaneMs =
|
|
19421
|
+
chunkLaneMs = nowMs() - chunkStart;
|
|
19114
19422
|
}
|
|
19115
|
-
const fuseStart =
|
|
19423
|
+
const fuseStart = nowMs();
|
|
19116
19424
|
if (types.length === 1 || factResults.length === 0 || chunkResults.length === 0) {
|
|
19117
19425
|
const memories2 = [
|
|
19118
19426
|
...factResults.map((r) => toFactMemory(r, options.now)),
|
|
@@ -19120,7 +19428,7 @@ async function recall(query, ctx, options = {}) {
|
|
|
19120
19428
|
];
|
|
19121
19429
|
memories2.sort((a, b) => b.score - a.score);
|
|
19122
19430
|
const candidateCount = factResults.length + chunkResults.length;
|
|
19123
|
-
fuseMs =
|
|
19431
|
+
fuseMs = nowMs() - fuseStart;
|
|
19124
19432
|
const admitted = memories2.slice(0, limit);
|
|
19125
19433
|
hitLimit = memories2.length > limit;
|
|
19126
19434
|
emitDiagnostics(candidateCount, admitted);
|
|
@@ -19184,7 +19492,7 @@ async function recall(query, ctx, options = {}) {
|
|
|
19184
19492
|
memories = selection.out;
|
|
19185
19493
|
}
|
|
19186
19494
|
hitLimit = selection.cut;
|
|
19187
|
-
fuseMs =
|
|
19495
|
+
fuseMs = nowMs() - fuseStart;
|
|
19188
19496
|
emitDiagnostics(byId.size, memories);
|
|
19189
19497
|
return {
|
|
19190
19498
|
memories,
|
|
@@ -26831,11 +27139,12 @@ function storedToLlmapiMessage(stored2) {
|
|
|
26831
27139
|
}
|
|
26832
27140
|
}
|
|
26833
27141
|
const messages = [];
|
|
26834
|
-
|
|
27142
|
+
const toolCallEvents = Array.isArray(stored2.toolCallEvents) ? stored2.toolCallEvents.filter((event) => !hasEncryptedToolPayload(event)) : [];
|
|
27143
|
+
if (stored2.role === "assistant" && toolCallEvents.length) {
|
|
26835
27144
|
messages.push({
|
|
26836
27145
|
role: stored2.role,
|
|
26837
27146
|
content: void 0,
|
|
26838
|
-
tool_calls:
|
|
27147
|
+
tool_calls: toolCallEvents.map((event) => ({
|
|
26839
27148
|
id: event.id,
|
|
26840
27149
|
type: "function",
|
|
26841
27150
|
function: {
|
|
@@ -26844,7 +27153,7 @@ function storedToLlmapiMessage(stored2) {
|
|
|
26844
27153
|
}
|
|
26845
27154
|
}))
|
|
26846
27155
|
});
|
|
26847
|
-
for (const event of
|
|
27156
|
+
for (const event of toolCallEvents) {
|
|
26848
27157
|
if (event.id && event.output !== void 0 && event.output !== null) {
|
|
26849
27158
|
messages.push({
|
|
26850
27159
|
role: "tool",
|
|
@@ -28848,6 +29157,7 @@ function useChatStorage(options) {
|
|
|
28848
29157
|
embedAllMessages,
|
|
28849
29158
|
embedMessage,
|
|
28850
29159
|
encodeChunkVector,
|
|
29160
|
+
encryptToolCallEventsOp,
|
|
28851
29161
|
ensureDefaultFoldersOp,
|
|
28852
29162
|
exchangeNotionCode,
|
|
28853
29163
|
extractAndLinkEntitiesForMemoriesOp,
|