@anuma/sdk 7.0.1 → 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 +284 -102
- package/dist/expo/index.d.mts +18 -24
- package/dist/expo/index.d.ts +18 -24
- package/dist/expo/index.mjs +284 -102
- package/dist/index.cjs +28 -10
- package/dist/index.mjs +28 -10
- package/dist/memory/context.d.mts +8 -0
- package/dist/memory/context.d.ts +8 -0
- package/dist/react/index.cjs +305 -123
- package/dist/react/index.d.mts +23 -44
- package/dist/react/index.d.ts +23 -44
- package/dist/react/index.mjs +305 -123
- package/dist/server/index.cjs +305 -101
- package/dist/server/index.d.mts +23 -21
- package/dist/server/index.d.ts +23 -21
- package/dist/server/index.mjs +305 -101
- 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/server/index.mjs
CHANGED
|
@@ -16019,6 +16019,35 @@ function decodeChunkVector(vector, onMalformed) {
|
|
|
16019
16019
|
}
|
|
16020
16020
|
}
|
|
16021
16021
|
|
|
16022
|
+
// src/lib/scheduling.ts
|
|
16023
|
+
function nowMs() {
|
|
16024
|
+
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
16025
|
+
}
|
|
16026
|
+
function yieldToEventLoop() {
|
|
16027
|
+
const scheduler = globalThis.scheduler;
|
|
16028
|
+
if (typeof scheduler?.yield === "function") return scheduler.yield();
|
|
16029
|
+
if (typeof MessageChannel === "function") {
|
|
16030
|
+
return new Promise((resolve) => {
|
|
16031
|
+
const channel = new MessageChannel();
|
|
16032
|
+
channel.port1.onmessage = () => {
|
|
16033
|
+
channel.port1.close();
|
|
16034
|
+
channel.port2.close();
|
|
16035
|
+
resolve();
|
|
16036
|
+
};
|
|
16037
|
+
channel.port2.postMessage(null);
|
|
16038
|
+
});
|
|
16039
|
+
}
|
|
16040
|
+
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
16041
|
+
}
|
|
16042
|
+
function createTimeSliceYielder(budgetMs) {
|
|
16043
|
+
let sliceStart = nowMs();
|
|
16044
|
+
return async () => {
|
|
16045
|
+
if (nowMs() - sliceStart < budgetMs) return;
|
|
16046
|
+
await yieldToEventLoop();
|
|
16047
|
+
sliceStart = nowMs();
|
|
16048
|
+
};
|
|
16049
|
+
}
|
|
16050
|
+
|
|
16022
16051
|
// src/lib/db/encryption-utils.ts
|
|
16023
16052
|
var ENCRYPTION_PREFIX = "enc:v3:";
|
|
16024
16053
|
function isEncrypted(value) {
|
|
@@ -17163,6 +17192,95 @@ async function searchMessagesOp(ctx, queryVector, options) {
|
|
|
17163
17192
|
})
|
|
17164
17193
|
);
|
|
17165
17194
|
}
|
|
17195
|
+
var CHUNK_VECTOR_BATCH_SIZE = 50;
|
|
17196
|
+
var CHUNK_SCAN_YIELD_BUDGET_MS = 12;
|
|
17197
|
+
var hasEmbeddingSql = `(coalesce("chunks", '') != '' or coalesce("vector", '') != '')`;
|
|
17198
|
+
function toChunkCandidateKey(raw) {
|
|
17199
|
+
return {
|
|
17200
|
+
id: raw.id,
|
|
17201
|
+
conversationId: String(raw.conversation_id),
|
|
17202
|
+
version: Number(raw.updated_at) || 0,
|
|
17203
|
+
embeddingModel: raw.embedding_model ?? null
|
|
17204
|
+
};
|
|
17205
|
+
}
|
|
17206
|
+
async function fetchRowsByIds(ctx, ids) {
|
|
17207
|
+
if (ids.length === 0) return [];
|
|
17208
|
+
return await ctx.messagesCollection.query(import_watermelondb2.Q.where("id", import_watermelondb2.Q.oneOf(ids))).unsafeFetchRaw();
|
|
17209
|
+
}
|
|
17210
|
+
async function projectedChunkRowSource(ctx, filter) {
|
|
17211
|
+
const clauses = [
|
|
17212
|
+
`"history"."_status" is not 'deleted'`,
|
|
17213
|
+
hasEmbeddingSql,
|
|
17214
|
+
`exists (select 1 from "conversations" where "conversations"."conversation_id" = "history"."conversation_id" and "conversations"."is_deleted" = 0 and "conversations"."_status" is not 'deleted')`
|
|
17215
|
+
];
|
|
17216
|
+
const args = [];
|
|
17217
|
+
if (filter.conversationId !== void 0) {
|
|
17218
|
+
clauses.push(`"history"."conversation_id" = ?`);
|
|
17219
|
+
args.push(filter.conversationId);
|
|
17220
|
+
}
|
|
17221
|
+
if (filter.excludeConversationId !== void 0) {
|
|
17222
|
+
clauses.push(`"history"."conversation_id" != ?`);
|
|
17223
|
+
args.push(filter.excludeConversationId);
|
|
17224
|
+
}
|
|
17225
|
+
const keyRows = await ctx.messagesCollection.query(
|
|
17226
|
+
import_watermelondb2.Q.unsafeSqlQuery(
|
|
17227
|
+
`select "id", "conversation_id", "updated_at", "embedding_model" from "history" where ${clauses.join(" and ")}`,
|
|
17228
|
+
args
|
|
17229
|
+
)
|
|
17230
|
+
).unsafeFetchRaw();
|
|
17231
|
+
return {
|
|
17232
|
+
keys: keyRows.map(toChunkCandidateKey),
|
|
17233
|
+
loadVectorRows: async (ids) => {
|
|
17234
|
+
if (ids.length === 0) return [];
|
|
17235
|
+
return await ctx.messagesCollection.query(
|
|
17236
|
+
import_watermelondb2.Q.unsafeSqlQuery(
|
|
17237
|
+
`select "id", "updated_at", "chunks", "vector" from "history" where "_status" is not 'deleted' and "id" in (${ids.map(() => "?").join(",")})`,
|
|
17238
|
+
ids
|
|
17239
|
+
)
|
|
17240
|
+
).unsafeFetchRaw();
|
|
17241
|
+
},
|
|
17242
|
+
loadFullRows: (ids) => fetchRowsByIds(ctx, ids)
|
|
17243
|
+
};
|
|
17244
|
+
}
|
|
17245
|
+
async function fullScanChunkRowSource(ctx, filter) {
|
|
17246
|
+
const activeConversations = await ctx.conversationsCollection.query(import_watermelondb2.Q.where("is_deleted", false)).fetch();
|
|
17247
|
+
const activeConversationIds = new Set(
|
|
17248
|
+
activeConversations.map((c) => String(c._getRaw("conversation_id")))
|
|
17249
|
+
);
|
|
17250
|
+
const queryConditions = filter.conversationId !== void 0 ? [import_watermelondb2.Q.where("conversation_id", filter.conversationId)] : [];
|
|
17251
|
+
const rows = await ctx.messagesCollection.query(...queryConditions).unsafeFetchRaw();
|
|
17252
|
+
const rowsById = /* @__PURE__ */ new Map();
|
|
17253
|
+
for (const row of rows) {
|
|
17254
|
+
const conversationId = String(row.conversation_id);
|
|
17255
|
+
if (!activeConversationIds.has(conversationId)) continue;
|
|
17256
|
+
if (filter.excludeConversationId !== void 0 && conversationId === filter.excludeConversationId) {
|
|
17257
|
+
continue;
|
|
17258
|
+
}
|
|
17259
|
+
if (!row.chunks && !row.vector) continue;
|
|
17260
|
+
rowsById.set(row.id, row);
|
|
17261
|
+
}
|
|
17262
|
+
const pick = (ids) => Promise.resolve(
|
|
17263
|
+
ids.flatMap((id) => {
|
|
17264
|
+
const row = rowsById.get(id);
|
|
17265
|
+
return row ? [row] : [];
|
|
17266
|
+
})
|
|
17267
|
+
);
|
|
17268
|
+
return {
|
|
17269
|
+
keys: Array.from(rowsById.values(), toChunkCandidateKey),
|
|
17270
|
+
loadVectorRows: pick,
|
|
17271
|
+
loadFullRows: pick
|
|
17272
|
+
};
|
|
17273
|
+
}
|
|
17274
|
+
async function chunkRowSource(ctx, filter) {
|
|
17275
|
+
try {
|
|
17276
|
+
return await projectedChunkRowSource(ctx, filter);
|
|
17277
|
+
} catch (err) {
|
|
17278
|
+
getLogger().debug(
|
|
17279
|
+
"searchChunksOp: projected SQL unavailable, using full-load fallback: " + (err instanceof Error ? err.message : String(err))
|
|
17280
|
+
);
|
|
17281
|
+
return fullScanChunkRowSource(ctx, filter);
|
|
17282
|
+
}
|
|
17283
|
+
}
|
|
17166
17284
|
async function searchChunksOp(ctx, queryVector, options) {
|
|
17167
17285
|
const {
|
|
17168
17286
|
limit = 10,
|
|
@@ -17170,83 +17288,111 @@ async function searchChunksOp(ctx, queryVector, options) {
|
|
|
17170
17288
|
conversationId,
|
|
17171
17289
|
excludeConversationId,
|
|
17172
17290
|
embeddingModel,
|
|
17173
|
-
chunkCache
|
|
17291
|
+
chunkCache,
|
|
17292
|
+
onStats
|
|
17174
17293
|
} = options || {};
|
|
17175
|
-
const
|
|
17176
|
-
const
|
|
17177
|
-
activeConversations.map((c) => String(c._getRaw("conversation_id")))
|
|
17178
|
-
);
|
|
17179
|
-
const queryConditions = conversationId ? [import_watermelondb2.Q.where("conversation_id", conversationId)] : [];
|
|
17180
|
-
const messages = await ctx.messagesCollection.query(...queryConditions).unsafeFetchRaw();
|
|
17294
|
+
const source = await chunkRowSource(ctx, { conversationId, excludeConversationId });
|
|
17295
|
+
const yieldIfDue = createTimeSliceYielder(CHUNK_SCAN_YIELD_BUDGET_MS);
|
|
17181
17296
|
const candidates = [];
|
|
17182
17297
|
let staleSkipped = 0;
|
|
17183
17298
|
let unreadableVectors = 0;
|
|
17184
|
-
|
|
17185
|
-
|
|
17186
|
-
|
|
17187
|
-
|
|
17188
|
-
|
|
17189
|
-
|
|
17190
|
-
|
|
17191
|
-
|
|
17192
|
-
|
|
17193
|
-
}
|
|
17299
|
+
let cacheHits = 0;
|
|
17300
|
+
const missIds = [];
|
|
17301
|
+
const orderById = /* @__PURE__ */ new Map();
|
|
17302
|
+
for (const [order, key] of source.keys.entries()) {
|
|
17303
|
+
await yieldIfDue();
|
|
17304
|
+
orderById.set(key.id, order);
|
|
17305
|
+
if (embeddingModel && key.embeddingModel !== null && key.embeddingModel !== embeddingModel) {
|
|
17306
|
+
staleSkipped++;
|
|
17307
|
+
continue;
|
|
17194
17308
|
}
|
|
17195
|
-
const
|
|
17196
|
-
|
|
17197
|
-
|
|
17198
|
-
if (cached.chunks.length > 0) {
|
|
17199
|
-
for (let ci = 0; ci < cached.chunks.length; ci++) {
|
|
17200
|
-
const vec = cached.chunks[ci];
|
|
17201
|
-
if (vec.length === 0) continue;
|
|
17202
|
-
const similarity = cosineSimilarity(queryVector, vec);
|
|
17203
|
-
if (similarity >= minSimilarity) {
|
|
17204
|
-
candidates.push({
|
|
17205
|
-
message,
|
|
17206
|
-
similarity,
|
|
17207
|
-
chunkTextSource: { kind: "chunk-cached", chunkIndex: ci, version }
|
|
17208
|
-
});
|
|
17209
|
-
}
|
|
17210
|
-
}
|
|
17211
|
-
} else if (cached.fallback) {
|
|
17212
|
-
const similarity = cosineSimilarity(queryVector, cached.fallback);
|
|
17213
|
-
if (similarity >= minSimilarity) {
|
|
17214
|
-
candidates.push({ message, similarity, chunkTextSource: { kind: "message" } });
|
|
17215
|
-
}
|
|
17216
|
-
}
|
|
17309
|
+
const cached = chunkCache?.get(key.id);
|
|
17310
|
+
if (!cached || cached.version !== key.version) {
|
|
17311
|
+
missIds.push(key.id);
|
|
17217
17312
|
continue;
|
|
17218
17313
|
}
|
|
17219
|
-
|
|
17220
|
-
if (chunks
|
|
17221
|
-
|
|
17222
|
-
|
|
17223
|
-
unreadableVectors++;
|
|
17224
|
-
})
|
|
17225
|
-
);
|
|
17226
|
-
chunkCache?.set(message.id, { version, chunks: vectors });
|
|
17227
|
-
for (let ci = 0; ci < chunks.length; ci++) {
|
|
17228
|
-
const vec = vectors[ci];
|
|
17314
|
+
cacheHits++;
|
|
17315
|
+
if (cached.chunks.length > 0) {
|
|
17316
|
+
for (let ci = 0; ci < cached.chunks.length; ci++) {
|
|
17317
|
+
const vec = cached.chunks[ci];
|
|
17229
17318
|
if (vec.length === 0) continue;
|
|
17230
17319
|
const similarity = cosineSimilarity(queryVector, vec);
|
|
17231
17320
|
if (similarity >= minSimilarity) {
|
|
17232
17321
|
candidates.push({
|
|
17233
|
-
|
|
17322
|
+
id: key.id,
|
|
17323
|
+
version: key.version,
|
|
17324
|
+
order,
|
|
17234
17325
|
similarity,
|
|
17235
|
-
chunkTextSource: { kind: "chunk",
|
|
17326
|
+
chunkTextSource: { kind: "chunk-cached", chunkIndex: ci, version: key.version }
|
|
17236
17327
|
});
|
|
17237
17328
|
}
|
|
17238
17329
|
}
|
|
17239
|
-
} else {
|
|
17240
|
-
const
|
|
17241
|
-
if (!messageVector || messageVector.length === 0) continue;
|
|
17242
|
-
const fallback = Float32Array.from(messageVector);
|
|
17243
|
-
chunkCache?.set(message.id, { version, chunks: [], fallback });
|
|
17244
|
-
const similarity = cosineSimilarity(queryVector, fallback);
|
|
17330
|
+
} else if (cached.fallback) {
|
|
17331
|
+
const similarity = cosineSimilarity(queryVector, cached.fallback);
|
|
17245
17332
|
if (similarity >= minSimilarity) {
|
|
17246
|
-
candidates.push({
|
|
17333
|
+
candidates.push({
|
|
17334
|
+
id: key.id,
|
|
17335
|
+
version: key.version,
|
|
17336
|
+
order,
|
|
17337
|
+
similarity,
|
|
17338
|
+
chunkTextSource: { kind: "message" }
|
|
17339
|
+
});
|
|
17247
17340
|
}
|
|
17248
17341
|
}
|
|
17249
17342
|
}
|
|
17343
|
+
let rowsFetched = 0;
|
|
17344
|
+
for (let offset = 0; offset < missIds.length; offset += CHUNK_VECTOR_BATCH_SIZE) {
|
|
17345
|
+
const rows = await source.loadVectorRows(
|
|
17346
|
+
missIds.slice(offset, offset + CHUNK_VECTOR_BATCH_SIZE)
|
|
17347
|
+
);
|
|
17348
|
+
rowsFetched += rows.length;
|
|
17349
|
+
for (const row of rows) {
|
|
17350
|
+
await yieldIfDue();
|
|
17351
|
+
const id = row.id;
|
|
17352
|
+
const version = Number(row.updated_at) || 0;
|
|
17353
|
+
const order = orderById.get(id) ?? Number.MAX_SAFE_INTEGER;
|
|
17354
|
+
const chunks = await readRawJsonField(row, "chunks", ctx.walletAddress);
|
|
17355
|
+
if (chunks && chunks.length > 0) {
|
|
17356
|
+
const vectors = chunks.map(
|
|
17357
|
+
(chunk) => decodeChunkVector(chunk.vector, () => {
|
|
17358
|
+
unreadableVectors++;
|
|
17359
|
+
})
|
|
17360
|
+
);
|
|
17361
|
+
chunkCache?.set(id, { version, chunks: vectors });
|
|
17362
|
+
for (let ci = 0; ci < chunks.length; ci++) {
|
|
17363
|
+
const vec = vectors[ci];
|
|
17364
|
+
if (vec.length === 0) continue;
|
|
17365
|
+
const similarity = cosineSimilarity(queryVector, vec);
|
|
17366
|
+
if (similarity >= minSimilarity) {
|
|
17367
|
+
candidates.push({
|
|
17368
|
+
id,
|
|
17369
|
+
version,
|
|
17370
|
+
order,
|
|
17371
|
+
similarity,
|
|
17372
|
+
chunkTextSource: { kind: "chunk", chunk: chunks[ci], siblings: chunks }
|
|
17373
|
+
});
|
|
17374
|
+
}
|
|
17375
|
+
}
|
|
17376
|
+
} else {
|
|
17377
|
+
const messageVector = await readRawJsonField(row, "vector", ctx.walletAddress);
|
|
17378
|
+
if (messageVector && messageVector.length > 0) {
|
|
17379
|
+
const fallback = Float32Array.from(messageVector);
|
|
17380
|
+
chunkCache?.set(id, { version, chunks: [], fallback });
|
|
17381
|
+
const similarity = cosineSimilarity(queryVector, fallback);
|
|
17382
|
+
if (similarity >= minSimilarity) {
|
|
17383
|
+
candidates.push({
|
|
17384
|
+
id,
|
|
17385
|
+
version,
|
|
17386
|
+
order,
|
|
17387
|
+
similarity,
|
|
17388
|
+
chunkTextSource: { kind: "message" }
|
|
17389
|
+
});
|
|
17390
|
+
}
|
|
17391
|
+
}
|
|
17392
|
+
}
|
|
17393
|
+
}
|
|
17394
|
+
}
|
|
17395
|
+
onStats?.({ rowsScanned: source.keys.length, rowsFetched, cacheHits });
|
|
17250
17396
|
if (staleSkipped > 0) {
|
|
17251
17397
|
getLogger().warn(
|
|
17252
17398
|
`searchChunksOp: skipped ${staleSkipped} messages whose embedding model differs from the current model (${embeddingModel}) \u2014 re-embed via chunkAndEmbedAllMessages`
|
|
@@ -17257,12 +17403,17 @@ async function searchChunksOp(ctx, queryVector, options) {
|
|
|
17257
17403
|
`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`
|
|
17258
17404
|
);
|
|
17259
17405
|
}
|
|
17260
|
-
const topK = candidates.sort((a, b) => b.similarity - a.similarity).slice(0, limit);
|
|
17261
|
-
const
|
|
17262
|
-
|
|
17406
|
+
const topK = candidates.sort((a, b) => b.similarity - a.similarity || a.order - b.order).slice(0, limit);
|
|
17407
|
+
const topIds = Array.from(new Set(topK.map((c) => c.id)));
|
|
17408
|
+
const fullRows = await source.loadFullRows(topIds);
|
|
17409
|
+
const rowById = new Map(fullRows.map((row) => [row.id, row]));
|
|
17410
|
+
const liveTopK = topK.filter((c) => {
|
|
17411
|
+
const row = rowById.get(c.id);
|
|
17412
|
+
return row !== void 0 && (Number(row.updated_at) || 0) === c.version;
|
|
17413
|
+
});
|
|
17263
17414
|
const storedById = new Map(
|
|
17264
17415
|
await Promise.all(
|
|
17265
|
-
Array.from(
|
|
17416
|
+
Array.from(rowById, async ([id, m]) => {
|
|
17266
17417
|
const stored = await messageRawToStored(
|
|
17267
17418
|
m,
|
|
17268
17419
|
ctx.walletAddress,
|
|
@@ -17275,24 +17426,24 @@ async function searchChunksOp(ctx, queryVector, options) {
|
|
|
17275
17426
|
);
|
|
17276
17427
|
const chunkStateByMsgId = /* @__PURE__ */ new Map();
|
|
17277
17428
|
const needsChunkText = /* @__PURE__ */ new Set();
|
|
17278
|
-
for (const c of
|
|
17279
|
-
if (c.chunkTextSource.kind === "chunk-cached") needsChunkText.add(c.
|
|
17429
|
+
for (const c of liveTopK) {
|
|
17430
|
+
if (c.chunkTextSource.kind === "chunk-cached") needsChunkText.add(c.id);
|
|
17280
17431
|
}
|
|
17281
17432
|
await Promise.all(
|
|
17282
17433
|
Array.from(needsChunkText, async (id) => {
|
|
17283
|
-
const m =
|
|
17434
|
+
const m = rowById.get(id);
|
|
17284
17435
|
const parsed = await readRawJsonField(m, "chunks", ctx.walletAddress);
|
|
17285
17436
|
const version = Number(m.updated_at) || 0;
|
|
17286
17437
|
if (parsed) chunkStateByMsgId.set(id, { version, chunks: parsed });
|
|
17287
17438
|
})
|
|
17288
17439
|
);
|
|
17289
|
-
return
|
|
17290
|
-
const stored = { ...storedById.get(
|
|
17440
|
+
return liveTopK.map(({ id, similarity, chunkTextSource }) => {
|
|
17441
|
+
const stored = { ...storedById.get(id) };
|
|
17291
17442
|
let chunkText2;
|
|
17292
17443
|
if (chunkTextSource.kind === "chunk") {
|
|
17293
17444
|
chunkText2 = resolveChunkText(stored.content, chunkTextSource.chunk, chunkTextSource.siblings);
|
|
17294
17445
|
} else if (chunkTextSource.kind === "chunk-cached") {
|
|
17295
|
-
const state = chunkStateByMsgId.get(
|
|
17446
|
+
const state = chunkStateByMsgId.get(id);
|
|
17296
17447
|
const chunk = state && state.version === chunkTextSource.version ? state.chunks[chunkTextSource.chunkIndex] : void 0;
|
|
17297
17448
|
chunkText2 = resolveChunkText(stored.content, chunk, state?.chunks);
|
|
17298
17449
|
} else {
|
|
@@ -18388,13 +18539,35 @@ async function generateEmbedding(text12, options) {
|
|
|
18388
18539
|
if (total === void 0) return embedOne(text12, options, void 0);
|
|
18389
18540
|
return withDeadline((signal) => embedOne(text12, options, signal), total, "embedding");
|
|
18390
18541
|
}
|
|
18542
|
+
var inFlightByCache = /* @__PURE__ */ new WeakMap();
|
|
18391
18543
|
async function embedOne(text12, options, outer) {
|
|
18392
|
-
const {
|
|
18393
|
-
|
|
18394
|
-
|
|
18395
|
-
|
|
18396
|
-
|
|
18544
|
+
const { cache } = options;
|
|
18545
|
+
if (!cache) return Array.from(await requestEmbedding(text12, options, outer));
|
|
18546
|
+
const cached = cache.get(text12);
|
|
18547
|
+
if (cached) return Array.from(cached);
|
|
18548
|
+
const inFlight = inFlightByCache.get(cache) ?? /* @__PURE__ */ new Map();
|
|
18549
|
+
inFlightByCache.set(cache, inFlight);
|
|
18550
|
+
const pending = inFlight.get(text12);
|
|
18551
|
+
if (pending) {
|
|
18552
|
+
try {
|
|
18553
|
+
return Array.from(await pending);
|
|
18554
|
+
} catch (err) {
|
|
18555
|
+
if (outer?.aborted) throw err;
|
|
18556
|
+
}
|
|
18557
|
+
}
|
|
18558
|
+
const request = requestEmbedding(text12, options, outer);
|
|
18559
|
+
inFlight.set(text12, request);
|
|
18560
|
+
try {
|
|
18561
|
+
const embedding = await request;
|
|
18562
|
+
cache.set(text12, embedding);
|
|
18563
|
+
return Array.from(embedding);
|
|
18564
|
+
} finally {
|
|
18565
|
+
if (inFlight.get(text12) === request) inFlight.delete(text12);
|
|
18397
18566
|
}
|
|
18567
|
+
}
|
|
18568
|
+
async function requestEmbedding(text12, options, outer) {
|
|
18569
|
+
const { baseUrl = BASE_URL, model } = options;
|
|
18570
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_EMBEDDING_REQUEST_TIMEOUT_MS;
|
|
18398
18571
|
const headers = {
|
|
18399
18572
|
...await resolveAuthHeaders(options, outer),
|
|
18400
18573
|
...taskTypeHeader(options.taskType)
|
|
@@ -18429,11 +18602,7 @@ async function embedOne(text12, options, outer) {
|
|
|
18429
18602
|
totalTokens: response.data.usage.total_tokens ?? 0
|
|
18430
18603
|
});
|
|
18431
18604
|
}
|
|
18432
|
-
|
|
18433
|
-
if (cache) {
|
|
18434
|
-
cache.set(text12, f32Embedding);
|
|
18435
|
-
}
|
|
18436
|
-
return Array.from(f32Embedding);
|
|
18605
|
+
return Float32Array.from(embedding);
|
|
18437
18606
|
}
|
|
18438
18607
|
var DEFAULT_EMBEDDING_BATCH_SIZE = 100;
|
|
18439
18608
|
var DEFAULT_EMBEDDING_BATCH_CONCURRENCY = 3;
|
|
@@ -28856,9 +29025,9 @@ function recencyMultiplier(updatedAt, opts = {}) {
|
|
|
28856
29025
|
const updatedMs = updatedAt.getTime();
|
|
28857
29026
|
if (!Number.isFinite(updatedMs)) return noDate;
|
|
28858
29027
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
28859
|
-
const
|
|
28860
|
-
if (!Number.isFinite(
|
|
28861
|
-
const ageDays2 = Math.max(0, (
|
|
29028
|
+
const nowMs2 = now.getTime();
|
|
29029
|
+
if (!Number.isFinite(nowMs2)) return noDate;
|
|
29030
|
+
const ageDays2 = Math.max(0, (nowMs2 - updatedMs) / DAY_MS2);
|
|
28862
29031
|
const perYearDecay = opts.perYearDecay ?? DEFAULT_PER_YEAR_DECAY;
|
|
28863
29032
|
const floor = opts.floor ?? DEFAULT_FLOOR;
|
|
28864
29033
|
const decay = perYearDecay * (ageDays2 / 365);
|
|
@@ -29367,9 +29536,6 @@ function createVaultEmbeddingCache(maxSize = DEFAULT_VAULT_CACHE_SIZE) {
|
|
|
29367
29536
|
|
|
29368
29537
|
// src/lib/memoryVault/searchTool.ts
|
|
29369
29538
|
var DEFAULT_RERANK_TOP_N = 5;
|
|
29370
|
-
function nowMs() {
|
|
29371
|
-
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
29372
|
-
}
|
|
29373
29539
|
var warnedProgrammaticDecomposeIgnored = false;
|
|
29374
29540
|
function normalizeEventTimeKind(kind) {
|
|
29375
29541
|
return kind === "point" || kind === "range" || kind === "ongoing" ? kind : null;
|
|
@@ -32260,6 +32426,32 @@ function rankProfileCandidates(inputs, now = Date.now(), options = {}) {
|
|
|
32260
32426
|
return ranked;
|
|
32261
32427
|
}
|
|
32262
32428
|
|
|
32429
|
+
// src/lib/db/chat/embeddingCache.ts
|
|
32430
|
+
var MaskScopedEmbeddingCache = class extends Map {
|
|
32431
|
+
constructor(inner, masked) {
|
|
32432
|
+
super();
|
|
32433
|
+
this.inner = inner;
|
|
32434
|
+
this.masked = masked;
|
|
32435
|
+
}
|
|
32436
|
+
key(text12) {
|
|
32437
|
+
return `${this.masked ? "m" : "r"}:${text12}`;
|
|
32438
|
+
}
|
|
32439
|
+
get(text12) {
|
|
32440
|
+
return this.inner.get(this.key(text12));
|
|
32441
|
+
}
|
|
32442
|
+
set(text12, value) {
|
|
32443
|
+
this.inner.set(this.key(text12), value);
|
|
32444
|
+
return this;
|
|
32445
|
+
}
|
|
32446
|
+
};
|
|
32447
|
+
var scopedViews = /* @__PURE__ */ new WeakMap();
|
|
32448
|
+
function maskScopedEmbeddingCache(cache, masked) {
|
|
32449
|
+
const views = scopedViews.get(cache) ?? {};
|
|
32450
|
+
scopedViews.set(cache, views);
|
|
32451
|
+
const slot = masked ? "masked" : "raw";
|
|
32452
|
+
return views[slot] ?? (views[slot] = new MaskScopedEmbeddingCache(cache, masked));
|
|
32453
|
+
}
|
|
32454
|
+
|
|
32263
32455
|
// src/lib/memory/queryTemporal.ts
|
|
32264
32456
|
var RELATIVE_DAY_ENTRIES = [
|
|
32265
32457
|
[/\btoday\b/, "today", 0],
|
|
@@ -32467,9 +32659,6 @@ function scoreEventTimeOverlap(memoryStart, memoryEnd, memoryKind, window2) {
|
|
|
32467
32659
|
var DEFAULT_LIMIT = 8;
|
|
32468
32660
|
var DEFAULT_BUDGET = "low";
|
|
32469
32661
|
var DEFAULT_FACT_MIN_SCORE = 0.1;
|
|
32470
|
-
function nowMs2() {
|
|
32471
|
-
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
32472
|
-
}
|
|
32473
32662
|
var DEFAULT_CHUNK_MIN_SCORE = 0.5;
|
|
32474
32663
|
var DEFAULT_QUERY_EMBED_TOTAL_TIMEOUT_MS = 8e3;
|
|
32475
32664
|
function flagsForBudget(budget) {
|
|
@@ -32507,12 +32696,19 @@ async function recall(query, ctx, options = {}) {
|
|
|
32507
32696
|
const subQueries = normalizedFacets.length >= 2 ? normalizedFacets : void 0;
|
|
32508
32697
|
const embeddingOptions = {
|
|
32509
32698
|
...ctx.embeddingOptions,
|
|
32510
|
-
taskType: "memory_recall_embed"
|
|
32699
|
+
taskType: "memory_recall_embed",
|
|
32700
|
+
...options.embeddingCache && !ctx.embeddingOptions.cache && {
|
|
32701
|
+
cache: maskScopedEmbeddingCache(
|
|
32702
|
+
options.embeddingCache,
|
|
32703
|
+
Boolean(ctx.embeddingOptions.maskInput)
|
|
32704
|
+
)
|
|
32705
|
+
}
|
|
32511
32706
|
};
|
|
32512
|
-
const t0 =
|
|
32707
|
+
const t0 = nowMs();
|
|
32513
32708
|
let prepMs = 0;
|
|
32514
32709
|
let factLaneMs = 0;
|
|
32515
32710
|
let chunkLaneMs = 0;
|
|
32711
|
+
let chunkStats;
|
|
32516
32712
|
let fuseMs = 0;
|
|
32517
32713
|
const factResults = [];
|
|
32518
32714
|
const chunkResults = [];
|
|
@@ -32567,6 +32763,11 @@ async function recall(query, ctx, options = {}) {
|
|
|
32567
32763
|
...decryptLastRan !== void 0 && { decryptLast: decryptLastRan },
|
|
32568
32764
|
...vaultRowsDecrypted !== void 0 && { vaultRowsDecrypted },
|
|
32569
32765
|
...vaultRowsEmbedded !== void 0 && { vaultRowsEmbedded },
|
|
32766
|
+
...chunkStats && {
|
|
32767
|
+
chunkRowsScanned: chunkStats.rowsScanned,
|
|
32768
|
+
chunkRowsFetched: chunkStats.rowsFetched,
|
|
32769
|
+
chunkCacheHits: chunkStats.cacheHits
|
|
32770
|
+
},
|
|
32570
32771
|
factCount: factResults.length,
|
|
32571
32772
|
chunkCount: chunkResults.length,
|
|
32572
32773
|
admittedCount: admitted.length,
|
|
@@ -32578,7 +32779,7 @@ async function recall(query, ctx, options = {}) {
|
|
|
32578
32779
|
temporalLaneCount,
|
|
32579
32780
|
emptyReason: emptyReasonFor(admitted.length, laneRan),
|
|
32580
32781
|
timings: {
|
|
32581
|
-
total:
|
|
32782
|
+
total: nowMs() - t0,
|
|
32582
32783
|
prep: prepMs,
|
|
32583
32784
|
factLane: factLaneMs,
|
|
32584
32785
|
rerank: rerankMs,
|
|
@@ -32600,14 +32801,14 @@ async function recall(query, ctx, options = {}) {
|
|
|
32600
32801
|
const needsChunkEmbedding = types.includes("chunk") && ctx.storageCtx;
|
|
32601
32802
|
const wantsTemporal = types.includes("fact") && (ctx.factSource || ctx.vaultCtx);
|
|
32602
32803
|
const queryEmbedTotalTimeoutMs = options.queryEmbedTotalTimeoutMs ?? DEFAULT_QUERY_EMBED_TOTAL_TIMEOUT_MS;
|
|
32603
|
-
const prepStart =
|
|
32804
|
+
const prepStart = nowMs();
|
|
32604
32805
|
let sharedEmbedMs = 0;
|
|
32605
32806
|
const [queryEmbedding, entityRanking, temporalRanking] = await Promise.all([
|
|
32606
32807
|
needsChunkEmbedding ? generateEmbedding(query, {
|
|
32607
32808
|
...embeddingOptions,
|
|
32608
32809
|
totalTimeoutMs: queryEmbedTotalTimeoutMs
|
|
32609
32810
|
}).finally(() => {
|
|
32610
|
-
sharedEmbedMs =
|
|
32811
|
+
sharedEmbedMs = nowMs() - prepStart;
|
|
32611
32812
|
}).then((vec) => {
|
|
32612
32813
|
if (vec.length === 0) {
|
|
32613
32814
|
getLogger().warn(
|
|
@@ -32640,11 +32841,11 @@ async function recall(query, ctx, options = {}) {
|
|
|
32640
32841
|
() => ctx.factSource ? ctx.factSource.temporalRanking(query, options.now) : buildTemporalLaneRanking(query, ctx.vaultCtx, options.now)
|
|
32641
32842
|
) : Promise.resolve([])
|
|
32642
32843
|
]);
|
|
32643
|
-
prepMs =
|
|
32844
|
+
prepMs = nowMs() - prepStart;
|
|
32644
32845
|
graphLaneCount = entityRanking.length;
|
|
32645
32846
|
temporalLaneCount = temporalRanking.length;
|
|
32646
32847
|
if (types.includes("fact") && (ctx.factSource || ctx.vaultCtx && ctx.vaultCache)) {
|
|
32647
|
-
const factStart =
|
|
32848
|
+
const factStart = nowMs();
|
|
32648
32849
|
const vaultMinScore = options.minScore ?? DEFAULT_FACT_MIN_SCORE;
|
|
32649
32850
|
factFloor = vaultMinScore;
|
|
32650
32851
|
const {
|
|
@@ -32720,11 +32921,11 @@ async function recall(query, ctx, options = {}) {
|
|
|
32720
32921
|
factLaneRankedOnCosine = factRankedOnCosine;
|
|
32721
32922
|
decryptLastRan = factDecryptLast;
|
|
32722
32923
|
vaultRowsDecrypted = factRowsDecrypted;
|
|
32723
|
-
factLaneMs =
|
|
32924
|
+
factLaneMs = nowMs() - factStart;
|
|
32724
32925
|
}
|
|
32725
32926
|
if (chunkEmbedFailed && !factLaneRankedOnCosine) embeddingsUnavailable = true;
|
|
32726
32927
|
if (types.includes("chunk") && ctx.storageCtx && queryEmbedding) {
|
|
32727
|
-
const chunkStart =
|
|
32928
|
+
const chunkStart = nowMs();
|
|
32728
32929
|
const chunkMinScore = options.minScore ?? DEFAULT_CHUNK_MIN_SCORE;
|
|
32729
32930
|
chunkFloor = chunkMinScore;
|
|
32730
32931
|
const results = await searchChunksOp(ctx.storageCtx, queryEmbedding, {
|
|
@@ -32735,12 +32936,15 @@ async function recall(query, ctx, options = {}) {
|
|
|
32735
32936
|
...options.excludeConversationId && {
|
|
32736
32937
|
excludeConversationId: options.excludeConversationId
|
|
32737
32938
|
},
|
|
32738
|
-
...ctx.chunkCache && { chunkCache: ctx.chunkCache }
|
|
32939
|
+
...ctx.chunkCache && { chunkCache: ctx.chunkCache },
|
|
32940
|
+
onStats: (stats) => {
|
|
32941
|
+
chunkStats = stats;
|
|
32942
|
+
}
|
|
32739
32943
|
});
|
|
32740
32944
|
chunkResults.push(...dedupeBy(results, (r) => r.chunkText.trim()));
|
|
32741
|
-
chunkLaneMs =
|
|
32945
|
+
chunkLaneMs = nowMs() - chunkStart;
|
|
32742
32946
|
}
|
|
32743
|
-
const fuseStart =
|
|
32947
|
+
const fuseStart = nowMs();
|
|
32744
32948
|
if (types.length === 1 || factResults.length === 0 || chunkResults.length === 0) {
|
|
32745
32949
|
const memories2 = [
|
|
32746
32950
|
...factResults.map((r) => toFactMemory(r, options.now)),
|
|
@@ -32748,7 +32952,7 @@ async function recall(query, ctx, options = {}) {
|
|
|
32748
32952
|
];
|
|
32749
32953
|
memories2.sort((a, b) => b.score - a.score);
|
|
32750
32954
|
const candidateCount = factResults.length + chunkResults.length;
|
|
32751
|
-
fuseMs =
|
|
32955
|
+
fuseMs = nowMs() - fuseStart;
|
|
32752
32956
|
const admitted = memories2.slice(0, limit);
|
|
32753
32957
|
hitLimit = memories2.length > limit;
|
|
32754
32958
|
emitDiagnostics(candidateCount, admitted);
|
|
@@ -32812,7 +33016,7 @@ async function recall(query, ctx, options = {}) {
|
|
|
32812
33016
|
memories = selection.out;
|
|
32813
33017
|
}
|
|
32814
33018
|
hitLimit = selection.cut;
|
|
32815
|
-
fuseMs =
|
|
33019
|
+
fuseMs = nowMs() - fuseStart;
|
|
32816
33020
|
emitDiagnostics(byId.size, memories);
|
|
32817
33021
|
return {
|
|
32818
33022
|
memories,
|
|
@@ -332,6 +332,12 @@ interface RecallDiagnostics {
|
|
|
332
332
|
* LEGACY read path this batch is uncapped, so it can be the whole vault.
|
|
333
333
|
*/
|
|
334
334
|
vaultRowsEmbedded?: number;
|
|
335
|
+
/** Embedded messages the chunk lane considered; absent when the lane did not run. */
|
|
336
|
+
chunkRowsScanned?: number;
|
|
337
|
+
/** Messages whose vectors the chunk lane read from storage on a cache miss. */
|
|
338
|
+
chunkRowsFetched?: number;
|
|
339
|
+
/** Messages the chunk lane scored from the vector cache. */
|
|
340
|
+
chunkCacheHits?: number;
|
|
335
341
|
/** Facts the fact lane returned (post-dedupe, pre-fusion). */
|
|
336
342
|
factCount: number;
|
|
337
343
|
/** Chunks the chunk lane returned (post-dedupe, pre-fusion). */
|
|
@@ -332,6 +332,12 @@ interface RecallDiagnostics {
|
|
|
332
332
|
* LEGACY read path this batch is uncapped, so it can be the whole vault.
|
|
333
333
|
*/
|
|
334
334
|
vaultRowsEmbedded?: number;
|
|
335
|
+
/** Embedded messages the chunk lane considered; absent when the lane did not run. */
|
|
336
|
+
chunkRowsScanned?: number;
|
|
337
|
+
/** Messages whose vectors the chunk lane read from storage on a cache miss. */
|
|
338
|
+
chunkRowsFetched?: number;
|
|
339
|
+
/** Messages the chunk lane scored from the vector cache. */
|
|
340
|
+
chunkCacheHits?: number;
|
|
335
341
|
/** Facts the fact lane returned (post-dedupe, pre-fusion). */
|
|
336
342
|
factCount: number;
|
|
337
343
|
/** Chunks the chunk lane returned (post-dedupe, pre-fusion). */
|
|
@@ -1018,13 +1018,35 @@ async function generateEmbedding(text, options) {
|
|
|
1018
1018
|
if (total === void 0) return embedOne(text, options, void 0);
|
|
1019
1019
|
return withDeadline((signal) => embedOne(text, options, signal), total, "embedding");
|
|
1020
1020
|
}
|
|
1021
|
+
var inFlightByCache = /* @__PURE__ */ new WeakMap();
|
|
1021
1022
|
async function embedOne(text, options, outer) {
|
|
1022
|
-
const {
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1023
|
+
const { cache } = options;
|
|
1024
|
+
if (!cache) return Array.from(await requestEmbedding(text, options, outer));
|
|
1025
|
+
const cached = cache.get(text);
|
|
1026
|
+
if (cached) return Array.from(cached);
|
|
1027
|
+
const inFlight = inFlightByCache.get(cache) ?? /* @__PURE__ */ new Map();
|
|
1028
|
+
inFlightByCache.set(cache, inFlight);
|
|
1029
|
+
const pending = inFlight.get(text);
|
|
1030
|
+
if (pending) {
|
|
1031
|
+
try {
|
|
1032
|
+
return Array.from(await pending);
|
|
1033
|
+
} catch (err) {
|
|
1034
|
+
if (outer?.aborted) throw err;
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
const request = requestEmbedding(text, options, outer);
|
|
1038
|
+
inFlight.set(text, request);
|
|
1039
|
+
try {
|
|
1040
|
+
const embedding = await request;
|
|
1041
|
+
cache.set(text, embedding);
|
|
1042
|
+
return Array.from(embedding);
|
|
1043
|
+
} finally {
|
|
1044
|
+
if (inFlight.get(text) === request) inFlight.delete(text);
|
|
1027
1045
|
}
|
|
1046
|
+
}
|
|
1047
|
+
async function requestEmbedding(text, options, outer) {
|
|
1048
|
+
const { baseUrl = BASE_URL, model } = options;
|
|
1049
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_EMBEDDING_REQUEST_TIMEOUT_MS;
|
|
1028
1050
|
const headers = {
|
|
1029
1051
|
...await resolveAuthHeaders(options, outer),
|
|
1030
1052
|
...taskTypeHeader(options.taskType)
|
|
@@ -1059,11 +1081,7 @@ async function embedOne(text, options, outer) {
|
|
|
1059
1081
|
totalTokens: response.data.usage.total_tokens ?? 0
|
|
1060
1082
|
});
|
|
1061
1083
|
}
|
|
1062
|
-
|
|
1063
|
-
if (cache) {
|
|
1064
|
-
cache.set(text, f32Embedding);
|
|
1065
|
-
}
|
|
1066
|
-
return Array.from(f32Embedding);
|
|
1084
|
+
return Float32Array.from(embedding);
|
|
1067
1085
|
}
|
|
1068
1086
|
var DEFAULT_EMBEDDING_BATCH_SIZE = 100;
|
|
1069
1087
|
var DEFAULT_EMBEDDING_BATCH_CONCURRENCY = 3;
|