@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.cjs
CHANGED
|
@@ -17669,6 +17669,35 @@ function decodeChunkVector(vector, onMalformed) {
|
|
|
17669
17669
|
}
|
|
17670
17670
|
}
|
|
17671
17671
|
|
|
17672
|
+
// src/lib/scheduling.ts
|
|
17673
|
+
function nowMs() {
|
|
17674
|
+
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
17675
|
+
}
|
|
17676
|
+
function yieldToEventLoop() {
|
|
17677
|
+
const scheduler = globalThis.scheduler;
|
|
17678
|
+
if (typeof scheduler?.yield === "function") return scheduler.yield();
|
|
17679
|
+
if (typeof MessageChannel === "function") {
|
|
17680
|
+
return new Promise((resolve) => {
|
|
17681
|
+
const channel = new MessageChannel();
|
|
17682
|
+
channel.port1.onmessage = () => {
|
|
17683
|
+
channel.port1.close();
|
|
17684
|
+
channel.port2.close();
|
|
17685
|
+
resolve();
|
|
17686
|
+
};
|
|
17687
|
+
channel.port2.postMessage(null);
|
|
17688
|
+
});
|
|
17689
|
+
}
|
|
17690
|
+
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
17691
|
+
}
|
|
17692
|
+
function createTimeSliceYielder(budgetMs) {
|
|
17693
|
+
let sliceStart = nowMs();
|
|
17694
|
+
return async () => {
|
|
17695
|
+
if (nowMs() - sliceStart < budgetMs) return;
|
|
17696
|
+
await yieldToEventLoop();
|
|
17697
|
+
sliceStart = nowMs();
|
|
17698
|
+
};
|
|
17699
|
+
}
|
|
17700
|
+
|
|
17672
17701
|
// src/lib/db/encryption-utils.ts
|
|
17673
17702
|
init_logger();
|
|
17674
17703
|
var ENCRYPTION_PREFIX = "enc:v3:";
|
|
@@ -18816,6 +18845,95 @@ async function searchMessagesOp(ctx, queryVector, options) {
|
|
|
18816
18845
|
})
|
|
18817
18846
|
);
|
|
18818
18847
|
}
|
|
18848
|
+
var CHUNK_VECTOR_BATCH_SIZE = 50;
|
|
18849
|
+
var CHUNK_SCAN_YIELD_BUDGET_MS = 12;
|
|
18850
|
+
var hasEmbeddingSql = `(coalesce("chunks", '') != '' or coalesce("vector", '') != '')`;
|
|
18851
|
+
function toChunkCandidateKey(raw) {
|
|
18852
|
+
return {
|
|
18853
|
+
id: raw.id,
|
|
18854
|
+
conversationId: String(raw.conversation_id),
|
|
18855
|
+
version: Number(raw.updated_at) || 0,
|
|
18856
|
+
embeddingModel: raw.embedding_model ?? null
|
|
18857
|
+
};
|
|
18858
|
+
}
|
|
18859
|
+
async function fetchRowsByIds(ctx, ids) {
|
|
18860
|
+
if (ids.length === 0) return [];
|
|
18861
|
+
return await ctx.messagesCollection.query(import_watermelondb2.Q.where("id", import_watermelondb2.Q.oneOf(ids))).unsafeFetchRaw();
|
|
18862
|
+
}
|
|
18863
|
+
async function projectedChunkRowSource(ctx, filter) {
|
|
18864
|
+
const clauses = [
|
|
18865
|
+
`"history"."_status" is not 'deleted'`,
|
|
18866
|
+
hasEmbeddingSql,
|
|
18867
|
+
`exists (select 1 from "conversations" where "conversations"."conversation_id" = "history"."conversation_id" and "conversations"."is_deleted" = 0 and "conversations"."_status" is not 'deleted')`
|
|
18868
|
+
];
|
|
18869
|
+
const args = [];
|
|
18870
|
+
if (filter.conversationId !== void 0) {
|
|
18871
|
+
clauses.push(`"history"."conversation_id" = ?`);
|
|
18872
|
+
args.push(filter.conversationId);
|
|
18873
|
+
}
|
|
18874
|
+
if (filter.excludeConversationId !== void 0) {
|
|
18875
|
+
clauses.push(`"history"."conversation_id" != ?`);
|
|
18876
|
+
args.push(filter.excludeConversationId);
|
|
18877
|
+
}
|
|
18878
|
+
const keyRows = await ctx.messagesCollection.query(
|
|
18879
|
+
import_watermelondb2.Q.unsafeSqlQuery(
|
|
18880
|
+
`select "id", "conversation_id", "updated_at", "embedding_model" from "history" where ${clauses.join(" and ")}`,
|
|
18881
|
+
args
|
|
18882
|
+
)
|
|
18883
|
+
).unsafeFetchRaw();
|
|
18884
|
+
return {
|
|
18885
|
+
keys: keyRows.map(toChunkCandidateKey),
|
|
18886
|
+
loadVectorRows: async (ids) => {
|
|
18887
|
+
if (ids.length === 0) return [];
|
|
18888
|
+
return await ctx.messagesCollection.query(
|
|
18889
|
+
import_watermelondb2.Q.unsafeSqlQuery(
|
|
18890
|
+
`select "id", "updated_at", "chunks", "vector" from "history" where "_status" is not 'deleted' and "id" in (${ids.map(() => "?").join(",")})`,
|
|
18891
|
+
ids
|
|
18892
|
+
)
|
|
18893
|
+
).unsafeFetchRaw();
|
|
18894
|
+
},
|
|
18895
|
+
loadFullRows: (ids) => fetchRowsByIds(ctx, ids)
|
|
18896
|
+
};
|
|
18897
|
+
}
|
|
18898
|
+
async function fullScanChunkRowSource(ctx, filter) {
|
|
18899
|
+
const activeConversations = await ctx.conversationsCollection.query(import_watermelondb2.Q.where("is_deleted", false)).fetch();
|
|
18900
|
+
const activeConversationIds = new Set(
|
|
18901
|
+
activeConversations.map((c) => String(c._getRaw("conversation_id")))
|
|
18902
|
+
);
|
|
18903
|
+
const queryConditions = filter.conversationId !== void 0 ? [import_watermelondb2.Q.where("conversation_id", filter.conversationId)] : [];
|
|
18904
|
+
const rows = await ctx.messagesCollection.query(...queryConditions).unsafeFetchRaw();
|
|
18905
|
+
const rowsById = /* @__PURE__ */ new Map();
|
|
18906
|
+
for (const row of rows) {
|
|
18907
|
+
const conversationId = String(row.conversation_id);
|
|
18908
|
+
if (!activeConversationIds.has(conversationId)) continue;
|
|
18909
|
+
if (filter.excludeConversationId !== void 0 && conversationId === filter.excludeConversationId) {
|
|
18910
|
+
continue;
|
|
18911
|
+
}
|
|
18912
|
+
if (!row.chunks && !row.vector) continue;
|
|
18913
|
+
rowsById.set(row.id, row);
|
|
18914
|
+
}
|
|
18915
|
+
const pick = (ids) => Promise.resolve(
|
|
18916
|
+
ids.flatMap((id) => {
|
|
18917
|
+
const row = rowsById.get(id);
|
|
18918
|
+
return row ? [row] : [];
|
|
18919
|
+
})
|
|
18920
|
+
);
|
|
18921
|
+
return {
|
|
18922
|
+
keys: Array.from(rowsById.values(), toChunkCandidateKey),
|
|
18923
|
+
loadVectorRows: pick,
|
|
18924
|
+
loadFullRows: pick
|
|
18925
|
+
};
|
|
18926
|
+
}
|
|
18927
|
+
async function chunkRowSource(ctx, filter) {
|
|
18928
|
+
try {
|
|
18929
|
+
return await projectedChunkRowSource(ctx, filter);
|
|
18930
|
+
} catch (err) {
|
|
18931
|
+
getLogger().debug(
|
|
18932
|
+
"searchChunksOp: projected SQL unavailable, using full-load fallback: " + (err instanceof Error ? err.message : String(err))
|
|
18933
|
+
);
|
|
18934
|
+
return fullScanChunkRowSource(ctx, filter);
|
|
18935
|
+
}
|
|
18936
|
+
}
|
|
18819
18937
|
async function searchChunksOp(ctx, queryVector, options) {
|
|
18820
18938
|
const {
|
|
18821
18939
|
limit = 10,
|
|
@@ -18823,83 +18941,111 @@ async function searchChunksOp(ctx, queryVector, options) {
|
|
|
18823
18941
|
conversationId,
|
|
18824
18942
|
excludeConversationId,
|
|
18825
18943
|
embeddingModel,
|
|
18826
|
-
chunkCache
|
|
18944
|
+
chunkCache,
|
|
18945
|
+
onStats
|
|
18827
18946
|
} = options || {};
|
|
18828
|
-
const
|
|
18829
|
-
const
|
|
18830
|
-
activeConversations.map((c) => String(c._getRaw("conversation_id")))
|
|
18831
|
-
);
|
|
18832
|
-
const queryConditions = conversationId ? [import_watermelondb2.Q.where("conversation_id", conversationId)] : [];
|
|
18833
|
-
const messages = await ctx.messagesCollection.query(...queryConditions).unsafeFetchRaw();
|
|
18947
|
+
const source = await chunkRowSource(ctx, { conversationId, excludeConversationId });
|
|
18948
|
+
const yieldIfDue = createTimeSliceYielder(CHUNK_SCAN_YIELD_BUDGET_MS);
|
|
18834
18949
|
const candidates = [];
|
|
18835
18950
|
let staleSkipped = 0;
|
|
18836
18951
|
let unreadableVectors = 0;
|
|
18837
|
-
|
|
18838
|
-
|
|
18839
|
-
|
|
18840
|
-
|
|
18841
|
-
|
|
18842
|
-
|
|
18843
|
-
|
|
18844
|
-
|
|
18845
|
-
|
|
18846
|
-
}
|
|
18952
|
+
let cacheHits = 0;
|
|
18953
|
+
const missIds = [];
|
|
18954
|
+
const orderById = /* @__PURE__ */ new Map();
|
|
18955
|
+
for (const [order, key] of source.keys.entries()) {
|
|
18956
|
+
await yieldIfDue();
|
|
18957
|
+
orderById.set(key.id, order);
|
|
18958
|
+
if (embeddingModel && key.embeddingModel !== null && key.embeddingModel !== embeddingModel) {
|
|
18959
|
+
staleSkipped++;
|
|
18960
|
+
continue;
|
|
18847
18961
|
}
|
|
18848
|
-
const
|
|
18849
|
-
|
|
18850
|
-
|
|
18851
|
-
if (cached.chunks.length > 0) {
|
|
18852
|
-
for (let ci = 0; ci < cached.chunks.length; ci++) {
|
|
18853
|
-
const vec = cached.chunks[ci];
|
|
18854
|
-
if (vec.length === 0) continue;
|
|
18855
|
-
const similarity = cosineSimilarity(queryVector, vec);
|
|
18856
|
-
if (similarity >= minSimilarity) {
|
|
18857
|
-
candidates.push({
|
|
18858
|
-
message,
|
|
18859
|
-
similarity,
|
|
18860
|
-
chunkTextSource: { kind: "chunk-cached", chunkIndex: ci, version }
|
|
18861
|
-
});
|
|
18862
|
-
}
|
|
18863
|
-
}
|
|
18864
|
-
} else if (cached.fallback) {
|
|
18865
|
-
const similarity = cosineSimilarity(queryVector, cached.fallback);
|
|
18866
|
-
if (similarity >= minSimilarity) {
|
|
18867
|
-
candidates.push({ message, similarity, chunkTextSource: { kind: "message" } });
|
|
18868
|
-
}
|
|
18869
|
-
}
|
|
18962
|
+
const cached = chunkCache?.get(key.id);
|
|
18963
|
+
if (!cached || cached.version !== key.version) {
|
|
18964
|
+
missIds.push(key.id);
|
|
18870
18965
|
continue;
|
|
18871
18966
|
}
|
|
18872
|
-
|
|
18873
|
-
if (chunks
|
|
18874
|
-
|
|
18875
|
-
|
|
18876
|
-
unreadableVectors++;
|
|
18877
|
-
})
|
|
18878
|
-
);
|
|
18879
|
-
chunkCache?.set(message.id, { version, chunks: vectors });
|
|
18880
|
-
for (let ci = 0; ci < chunks.length; ci++) {
|
|
18881
|
-
const vec = vectors[ci];
|
|
18967
|
+
cacheHits++;
|
|
18968
|
+
if (cached.chunks.length > 0) {
|
|
18969
|
+
for (let ci = 0; ci < cached.chunks.length; ci++) {
|
|
18970
|
+
const vec = cached.chunks[ci];
|
|
18882
18971
|
if (vec.length === 0) continue;
|
|
18883
18972
|
const similarity = cosineSimilarity(queryVector, vec);
|
|
18884
18973
|
if (similarity >= minSimilarity) {
|
|
18885
18974
|
candidates.push({
|
|
18886
|
-
|
|
18975
|
+
id: key.id,
|
|
18976
|
+
version: key.version,
|
|
18977
|
+
order,
|
|
18887
18978
|
similarity,
|
|
18888
|
-
chunkTextSource: { kind: "chunk",
|
|
18979
|
+
chunkTextSource: { kind: "chunk-cached", chunkIndex: ci, version: key.version }
|
|
18889
18980
|
});
|
|
18890
18981
|
}
|
|
18891
18982
|
}
|
|
18892
|
-
} else {
|
|
18893
|
-
const
|
|
18894
|
-
if (!messageVector || messageVector.length === 0) continue;
|
|
18895
|
-
const fallback = Float32Array.from(messageVector);
|
|
18896
|
-
chunkCache?.set(message.id, { version, chunks: [], fallback });
|
|
18897
|
-
const similarity = cosineSimilarity(queryVector, fallback);
|
|
18983
|
+
} else if (cached.fallback) {
|
|
18984
|
+
const similarity = cosineSimilarity(queryVector, cached.fallback);
|
|
18898
18985
|
if (similarity >= minSimilarity) {
|
|
18899
|
-
candidates.push({
|
|
18986
|
+
candidates.push({
|
|
18987
|
+
id: key.id,
|
|
18988
|
+
version: key.version,
|
|
18989
|
+
order,
|
|
18990
|
+
similarity,
|
|
18991
|
+
chunkTextSource: { kind: "message" }
|
|
18992
|
+
});
|
|
18900
18993
|
}
|
|
18901
18994
|
}
|
|
18902
18995
|
}
|
|
18996
|
+
let rowsFetched = 0;
|
|
18997
|
+
for (let offset = 0; offset < missIds.length; offset += CHUNK_VECTOR_BATCH_SIZE) {
|
|
18998
|
+
const rows = await source.loadVectorRows(
|
|
18999
|
+
missIds.slice(offset, offset + CHUNK_VECTOR_BATCH_SIZE)
|
|
19000
|
+
);
|
|
19001
|
+
rowsFetched += rows.length;
|
|
19002
|
+
for (const row of rows) {
|
|
19003
|
+
await yieldIfDue();
|
|
19004
|
+
const id = row.id;
|
|
19005
|
+
const version = Number(row.updated_at) || 0;
|
|
19006
|
+
const order = orderById.get(id) ?? Number.MAX_SAFE_INTEGER;
|
|
19007
|
+
const chunks = await readRawJsonField(row, "chunks", ctx.walletAddress);
|
|
19008
|
+
if (chunks && chunks.length > 0) {
|
|
19009
|
+
const vectors = chunks.map(
|
|
19010
|
+
(chunk) => decodeChunkVector(chunk.vector, () => {
|
|
19011
|
+
unreadableVectors++;
|
|
19012
|
+
})
|
|
19013
|
+
);
|
|
19014
|
+
chunkCache?.set(id, { version, chunks: vectors });
|
|
19015
|
+
for (let ci = 0; ci < chunks.length; ci++) {
|
|
19016
|
+
const vec = vectors[ci];
|
|
19017
|
+
if (vec.length === 0) continue;
|
|
19018
|
+
const similarity = cosineSimilarity(queryVector, vec);
|
|
19019
|
+
if (similarity >= minSimilarity) {
|
|
19020
|
+
candidates.push({
|
|
19021
|
+
id,
|
|
19022
|
+
version,
|
|
19023
|
+
order,
|
|
19024
|
+
similarity,
|
|
19025
|
+
chunkTextSource: { kind: "chunk", chunk: chunks[ci], siblings: chunks }
|
|
19026
|
+
});
|
|
19027
|
+
}
|
|
19028
|
+
}
|
|
19029
|
+
} else {
|
|
19030
|
+
const messageVector = await readRawJsonField(row, "vector", ctx.walletAddress);
|
|
19031
|
+
if (messageVector && messageVector.length > 0) {
|
|
19032
|
+
const fallback = Float32Array.from(messageVector);
|
|
19033
|
+
chunkCache?.set(id, { version, chunks: [], fallback });
|
|
19034
|
+
const similarity = cosineSimilarity(queryVector, fallback);
|
|
19035
|
+
if (similarity >= minSimilarity) {
|
|
19036
|
+
candidates.push({
|
|
19037
|
+
id,
|
|
19038
|
+
version,
|
|
19039
|
+
order,
|
|
19040
|
+
similarity,
|
|
19041
|
+
chunkTextSource: { kind: "message" }
|
|
19042
|
+
});
|
|
19043
|
+
}
|
|
19044
|
+
}
|
|
19045
|
+
}
|
|
19046
|
+
}
|
|
19047
|
+
}
|
|
19048
|
+
onStats?.({ rowsScanned: source.keys.length, rowsFetched, cacheHits });
|
|
18903
19049
|
if (staleSkipped > 0) {
|
|
18904
19050
|
getLogger().warn(
|
|
18905
19051
|
`searchChunksOp: skipped ${staleSkipped} messages whose embedding model differs from the current model (${embeddingModel}) \u2014 re-embed via chunkAndEmbedAllMessages`
|
|
@@ -18910,12 +19056,17 @@ async function searchChunksOp(ctx, queryVector, options) {
|
|
|
18910
19056
|
`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`
|
|
18911
19057
|
);
|
|
18912
19058
|
}
|
|
18913
|
-
const topK = candidates.sort((a, b) => b.similarity - a.similarity).slice(0, limit);
|
|
18914
|
-
const
|
|
18915
|
-
|
|
19059
|
+
const topK = candidates.sort((a, b) => b.similarity - a.similarity || a.order - b.order).slice(0, limit);
|
|
19060
|
+
const topIds = Array.from(new Set(topK.map((c) => c.id)));
|
|
19061
|
+
const fullRows = await source.loadFullRows(topIds);
|
|
19062
|
+
const rowById = new Map(fullRows.map((row) => [row.id, row]));
|
|
19063
|
+
const liveTopK = topK.filter((c) => {
|
|
19064
|
+
const row = rowById.get(c.id);
|
|
19065
|
+
return row !== void 0 && (Number(row.updated_at) || 0) === c.version;
|
|
19066
|
+
});
|
|
18916
19067
|
const storedById = new Map(
|
|
18917
19068
|
await Promise.all(
|
|
18918
|
-
Array.from(
|
|
19069
|
+
Array.from(rowById, async ([id, m]) => {
|
|
18919
19070
|
const stored = await messageRawToStored(
|
|
18920
19071
|
m,
|
|
18921
19072
|
ctx.walletAddress,
|
|
@@ -18928,24 +19079,24 @@ async function searchChunksOp(ctx, queryVector, options) {
|
|
|
18928
19079
|
);
|
|
18929
19080
|
const chunkStateByMsgId = /* @__PURE__ */ new Map();
|
|
18930
19081
|
const needsChunkText = /* @__PURE__ */ new Set();
|
|
18931
|
-
for (const c of
|
|
18932
|
-
if (c.chunkTextSource.kind === "chunk-cached") needsChunkText.add(c.
|
|
19082
|
+
for (const c of liveTopK) {
|
|
19083
|
+
if (c.chunkTextSource.kind === "chunk-cached") needsChunkText.add(c.id);
|
|
18933
19084
|
}
|
|
18934
19085
|
await Promise.all(
|
|
18935
19086
|
Array.from(needsChunkText, async (id) => {
|
|
18936
|
-
const m =
|
|
19087
|
+
const m = rowById.get(id);
|
|
18937
19088
|
const parsed = await readRawJsonField(m, "chunks", ctx.walletAddress);
|
|
18938
19089
|
const version = Number(m.updated_at) || 0;
|
|
18939
19090
|
if (parsed) chunkStateByMsgId.set(id, { version, chunks: parsed });
|
|
18940
19091
|
})
|
|
18941
19092
|
);
|
|
18942
|
-
return
|
|
18943
|
-
const stored = { ...storedById.get(
|
|
19093
|
+
return liveTopK.map(({ id, similarity, chunkTextSource }) => {
|
|
19094
|
+
const stored = { ...storedById.get(id) };
|
|
18944
19095
|
let chunkText2;
|
|
18945
19096
|
if (chunkTextSource.kind === "chunk") {
|
|
18946
19097
|
chunkText2 = resolveChunkText(stored.content, chunkTextSource.chunk, chunkTextSource.siblings);
|
|
18947
19098
|
} else if (chunkTextSource.kind === "chunk-cached") {
|
|
18948
|
-
const state = chunkStateByMsgId.get(
|
|
19099
|
+
const state = chunkStateByMsgId.get(id);
|
|
18949
19100
|
const chunk = state && state.version === chunkTextSource.version ? state.chunks[chunkTextSource.chunkIndex] : void 0;
|
|
18950
19101
|
chunkText2 = resolveChunkText(stored.content, chunk, state?.chunks);
|
|
18951
19102
|
} else {
|
|
@@ -20047,13 +20198,35 @@ async function generateEmbedding(text12, options) {
|
|
|
20047
20198
|
if (total === void 0) return embedOne(text12, options, void 0);
|
|
20048
20199
|
return withDeadline((signal) => embedOne(text12, options, signal), total, "embedding");
|
|
20049
20200
|
}
|
|
20201
|
+
var inFlightByCache = /* @__PURE__ */ new WeakMap();
|
|
20050
20202
|
async function embedOne(text12, options, outer) {
|
|
20051
|
-
const {
|
|
20052
|
-
|
|
20053
|
-
|
|
20054
|
-
|
|
20055
|
-
|
|
20203
|
+
const { cache } = options;
|
|
20204
|
+
if (!cache) return Array.from(await requestEmbedding(text12, options, outer));
|
|
20205
|
+
const cached = cache.get(text12);
|
|
20206
|
+
if (cached) return Array.from(cached);
|
|
20207
|
+
const inFlight = inFlightByCache.get(cache) ?? /* @__PURE__ */ new Map();
|
|
20208
|
+
inFlightByCache.set(cache, inFlight);
|
|
20209
|
+
const pending = inFlight.get(text12);
|
|
20210
|
+
if (pending) {
|
|
20211
|
+
try {
|
|
20212
|
+
return Array.from(await pending);
|
|
20213
|
+
} catch (err) {
|
|
20214
|
+
if (outer?.aborted) throw err;
|
|
20215
|
+
}
|
|
20216
|
+
}
|
|
20217
|
+
const request = requestEmbedding(text12, options, outer);
|
|
20218
|
+
inFlight.set(text12, request);
|
|
20219
|
+
try {
|
|
20220
|
+
const embedding = await request;
|
|
20221
|
+
cache.set(text12, embedding);
|
|
20222
|
+
return Array.from(embedding);
|
|
20223
|
+
} finally {
|
|
20224
|
+
if (inFlight.get(text12) === request) inFlight.delete(text12);
|
|
20056
20225
|
}
|
|
20226
|
+
}
|
|
20227
|
+
async function requestEmbedding(text12, options, outer) {
|
|
20228
|
+
const { baseUrl = BASE_URL, model } = options;
|
|
20229
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_EMBEDDING_REQUEST_TIMEOUT_MS;
|
|
20057
20230
|
const headers = {
|
|
20058
20231
|
...await resolveAuthHeaders(options, outer),
|
|
20059
20232
|
...taskTypeHeader(options.taskType)
|
|
@@ -20088,11 +20261,7 @@ async function embedOne(text12, options, outer) {
|
|
|
20088
20261
|
totalTokens: response.data.usage.total_tokens ?? 0
|
|
20089
20262
|
});
|
|
20090
20263
|
}
|
|
20091
|
-
|
|
20092
|
-
if (cache) {
|
|
20093
|
-
cache.set(text12, f32Embedding);
|
|
20094
|
-
}
|
|
20095
|
-
return Array.from(f32Embedding);
|
|
20264
|
+
return Float32Array.from(embedding);
|
|
20096
20265
|
}
|
|
20097
20266
|
var DEFAULT_EMBEDDING_BATCH_SIZE = 100;
|
|
20098
20267
|
var DEFAULT_EMBEDDING_BATCH_CONCURRENCY = 3;
|
|
@@ -30556,9 +30725,9 @@ function recencyMultiplier(updatedAt, opts = {}) {
|
|
|
30556
30725
|
const updatedMs = updatedAt.getTime();
|
|
30557
30726
|
if (!Number.isFinite(updatedMs)) return noDate;
|
|
30558
30727
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
30559
|
-
const
|
|
30560
|
-
if (!Number.isFinite(
|
|
30561
|
-
const ageDays2 = Math.max(0, (
|
|
30728
|
+
const nowMs2 = now.getTime();
|
|
30729
|
+
if (!Number.isFinite(nowMs2)) return noDate;
|
|
30730
|
+
const ageDays2 = Math.max(0, (nowMs2 - updatedMs) / DAY_MS2);
|
|
30562
30731
|
const perYearDecay = opts.perYearDecay ?? DEFAULT_PER_YEAR_DECAY;
|
|
30563
30732
|
const floor = opts.floor ?? DEFAULT_FLOOR;
|
|
30564
30733
|
const decay = perYearDecay * (ageDays2 / 365);
|
|
@@ -31070,9 +31239,6 @@ function createVaultEmbeddingCache(maxSize = DEFAULT_VAULT_CACHE_SIZE) {
|
|
|
31070
31239
|
|
|
31071
31240
|
// src/lib/memoryVault/searchTool.ts
|
|
31072
31241
|
var DEFAULT_RERANK_TOP_N = 5;
|
|
31073
|
-
function nowMs() {
|
|
31074
|
-
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
31075
|
-
}
|
|
31076
31242
|
var warnedProgrammaticDecomposeIgnored = false;
|
|
31077
31243
|
function normalizeEventTimeKind(kind) {
|
|
31078
31244
|
return kind === "point" || kind === "range" || kind === "ongoing" ? kind : null;
|
|
@@ -33974,6 +34140,32 @@ function rankProfileCandidates(inputs, now = Date.now(), options = {}) {
|
|
|
33974
34140
|
return ranked;
|
|
33975
34141
|
}
|
|
33976
34142
|
|
|
34143
|
+
// src/lib/db/chat/embeddingCache.ts
|
|
34144
|
+
var MaskScopedEmbeddingCache = class extends Map {
|
|
34145
|
+
constructor(inner, masked) {
|
|
34146
|
+
super();
|
|
34147
|
+
this.inner = inner;
|
|
34148
|
+
this.masked = masked;
|
|
34149
|
+
}
|
|
34150
|
+
key(text12) {
|
|
34151
|
+
return `${this.masked ? "m" : "r"}:${text12}`;
|
|
34152
|
+
}
|
|
34153
|
+
get(text12) {
|
|
34154
|
+
return this.inner.get(this.key(text12));
|
|
34155
|
+
}
|
|
34156
|
+
set(text12, value) {
|
|
34157
|
+
this.inner.set(this.key(text12), value);
|
|
34158
|
+
return this;
|
|
34159
|
+
}
|
|
34160
|
+
};
|
|
34161
|
+
var scopedViews = /* @__PURE__ */ new WeakMap();
|
|
34162
|
+
function maskScopedEmbeddingCache(cache, masked) {
|
|
34163
|
+
const views = scopedViews.get(cache) ?? {};
|
|
34164
|
+
scopedViews.set(cache, views);
|
|
34165
|
+
const slot = masked ? "masked" : "raw";
|
|
34166
|
+
return views[slot] ?? (views[slot] = new MaskScopedEmbeddingCache(cache, masked));
|
|
34167
|
+
}
|
|
34168
|
+
|
|
33977
34169
|
// src/lib/memory/recall.ts
|
|
33978
34170
|
init_logger();
|
|
33979
34171
|
|
|
@@ -34184,9 +34376,6 @@ function scoreEventTimeOverlap(memoryStart, memoryEnd, memoryKind, window2) {
|
|
|
34184
34376
|
var DEFAULT_LIMIT = 8;
|
|
34185
34377
|
var DEFAULT_BUDGET = "low";
|
|
34186
34378
|
var DEFAULT_FACT_MIN_SCORE = 0.1;
|
|
34187
|
-
function nowMs2() {
|
|
34188
|
-
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
34189
|
-
}
|
|
34190
34379
|
var DEFAULT_CHUNK_MIN_SCORE = 0.5;
|
|
34191
34380
|
var DEFAULT_QUERY_EMBED_TOTAL_TIMEOUT_MS = 8e3;
|
|
34192
34381
|
function flagsForBudget(budget) {
|
|
@@ -34224,12 +34413,19 @@ async function recall(query, ctx, options = {}) {
|
|
|
34224
34413
|
const subQueries = normalizedFacets.length >= 2 ? normalizedFacets : void 0;
|
|
34225
34414
|
const embeddingOptions = {
|
|
34226
34415
|
...ctx.embeddingOptions,
|
|
34227
|
-
taskType: "memory_recall_embed"
|
|
34416
|
+
taskType: "memory_recall_embed",
|
|
34417
|
+
...options.embeddingCache && !ctx.embeddingOptions.cache && {
|
|
34418
|
+
cache: maskScopedEmbeddingCache(
|
|
34419
|
+
options.embeddingCache,
|
|
34420
|
+
Boolean(ctx.embeddingOptions.maskInput)
|
|
34421
|
+
)
|
|
34422
|
+
}
|
|
34228
34423
|
};
|
|
34229
|
-
const t0 =
|
|
34424
|
+
const t0 = nowMs();
|
|
34230
34425
|
let prepMs = 0;
|
|
34231
34426
|
let factLaneMs = 0;
|
|
34232
34427
|
let chunkLaneMs = 0;
|
|
34428
|
+
let chunkStats;
|
|
34233
34429
|
let fuseMs = 0;
|
|
34234
34430
|
const factResults = [];
|
|
34235
34431
|
const chunkResults = [];
|
|
@@ -34284,6 +34480,11 @@ async function recall(query, ctx, options = {}) {
|
|
|
34284
34480
|
...decryptLastRan !== void 0 && { decryptLast: decryptLastRan },
|
|
34285
34481
|
...vaultRowsDecrypted !== void 0 && { vaultRowsDecrypted },
|
|
34286
34482
|
...vaultRowsEmbedded !== void 0 && { vaultRowsEmbedded },
|
|
34483
|
+
...chunkStats && {
|
|
34484
|
+
chunkRowsScanned: chunkStats.rowsScanned,
|
|
34485
|
+
chunkRowsFetched: chunkStats.rowsFetched,
|
|
34486
|
+
chunkCacheHits: chunkStats.cacheHits
|
|
34487
|
+
},
|
|
34287
34488
|
factCount: factResults.length,
|
|
34288
34489
|
chunkCount: chunkResults.length,
|
|
34289
34490
|
admittedCount: admitted.length,
|
|
@@ -34295,7 +34496,7 @@ async function recall(query, ctx, options = {}) {
|
|
|
34295
34496
|
temporalLaneCount,
|
|
34296
34497
|
emptyReason: emptyReasonFor(admitted.length, laneRan),
|
|
34297
34498
|
timings: {
|
|
34298
|
-
total:
|
|
34499
|
+
total: nowMs() - t0,
|
|
34299
34500
|
prep: prepMs,
|
|
34300
34501
|
factLane: factLaneMs,
|
|
34301
34502
|
rerank: rerankMs,
|
|
@@ -34317,14 +34518,14 @@ async function recall(query, ctx, options = {}) {
|
|
|
34317
34518
|
const needsChunkEmbedding = types.includes("chunk") && ctx.storageCtx;
|
|
34318
34519
|
const wantsTemporal = types.includes("fact") && (ctx.factSource || ctx.vaultCtx);
|
|
34319
34520
|
const queryEmbedTotalTimeoutMs = options.queryEmbedTotalTimeoutMs ?? DEFAULT_QUERY_EMBED_TOTAL_TIMEOUT_MS;
|
|
34320
|
-
const prepStart =
|
|
34521
|
+
const prepStart = nowMs();
|
|
34321
34522
|
let sharedEmbedMs = 0;
|
|
34322
34523
|
const [queryEmbedding, entityRanking, temporalRanking] = await Promise.all([
|
|
34323
34524
|
needsChunkEmbedding ? generateEmbedding(query, {
|
|
34324
34525
|
...embeddingOptions,
|
|
34325
34526
|
totalTimeoutMs: queryEmbedTotalTimeoutMs
|
|
34326
34527
|
}).finally(() => {
|
|
34327
|
-
sharedEmbedMs =
|
|
34528
|
+
sharedEmbedMs = nowMs() - prepStart;
|
|
34328
34529
|
}).then((vec) => {
|
|
34329
34530
|
if (vec.length === 0) {
|
|
34330
34531
|
getLogger().warn(
|
|
@@ -34357,11 +34558,11 @@ async function recall(query, ctx, options = {}) {
|
|
|
34357
34558
|
() => ctx.factSource ? ctx.factSource.temporalRanking(query, options.now) : buildTemporalLaneRanking(query, ctx.vaultCtx, options.now)
|
|
34358
34559
|
) : Promise.resolve([])
|
|
34359
34560
|
]);
|
|
34360
|
-
prepMs =
|
|
34561
|
+
prepMs = nowMs() - prepStart;
|
|
34361
34562
|
graphLaneCount = entityRanking.length;
|
|
34362
34563
|
temporalLaneCount = temporalRanking.length;
|
|
34363
34564
|
if (types.includes("fact") && (ctx.factSource || ctx.vaultCtx && ctx.vaultCache)) {
|
|
34364
|
-
const factStart =
|
|
34565
|
+
const factStart = nowMs();
|
|
34365
34566
|
const vaultMinScore = options.minScore ?? DEFAULT_FACT_MIN_SCORE;
|
|
34366
34567
|
factFloor = vaultMinScore;
|
|
34367
34568
|
const {
|
|
@@ -34437,11 +34638,11 @@ async function recall(query, ctx, options = {}) {
|
|
|
34437
34638
|
factLaneRankedOnCosine = factRankedOnCosine;
|
|
34438
34639
|
decryptLastRan = factDecryptLast;
|
|
34439
34640
|
vaultRowsDecrypted = factRowsDecrypted;
|
|
34440
|
-
factLaneMs =
|
|
34641
|
+
factLaneMs = nowMs() - factStart;
|
|
34441
34642
|
}
|
|
34442
34643
|
if (chunkEmbedFailed && !factLaneRankedOnCosine) embeddingsUnavailable = true;
|
|
34443
34644
|
if (types.includes("chunk") && ctx.storageCtx && queryEmbedding) {
|
|
34444
|
-
const chunkStart =
|
|
34645
|
+
const chunkStart = nowMs();
|
|
34445
34646
|
const chunkMinScore = options.minScore ?? DEFAULT_CHUNK_MIN_SCORE;
|
|
34446
34647
|
chunkFloor = chunkMinScore;
|
|
34447
34648
|
const results = await searchChunksOp(ctx.storageCtx, queryEmbedding, {
|
|
@@ -34452,12 +34653,15 @@ async function recall(query, ctx, options = {}) {
|
|
|
34452
34653
|
...options.excludeConversationId && {
|
|
34453
34654
|
excludeConversationId: options.excludeConversationId
|
|
34454
34655
|
},
|
|
34455
|
-
...ctx.chunkCache && { chunkCache: ctx.chunkCache }
|
|
34656
|
+
...ctx.chunkCache && { chunkCache: ctx.chunkCache },
|
|
34657
|
+
onStats: (stats) => {
|
|
34658
|
+
chunkStats = stats;
|
|
34659
|
+
}
|
|
34456
34660
|
});
|
|
34457
34661
|
chunkResults.push(...dedupeBy(results, (r) => r.chunkText.trim()));
|
|
34458
|
-
chunkLaneMs =
|
|
34662
|
+
chunkLaneMs = nowMs() - chunkStart;
|
|
34459
34663
|
}
|
|
34460
|
-
const fuseStart =
|
|
34664
|
+
const fuseStart = nowMs();
|
|
34461
34665
|
if (types.length === 1 || factResults.length === 0 || chunkResults.length === 0) {
|
|
34462
34666
|
const memories2 = [
|
|
34463
34667
|
...factResults.map((r) => toFactMemory(r, options.now)),
|
|
@@ -34465,7 +34669,7 @@ async function recall(query, ctx, options = {}) {
|
|
|
34465
34669
|
];
|
|
34466
34670
|
memories2.sort((a, b) => b.score - a.score);
|
|
34467
34671
|
const candidateCount = factResults.length + chunkResults.length;
|
|
34468
|
-
fuseMs =
|
|
34672
|
+
fuseMs = nowMs() - fuseStart;
|
|
34469
34673
|
const admitted = memories2.slice(0, limit);
|
|
34470
34674
|
hitLimit = memories2.length > limit;
|
|
34471
34675
|
emitDiagnostics(candidateCount, admitted);
|
|
@@ -34529,7 +34733,7 @@ async function recall(query, ctx, options = {}) {
|
|
|
34529
34733
|
memories = selection.out;
|
|
34530
34734
|
}
|
|
34531
34735
|
hitLimit = selection.cut;
|
|
34532
|
-
fuseMs =
|
|
34736
|
+
fuseMs = nowMs() - fuseStart;
|
|
34533
34737
|
emitDiagnostics(byId.size, memories);
|
|
34534
34738
|
return {
|
|
34535
34739
|
memories,
|