@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/expo/index.mjs
CHANGED
|
@@ -1314,6 +1314,35 @@ function decodeChunkVector(vector, onMalformed) {
|
|
|
1314
1314
|
}
|
|
1315
1315
|
}
|
|
1316
1316
|
|
|
1317
|
+
// src/lib/scheduling.ts
|
|
1318
|
+
function nowMs() {
|
|
1319
|
+
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
1320
|
+
}
|
|
1321
|
+
function yieldToEventLoop() {
|
|
1322
|
+
const scheduler = globalThis.scheduler;
|
|
1323
|
+
if (typeof scheduler?.yield === "function") return scheduler.yield();
|
|
1324
|
+
if (typeof MessageChannel === "function") {
|
|
1325
|
+
return new Promise((resolve) => {
|
|
1326
|
+
const channel = new MessageChannel();
|
|
1327
|
+
channel.port1.onmessage = () => {
|
|
1328
|
+
channel.port1.close();
|
|
1329
|
+
channel.port2.close();
|
|
1330
|
+
resolve();
|
|
1331
|
+
};
|
|
1332
|
+
channel.port2.postMessage(null);
|
|
1333
|
+
});
|
|
1334
|
+
}
|
|
1335
|
+
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
1336
|
+
}
|
|
1337
|
+
function createTimeSliceYielder(budgetMs) {
|
|
1338
|
+
let sliceStart = nowMs();
|
|
1339
|
+
return async () => {
|
|
1340
|
+
if (nowMs() - sliceStart < budgetMs) return;
|
|
1341
|
+
await yieldToEventLoop();
|
|
1342
|
+
sliceStart = nowMs();
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1317
1346
|
// src/lib/db/encryption-utils.ts
|
|
1318
1347
|
var ENCRYPTION_PREFIX = "enc:v3:";
|
|
1319
1348
|
function isEncrypted(value) {
|
|
@@ -2647,6 +2676,95 @@ async function readRawJsonField(raw, column, walletAddress) {
|
|
|
2647
2676
|
return void 0;
|
|
2648
2677
|
}
|
|
2649
2678
|
}
|
|
2679
|
+
var CHUNK_VECTOR_BATCH_SIZE = 50;
|
|
2680
|
+
var CHUNK_SCAN_YIELD_BUDGET_MS = 12;
|
|
2681
|
+
var hasEmbeddingSql = `(coalesce("chunks", '') != '' or coalesce("vector", '') != '')`;
|
|
2682
|
+
function toChunkCandidateKey(raw) {
|
|
2683
|
+
return {
|
|
2684
|
+
id: raw.id,
|
|
2685
|
+
conversationId: String(raw.conversation_id),
|
|
2686
|
+
version: Number(raw.updated_at) || 0,
|
|
2687
|
+
embeddingModel: raw.embedding_model ?? null
|
|
2688
|
+
};
|
|
2689
|
+
}
|
|
2690
|
+
async function fetchRowsByIds(ctx, ids) {
|
|
2691
|
+
if (ids.length === 0) return [];
|
|
2692
|
+
return await ctx.messagesCollection.query(Q.where("id", Q.oneOf(ids))).unsafeFetchRaw();
|
|
2693
|
+
}
|
|
2694
|
+
async function projectedChunkRowSource(ctx, filter) {
|
|
2695
|
+
const clauses = [
|
|
2696
|
+
`"history"."_status" is not 'deleted'`,
|
|
2697
|
+
hasEmbeddingSql,
|
|
2698
|
+
`exists (select 1 from "conversations" where "conversations"."conversation_id" = "history"."conversation_id" and "conversations"."is_deleted" = 0 and "conversations"."_status" is not 'deleted')`
|
|
2699
|
+
];
|
|
2700
|
+
const args = [];
|
|
2701
|
+
if (filter.conversationId !== void 0) {
|
|
2702
|
+
clauses.push(`"history"."conversation_id" = ?`);
|
|
2703
|
+
args.push(filter.conversationId);
|
|
2704
|
+
}
|
|
2705
|
+
if (filter.excludeConversationId !== void 0) {
|
|
2706
|
+
clauses.push(`"history"."conversation_id" != ?`);
|
|
2707
|
+
args.push(filter.excludeConversationId);
|
|
2708
|
+
}
|
|
2709
|
+
const keyRows = await ctx.messagesCollection.query(
|
|
2710
|
+
Q.unsafeSqlQuery(
|
|
2711
|
+
`select "id", "conversation_id", "updated_at", "embedding_model" from "history" where ${clauses.join(" and ")}`,
|
|
2712
|
+
args
|
|
2713
|
+
)
|
|
2714
|
+
).unsafeFetchRaw();
|
|
2715
|
+
return {
|
|
2716
|
+
keys: keyRows.map(toChunkCandidateKey),
|
|
2717
|
+
loadVectorRows: async (ids) => {
|
|
2718
|
+
if (ids.length === 0) return [];
|
|
2719
|
+
return await ctx.messagesCollection.query(
|
|
2720
|
+
Q.unsafeSqlQuery(
|
|
2721
|
+
`select "id", "updated_at", "chunks", "vector" from "history" where "_status" is not 'deleted' and "id" in (${ids.map(() => "?").join(",")})`,
|
|
2722
|
+
ids
|
|
2723
|
+
)
|
|
2724
|
+
).unsafeFetchRaw();
|
|
2725
|
+
},
|
|
2726
|
+
loadFullRows: (ids) => fetchRowsByIds(ctx, ids)
|
|
2727
|
+
};
|
|
2728
|
+
}
|
|
2729
|
+
async function fullScanChunkRowSource(ctx, filter) {
|
|
2730
|
+
const activeConversations = await ctx.conversationsCollection.query(Q.where("is_deleted", false)).fetch();
|
|
2731
|
+
const activeConversationIds = new Set(
|
|
2732
|
+
activeConversations.map((c) => String(c._getRaw("conversation_id")))
|
|
2733
|
+
);
|
|
2734
|
+
const queryConditions = filter.conversationId !== void 0 ? [Q.where("conversation_id", filter.conversationId)] : [];
|
|
2735
|
+
const rows = await ctx.messagesCollection.query(...queryConditions).unsafeFetchRaw();
|
|
2736
|
+
const rowsById = /* @__PURE__ */ new Map();
|
|
2737
|
+
for (const row of rows) {
|
|
2738
|
+
const conversationId = String(row.conversation_id);
|
|
2739
|
+
if (!activeConversationIds.has(conversationId)) continue;
|
|
2740
|
+
if (filter.excludeConversationId !== void 0 && conversationId === filter.excludeConversationId) {
|
|
2741
|
+
continue;
|
|
2742
|
+
}
|
|
2743
|
+
if (!row.chunks && !row.vector) continue;
|
|
2744
|
+
rowsById.set(row.id, row);
|
|
2745
|
+
}
|
|
2746
|
+
const pick = (ids) => Promise.resolve(
|
|
2747
|
+
ids.flatMap((id) => {
|
|
2748
|
+
const row = rowsById.get(id);
|
|
2749
|
+
return row ? [row] : [];
|
|
2750
|
+
})
|
|
2751
|
+
);
|
|
2752
|
+
return {
|
|
2753
|
+
keys: Array.from(rowsById.values(), toChunkCandidateKey),
|
|
2754
|
+
loadVectorRows: pick,
|
|
2755
|
+
loadFullRows: pick
|
|
2756
|
+
};
|
|
2757
|
+
}
|
|
2758
|
+
async function chunkRowSource(ctx, filter) {
|
|
2759
|
+
try {
|
|
2760
|
+
return await projectedChunkRowSource(ctx, filter);
|
|
2761
|
+
} catch (err) {
|
|
2762
|
+
getLogger().debug(
|
|
2763
|
+
"searchChunksOp: projected SQL unavailable, using full-load fallback: " + (err instanceof Error ? err.message : String(err))
|
|
2764
|
+
);
|
|
2765
|
+
return fullScanChunkRowSource(ctx, filter);
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2650
2768
|
async function searchChunksOp(ctx, queryVector, options) {
|
|
2651
2769
|
const {
|
|
2652
2770
|
limit = 10,
|
|
@@ -2654,83 +2772,111 @@ async function searchChunksOp(ctx, queryVector, options) {
|
|
|
2654
2772
|
conversationId,
|
|
2655
2773
|
excludeConversationId,
|
|
2656
2774
|
embeddingModel,
|
|
2657
|
-
chunkCache
|
|
2775
|
+
chunkCache,
|
|
2776
|
+
onStats
|
|
2658
2777
|
} = options || {};
|
|
2659
|
-
const
|
|
2660
|
-
const
|
|
2661
|
-
activeConversations.map((c) => String(c._getRaw("conversation_id")))
|
|
2662
|
-
);
|
|
2663
|
-
const queryConditions = conversationId ? [Q.where("conversation_id", conversationId)] : [];
|
|
2664
|
-
const messages = await ctx.messagesCollection.query(...queryConditions).unsafeFetchRaw();
|
|
2778
|
+
const source = await chunkRowSource(ctx, { conversationId, excludeConversationId });
|
|
2779
|
+
const yieldIfDue = createTimeSliceYielder(CHUNK_SCAN_YIELD_BUDGET_MS);
|
|
2665
2780
|
const candidates = [];
|
|
2666
2781
|
let staleSkipped = 0;
|
|
2667
2782
|
let unreadableVectors = 0;
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
}
|
|
2783
|
+
let cacheHits = 0;
|
|
2784
|
+
const missIds = [];
|
|
2785
|
+
const orderById = /* @__PURE__ */ new Map();
|
|
2786
|
+
for (const [order, key] of source.keys.entries()) {
|
|
2787
|
+
await yieldIfDue();
|
|
2788
|
+
orderById.set(key.id, order);
|
|
2789
|
+
if (embeddingModel && key.embeddingModel !== null && key.embeddingModel !== embeddingModel) {
|
|
2790
|
+
staleSkipped++;
|
|
2791
|
+
continue;
|
|
2678
2792
|
}
|
|
2679
|
-
const
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
if (cached.chunks.length > 0) {
|
|
2683
|
-
for (let ci = 0; ci < cached.chunks.length; ci++) {
|
|
2684
|
-
const vec = cached.chunks[ci];
|
|
2685
|
-
if (vec.length === 0) continue;
|
|
2686
|
-
const similarity = cosineSimilarity(queryVector, vec);
|
|
2687
|
-
if (similarity >= minSimilarity) {
|
|
2688
|
-
candidates.push({
|
|
2689
|
-
message,
|
|
2690
|
-
similarity,
|
|
2691
|
-
chunkTextSource: { kind: "chunk-cached", chunkIndex: ci, version }
|
|
2692
|
-
});
|
|
2693
|
-
}
|
|
2694
|
-
}
|
|
2695
|
-
} else if (cached.fallback) {
|
|
2696
|
-
const similarity = cosineSimilarity(queryVector, cached.fallback);
|
|
2697
|
-
if (similarity >= minSimilarity) {
|
|
2698
|
-
candidates.push({ message, similarity, chunkTextSource: { kind: "message" } });
|
|
2699
|
-
}
|
|
2700
|
-
}
|
|
2793
|
+
const cached = chunkCache?.get(key.id);
|
|
2794
|
+
if (!cached || cached.version !== key.version) {
|
|
2795
|
+
missIds.push(key.id);
|
|
2701
2796
|
continue;
|
|
2702
2797
|
}
|
|
2703
|
-
|
|
2704
|
-
if (
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
unreadableVectors++;
|
|
2708
|
-
})
|
|
2709
|
-
);
|
|
2710
|
-
chunkCache?.set(message.id, { version, chunks: vectors });
|
|
2711
|
-
for (let ci = 0; ci < chunks3.length; ci++) {
|
|
2712
|
-
const vec = vectors[ci];
|
|
2798
|
+
cacheHits++;
|
|
2799
|
+
if (cached.chunks.length > 0) {
|
|
2800
|
+
for (let ci = 0; ci < cached.chunks.length; ci++) {
|
|
2801
|
+
const vec = cached.chunks[ci];
|
|
2713
2802
|
if (vec.length === 0) continue;
|
|
2714
2803
|
const similarity = cosineSimilarity(queryVector, vec);
|
|
2715
2804
|
if (similarity >= minSimilarity) {
|
|
2716
2805
|
candidates.push({
|
|
2717
|
-
|
|
2806
|
+
id: key.id,
|
|
2807
|
+
version: key.version,
|
|
2808
|
+
order,
|
|
2718
2809
|
similarity,
|
|
2719
|
-
chunkTextSource: { kind: "chunk",
|
|
2810
|
+
chunkTextSource: { kind: "chunk-cached", chunkIndex: ci, version: key.version }
|
|
2720
2811
|
});
|
|
2721
2812
|
}
|
|
2722
2813
|
}
|
|
2723
|
-
} else {
|
|
2724
|
-
const
|
|
2725
|
-
if (!messageVector || messageVector.length === 0) continue;
|
|
2726
|
-
const fallback = Float32Array.from(messageVector);
|
|
2727
|
-
chunkCache?.set(message.id, { version, chunks: [], fallback });
|
|
2728
|
-
const similarity = cosineSimilarity(queryVector, fallback);
|
|
2814
|
+
} else if (cached.fallback) {
|
|
2815
|
+
const similarity = cosineSimilarity(queryVector, cached.fallback);
|
|
2729
2816
|
if (similarity >= minSimilarity) {
|
|
2730
|
-
candidates.push({
|
|
2817
|
+
candidates.push({
|
|
2818
|
+
id: key.id,
|
|
2819
|
+
version: key.version,
|
|
2820
|
+
order,
|
|
2821
|
+
similarity,
|
|
2822
|
+
chunkTextSource: { kind: "message" }
|
|
2823
|
+
});
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
let rowsFetched = 0;
|
|
2828
|
+
for (let offset = 0; offset < missIds.length; offset += CHUNK_VECTOR_BATCH_SIZE) {
|
|
2829
|
+
const rows = await source.loadVectorRows(
|
|
2830
|
+
missIds.slice(offset, offset + CHUNK_VECTOR_BATCH_SIZE)
|
|
2831
|
+
);
|
|
2832
|
+
rowsFetched += rows.length;
|
|
2833
|
+
for (const row of rows) {
|
|
2834
|
+
await yieldIfDue();
|
|
2835
|
+
const id = row.id;
|
|
2836
|
+
const version = Number(row.updated_at) || 0;
|
|
2837
|
+
const order = orderById.get(id) ?? Number.MAX_SAFE_INTEGER;
|
|
2838
|
+
const chunks3 = await readRawJsonField(row, "chunks", ctx.walletAddress);
|
|
2839
|
+
if (chunks3 && chunks3.length > 0) {
|
|
2840
|
+
const vectors = chunks3.map(
|
|
2841
|
+
(chunk) => decodeChunkVector(chunk.vector, () => {
|
|
2842
|
+
unreadableVectors++;
|
|
2843
|
+
})
|
|
2844
|
+
);
|
|
2845
|
+
chunkCache?.set(id, { version, chunks: vectors });
|
|
2846
|
+
for (let ci = 0; ci < chunks3.length; ci++) {
|
|
2847
|
+
const vec = vectors[ci];
|
|
2848
|
+
if (vec.length === 0) continue;
|
|
2849
|
+
const similarity = cosineSimilarity(queryVector, vec);
|
|
2850
|
+
if (similarity >= minSimilarity) {
|
|
2851
|
+
candidates.push({
|
|
2852
|
+
id,
|
|
2853
|
+
version,
|
|
2854
|
+
order,
|
|
2855
|
+
similarity,
|
|
2856
|
+
chunkTextSource: { kind: "chunk", chunk: chunks3[ci], siblings: chunks3 }
|
|
2857
|
+
});
|
|
2858
|
+
}
|
|
2859
|
+
}
|
|
2860
|
+
} else {
|
|
2861
|
+
const messageVector = await readRawJsonField(row, "vector", ctx.walletAddress);
|
|
2862
|
+
if (messageVector && messageVector.length > 0) {
|
|
2863
|
+
const fallback = Float32Array.from(messageVector);
|
|
2864
|
+
chunkCache?.set(id, { version, chunks: [], fallback });
|
|
2865
|
+
const similarity = cosineSimilarity(queryVector, fallback);
|
|
2866
|
+
if (similarity >= minSimilarity) {
|
|
2867
|
+
candidates.push({
|
|
2868
|
+
id,
|
|
2869
|
+
version,
|
|
2870
|
+
order,
|
|
2871
|
+
similarity,
|
|
2872
|
+
chunkTextSource: { kind: "message" }
|
|
2873
|
+
});
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2731
2876
|
}
|
|
2732
2877
|
}
|
|
2733
2878
|
}
|
|
2879
|
+
onStats?.({ rowsScanned: source.keys.length, rowsFetched, cacheHits });
|
|
2734
2880
|
if (staleSkipped > 0) {
|
|
2735
2881
|
getLogger().warn(
|
|
2736
2882
|
`searchChunksOp: skipped ${staleSkipped} messages whose embedding model differs from the current model (${embeddingModel}) \u2014 re-embed via chunkAndEmbedAllMessages`
|
|
@@ -2741,12 +2887,17 @@ async function searchChunksOp(ctx, queryVector, options) {
|
|
|
2741
2887
|
`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`
|
|
2742
2888
|
);
|
|
2743
2889
|
}
|
|
2744
|
-
const topK = candidates.sort((a, b) => b.similarity - a.similarity).slice(0, limit);
|
|
2745
|
-
const
|
|
2746
|
-
|
|
2890
|
+
const topK = candidates.sort((a, b) => b.similarity - a.similarity || a.order - b.order).slice(0, limit);
|
|
2891
|
+
const topIds = Array.from(new Set(topK.map((c) => c.id)));
|
|
2892
|
+
const fullRows = await source.loadFullRows(topIds);
|
|
2893
|
+
const rowById = new Map(fullRows.map((row) => [row.id, row]));
|
|
2894
|
+
const liveTopK = topK.filter((c) => {
|
|
2895
|
+
const row = rowById.get(c.id);
|
|
2896
|
+
return row !== void 0 && (Number(row.updated_at) || 0) === c.version;
|
|
2897
|
+
});
|
|
2747
2898
|
const storedById = new Map(
|
|
2748
2899
|
await Promise.all(
|
|
2749
|
-
Array.from(
|
|
2900
|
+
Array.from(rowById, async ([id, m]) => {
|
|
2750
2901
|
const stored2 = await messageRawToStored(
|
|
2751
2902
|
m,
|
|
2752
2903
|
ctx.walletAddress,
|
|
@@ -2759,24 +2910,24 @@ async function searchChunksOp(ctx, queryVector, options) {
|
|
|
2759
2910
|
);
|
|
2760
2911
|
const chunkStateByMsgId = /* @__PURE__ */ new Map();
|
|
2761
2912
|
const needsChunkText = /* @__PURE__ */ new Set();
|
|
2762
|
-
for (const c of
|
|
2763
|
-
if (c.chunkTextSource.kind === "chunk-cached") needsChunkText.add(c.
|
|
2913
|
+
for (const c of liveTopK) {
|
|
2914
|
+
if (c.chunkTextSource.kind === "chunk-cached") needsChunkText.add(c.id);
|
|
2764
2915
|
}
|
|
2765
2916
|
await Promise.all(
|
|
2766
2917
|
Array.from(needsChunkText, async (id) => {
|
|
2767
|
-
const m =
|
|
2918
|
+
const m = rowById.get(id);
|
|
2768
2919
|
const parsed = await readRawJsonField(m, "chunks", ctx.walletAddress);
|
|
2769
2920
|
const version = Number(m.updated_at) || 0;
|
|
2770
2921
|
if (parsed) chunkStateByMsgId.set(id, { version, chunks: parsed });
|
|
2771
2922
|
})
|
|
2772
2923
|
);
|
|
2773
|
-
return
|
|
2774
|
-
const stored2 = { ...storedById.get(
|
|
2924
|
+
return liveTopK.map(({ id, similarity, chunkTextSource }) => {
|
|
2925
|
+
const stored2 = { ...storedById.get(id) };
|
|
2775
2926
|
let chunkText2;
|
|
2776
2927
|
if (chunkTextSource.kind === "chunk") {
|
|
2777
2928
|
chunkText2 = resolveChunkText(stored2.content, chunkTextSource.chunk, chunkTextSource.siblings);
|
|
2778
2929
|
} else if (chunkTextSource.kind === "chunk-cached") {
|
|
2779
|
-
const state = chunkStateByMsgId.get(
|
|
2930
|
+
const state = chunkStateByMsgId.get(id);
|
|
2780
2931
|
const chunk = state && state.version === chunkTextSource.version ? state.chunks[chunkTextSource.chunkIndex] : void 0;
|
|
2781
2932
|
chunkText2 = resolveChunkText(stored2.content, chunk, state?.chunks);
|
|
2782
2933
|
} else {
|
|
@@ -3855,13 +4006,35 @@ async function generateEmbedding(text14, options) {
|
|
|
3855
4006
|
if (total === void 0) return embedOne(text14, options, void 0);
|
|
3856
4007
|
return withDeadline((signal) => embedOne(text14, options, signal), total, "embedding");
|
|
3857
4008
|
}
|
|
4009
|
+
var inFlightByCache = /* @__PURE__ */ new WeakMap();
|
|
3858
4010
|
async function embedOne(text14, options, outer) {
|
|
3859
|
-
const {
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
4011
|
+
const { cache } = options;
|
|
4012
|
+
if (!cache) return Array.from(await requestEmbedding(text14, options, outer));
|
|
4013
|
+
const cached = cache.get(text14);
|
|
4014
|
+
if (cached) return Array.from(cached);
|
|
4015
|
+
const inFlight = inFlightByCache.get(cache) ?? /* @__PURE__ */ new Map();
|
|
4016
|
+
inFlightByCache.set(cache, inFlight);
|
|
4017
|
+
const pending = inFlight.get(text14);
|
|
4018
|
+
if (pending) {
|
|
4019
|
+
try {
|
|
4020
|
+
return Array.from(await pending);
|
|
4021
|
+
} catch (err) {
|
|
4022
|
+
if (outer?.aborted) throw err;
|
|
4023
|
+
}
|
|
3864
4024
|
}
|
|
4025
|
+
const request = requestEmbedding(text14, options, outer);
|
|
4026
|
+
inFlight.set(text14, request);
|
|
4027
|
+
try {
|
|
4028
|
+
const embedding2 = await request;
|
|
4029
|
+
cache.set(text14, embedding2);
|
|
4030
|
+
return Array.from(embedding2);
|
|
4031
|
+
} finally {
|
|
4032
|
+
if (inFlight.get(text14) === request) inFlight.delete(text14);
|
|
4033
|
+
}
|
|
4034
|
+
}
|
|
4035
|
+
async function requestEmbedding(text14, options, outer) {
|
|
4036
|
+
const { baseUrl = BASE_URL, model } = options;
|
|
4037
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_EMBEDDING_REQUEST_TIMEOUT_MS;
|
|
3865
4038
|
const headers = {
|
|
3866
4039
|
...await resolveAuthHeaders(options, outer),
|
|
3867
4040
|
...taskTypeHeader(options.taskType)
|
|
@@ -3896,11 +4069,7 @@ async function embedOne(text14, options, outer) {
|
|
|
3896
4069
|
totalTokens: response.data.usage.total_tokens ?? 0
|
|
3897
4070
|
});
|
|
3898
4071
|
}
|
|
3899
|
-
|
|
3900
|
-
if (cache) {
|
|
3901
|
-
cache.set(text14, f32Embedding);
|
|
3902
|
-
}
|
|
3903
|
-
return Array.from(f32Embedding);
|
|
4072
|
+
return Float32Array.from(embedding2);
|
|
3904
4073
|
}
|
|
3905
4074
|
var DEFAULT_EMBEDDING_BATCH_SIZE = 100;
|
|
3906
4075
|
var DEFAULT_EMBEDDING_BATCH_CONCURRENCY = 3;
|
|
@@ -9382,8 +9551,12 @@ var MaskScopedEmbeddingCache = class extends Map {
|
|
|
9382
9551
|
return this;
|
|
9383
9552
|
}
|
|
9384
9553
|
};
|
|
9554
|
+
var scopedViews = /* @__PURE__ */ new WeakMap();
|
|
9385
9555
|
function maskScopedEmbeddingCache(cache, masked) {
|
|
9386
|
-
|
|
9556
|
+
const views = scopedViews.get(cache) ?? {};
|
|
9557
|
+
scopedViews.set(cache, views);
|
|
9558
|
+
const slot = masked ? "masked" : "raw";
|
|
9559
|
+
return views[slot] ?? (views[slot] = new MaskScopedEmbeddingCache(cache, masked));
|
|
9387
9560
|
}
|
|
9388
9561
|
|
|
9389
9562
|
// src/lib/db/conversationMemory/models.ts
|
|
@@ -13727,9 +13900,9 @@ function recencyMultiplier(updatedAt, opts = {}) {
|
|
|
13727
13900
|
const updatedMs = updatedAt.getTime();
|
|
13728
13901
|
if (!Number.isFinite(updatedMs)) return noDate;
|
|
13729
13902
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
13730
|
-
const
|
|
13731
|
-
if (!Number.isFinite(
|
|
13732
|
-
const ageDays2 = Math.max(0, (
|
|
13903
|
+
const nowMs2 = now.getTime();
|
|
13904
|
+
if (!Number.isFinite(nowMs2)) return noDate;
|
|
13905
|
+
const ageDays2 = Math.max(0, (nowMs2 - updatedMs) / DAY_MS2);
|
|
13733
13906
|
const perYearDecay = opts.perYearDecay ?? DEFAULT_PER_YEAR_DECAY;
|
|
13734
13907
|
const floor = opts.floor ?? DEFAULT_FLOOR;
|
|
13735
13908
|
const decay = perYearDecay * (ageDays2 / 365);
|
|
@@ -14238,9 +14411,6 @@ function createVaultEmbeddingCache(maxSize = DEFAULT_VAULT_CACHE_SIZE) {
|
|
|
14238
14411
|
|
|
14239
14412
|
// src/lib/memoryVault/searchTool.ts
|
|
14240
14413
|
var DEFAULT_RERANK_TOP_N = 5;
|
|
14241
|
-
function nowMs() {
|
|
14242
|
-
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
14243
|
-
}
|
|
14244
14414
|
var warnedProgrammaticDecomposeIgnored = false;
|
|
14245
14415
|
function normalizeEventTimeKind(kind) {
|
|
14246
14416
|
return kind === "point" || kind === "range" || kind === "ongoing" ? kind : null;
|
|
@@ -17338,9 +17508,6 @@ function scoreEventTimeOverlap(memoryStart, memoryEnd, memoryKind, window2) {
|
|
|
17338
17508
|
var DEFAULT_LIMIT = 8;
|
|
17339
17509
|
var DEFAULT_BUDGET = "low";
|
|
17340
17510
|
var DEFAULT_FACT_MIN_SCORE = 0.1;
|
|
17341
|
-
function nowMs2() {
|
|
17342
|
-
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
17343
|
-
}
|
|
17344
17511
|
var DEFAULT_CHUNK_MIN_SCORE = 0.5;
|
|
17345
17512
|
var DEFAULT_QUERY_EMBED_TOTAL_TIMEOUT_MS = 8e3;
|
|
17346
17513
|
function flagsForBudget(budget) {
|
|
@@ -17378,12 +17545,19 @@ async function recall(query, ctx, options = {}) {
|
|
|
17378
17545
|
const subQueries = normalizedFacets.length >= 2 ? normalizedFacets : void 0;
|
|
17379
17546
|
const embeddingOptions = {
|
|
17380
17547
|
...ctx.embeddingOptions,
|
|
17381
|
-
taskType: "memory_recall_embed"
|
|
17548
|
+
taskType: "memory_recall_embed",
|
|
17549
|
+
...options.embeddingCache && !ctx.embeddingOptions.cache && {
|
|
17550
|
+
cache: maskScopedEmbeddingCache(
|
|
17551
|
+
options.embeddingCache,
|
|
17552
|
+
Boolean(ctx.embeddingOptions.maskInput)
|
|
17553
|
+
)
|
|
17554
|
+
}
|
|
17382
17555
|
};
|
|
17383
|
-
const t0 =
|
|
17556
|
+
const t0 = nowMs();
|
|
17384
17557
|
let prepMs = 0;
|
|
17385
17558
|
let factLaneMs = 0;
|
|
17386
17559
|
let chunkLaneMs = 0;
|
|
17560
|
+
let chunkStats;
|
|
17387
17561
|
let fuseMs = 0;
|
|
17388
17562
|
const factResults = [];
|
|
17389
17563
|
const chunkResults = [];
|
|
@@ -17438,6 +17612,11 @@ async function recall(query, ctx, options = {}) {
|
|
|
17438
17612
|
...decryptLastRan !== void 0 && { decryptLast: decryptLastRan },
|
|
17439
17613
|
...vaultRowsDecrypted !== void 0 && { vaultRowsDecrypted },
|
|
17440
17614
|
...vaultRowsEmbedded !== void 0 && { vaultRowsEmbedded },
|
|
17615
|
+
...chunkStats && {
|
|
17616
|
+
chunkRowsScanned: chunkStats.rowsScanned,
|
|
17617
|
+
chunkRowsFetched: chunkStats.rowsFetched,
|
|
17618
|
+
chunkCacheHits: chunkStats.cacheHits
|
|
17619
|
+
},
|
|
17441
17620
|
factCount: factResults.length,
|
|
17442
17621
|
chunkCount: chunkResults.length,
|
|
17443
17622
|
admittedCount: admitted.length,
|
|
@@ -17449,7 +17628,7 @@ async function recall(query, ctx, options = {}) {
|
|
|
17449
17628
|
temporalLaneCount,
|
|
17450
17629
|
emptyReason: emptyReasonFor(admitted.length, laneRan),
|
|
17451
17630
|
timings: {
|
|
17452
|
-
total:
|
|
17631
|
+
total: nowMs() - t0,
|
|
17453
17632
|
prep: prepMs,
|
|
17454
17633
|
factLane: factLaneMs,
|
|
17455
17634
|
rerank: rerankMs,
|
|
@@ -17471,14 +17650,14 @@ async function recall(query, ctx, options = {}) {
|
|
|
17471
17650
|
const needsChunkEmbedding = types.includes("chunk") && ctx.storageCtx;
|
|
17472
17651
|
const wantsTemporal = types.includes("fact") && (ctx.factSource || ctx.vaultCtx);
|
|
17473
17652
|
const queryEmbedTotalTimeoutMs = options.queryEmbedTotalTimeoutMs ?? DEFAULT_QUERY_EMBED_TOTAL_TIMEOUT_MS;
|
|
17474
|
-
const prepStart =
|
|
17653
|
+
const prepStart = nowMs();
|
|
17475
17654
|
let sharedEmbedMs = 0;
|
|
17476
17655
|
const [queryEmbedding, entityRanking, temporalRanking] = await Promise.all([
|
|
17477
17656
|
needsChunkEmbedding ? generateEmbedding(query, {
|
|
17478
17657
|
...embeddingOptions,
|
|
17479
17658
|
totalTimeoutMs: queryEmbedTotalTimeoutMs
|
|
17480
17659
|
}).finally(() => {
|
|
17481
|
-
sharedEmbedMs =
|
|
17660
|
+
sharedEmbedMs = nowMs() - prepStart;
|
|
17482
17661
|
}).then((vec) => {
|
|
17483
17662
|
if (vec.length === 0) {
|
|
17484
17663
|
getLogger().warn(
|
|
@@ -17511,11 +17690,11 @@ async function recall(query, ctx, options = {}) {
|
|
|
17511
17690
|
() => ctx.factSource ? ctx.factSource.temporalRanking(query, options.now) : buildTemporalLaneRanking(query, ctx.vaultCtx, options.now)
|
|
17512
17691
|
) : Promise.resolve([])
|
|
17513
17692
|
]);
|
|
17514
|
-
prepMs =
|
|
17693
|
+
prepMs = nowMs() - prepStart;
|
|
17515
17694
|
graphLaneCount = entityRanking.length;
|
|
17516
17695
|
temporalLaneCount = temporalRanking.length;
|
|
17517
17696
|
if (types.includes("fact") && (ctx.factSource || ctx.vaultCtx && ctx.vaultCache)) {
|
|
17518
|
-
const factStart =
|
|
17697
|
+
const factStart = nowMs();
|
|
17519
17698
|
const vaultMinScore = options.minScore ?? DEFAULT_FACT_MIN_SCORE;
|
|
17520
17699
|
factFloor = vaultMinScore;
|
|
17521
17700
|
const {
|
|
@@ -17591,11 +17770,11 @@ async function recall(query, ctx, options = {}) {
|
|
|
17591
17770
|
factLaneRankedOnCosine = factRankedOnCosine;
|
|
17592
17771
|
decryptLastRan = factDecryptLast;
|
|
17593
17772
|
vaultRowsDecrypted = factRowsDecrypted;
|
|
17594
|
-
factLaneMs =
|
|
17773
|
+
factLaneMs = nowMs() - factStart;
|
|
17595
17774
|
}
|
|
17596
17775
|
if (chunkEmbedFailed && !factLaneRankedOnCosine) embeddingsUnavailable = true;
|
|
17597
17776
|
if (types.includes("chunk") && ctx.storageCtx && queryEmbedding) {
|
|
17598
|
-
const chunkStart =
|
|
17777
|
+
const chunkStart = nowMs();
|
|
17599
17778
|
const chunkMinScore = options.minScore ?? DEFAULT_CHUNK_MIN_SCORE;
|
|
17600
17779
|
chunkFloor = chunkMinScore;
|
|
17601
17780
|
const results = await searchChunksOp(ctx.storageCtx, queryEmbedding, {
|
|
@@ -17606,12 +17785,15 @@ async function recall(query, ctx, options = {}) {
|
|
|
17606
17785
|
...options.excludeConversationId && {
|
|
17607
17786
|
excludeConversationId: options.excludeConversationId
|
|
17608
17787
|
},
|
|
17609
|
-
...ctx.chunkCache && { chunkCache: ctx.chunkCache }
|
|
17788
|
+
...ctx.chunkCache && { chunkCache: ctx.chunkCache },
|
|
17789
|
+
onStats: (stats) => {
|
|
17790
|
+
chunkStats = stats;
|
|
17791
|
+
}
|
|
17610
17792
|
});
|
|
17611
17793
|
chunkResults.push(...dedupeBy(results, (r) => r.chunkText.trim()));
|
|
17612
|
-
chunkLaneMs =
|
|
17794
|
+
chunkLaneMs = nowMs() - chunkStart;
|
|
17613
17795
|
}
|
|
17614
|
-
const fuseStart =
|
|
17796
|
+
const fuseStart = nowMs();
|
|
17615
17797
|
if (types.length === 1 || factResults.length === 0 || chunkResults.length === 0) {
|
|
17616
17798
|
const memories2 = [
|
|
17617
17799
|
...factResults.map((r) => toFactMemory(r, options.now)),
|
|
@@ -17619,7 +17801,7 @@ async function recall(query, ctx, options = {}) {
|
|
|
17619
17801
|
];
|
|
17620
17802
|
memories2.sort((a, b) => b.score - a.score);
|
|
17621
17803
|
const candidateCount = factResults.length + chunkResults.length;
|
|
17622
|
-
fuseMs =
|
|
17804
|
+
fuseMs = nowMs() - fuseStart;
|
|
17623
17805
|
const admitted = memories2.slice(0, limit);
|
|
17624
17806
|
hitLimit = memories2.length > limit;
|
|
17625
17807
|
emitDiagnostics(candidateCount, admitted);
|
|
@@ -17683,7 +17865,7 @@ async function recall(query, ctx, options = {}) {
|
|
|
17683
17865
|
memories = selection.out;
|
|
17684
17866
|
}
|
|
17685
17867
|
hitLimit = selection.cut;
|
|
17686
|
-
fuseMs =
|
|
17868
|
+
fuseMs = nowMs() - fuseStart;
|
|
17687
17869
|
emitDiagnostics(byId.size, memories);
|
|
17688
17870
|
return {
|
|
17689
17871
|
memories,
|
package/dist/index.cjs
CHANGED
|
@@ -2177,13 +2177,35 @@ async function generateEmbedding(text2, options) {
|
|
|
2177
2177
|
if (total === void 0) return embedOne(text2, options, void 0);
|
|
2178
2178
|
return withDeadline((signal) => embedOne(text2, options, signal), total, "embedding");
|
|
2179
2179
|
}
|
|
2180
|
+
var inFlightByCache = /* @__PURE__ */ new WeakMap();
|
|
2180
2181
|
async function embedOne(text2, options, outer) {
|
|
2181
|
-
const {
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2182
|
+
const { cache } = options;
|
|
2183
|
+
if (!cache) return Array.from(await requestEmbedding(text2, options, outer));
|
|
2184
|
+
const cached = cache.get(text2);
|
|
2185
|
+
if (cached) return Array.from(cached);
|
|
2186
|
+
const inFlight = inFlightByCache.get(cache) ?? /* @__PURE__ */ new Map();
|
|
2187
|
+
inFlightByCache.set(cache, inFlight);
|
|
2188
|
+
const pending = inFlight.get(text2);
|
|
2189
|
+
if (pending) {
|
|
2190
|
+
try {
|
|
2191
|
+
return Array.from(await pending);
|
|
2192
|
+
} catch (err) {
|
|
2193
|
+
if (outer?.aborted) throw err;
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
const request = requestEmbedding(text2, options, outer);
|
|
2197
|
+
inFlight.set(text2, request);
|
|
2198
|
+
try {
|
|
2199
|
+
const embedding = await request;
|
|
2200
|
+
cache.set(text2, embedding);
|
|
2201
|
+
return Array.from(embedding);
|
|
2202
|
+
} finally {
|
|
2203
|
+
if (inFlight.get(text2) === request) inFlight.delete(text2);
|
|
2186
2204
|
}
|
|
2205
|
+
}
|
|
2206
|
+
async function requestEmbedding(text2, options, outer) {
|
|
2207
|
+
const { baseUrl = BASE_URL, model } = options;
|
|
2208
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_EMBEDDING_REQUEST_TIMEOUT_MS;
|
|
2187
2209
|
const headers = {
|
|
2188
2210
|
...await resolveAuthHeaders(options, outer),
|
|
2189
2211
|
...taskTypeHeader(options.taskType)
|
|
@@ -2218,11 +2240,7 @@ async function embedOne(text2, options, outer) {
|
|
|
2218
2240
|
totalTokens: response.data.usage.total_tokens ?? 0
|
|
2219
2241
|
});
|
|
2220
2242
|
}
|
|
2221
|
-
|
|
2222
|
-
if (cache) {
|
|
2223
|
-
cache.set(text2, f32Embedding);
|
|
2224
|
-
}
|
|
2225
|
-
return Array.from(f32Embedding);
|
|
2243
|
+
return Float32Array.from(embedding);
|
|
2226
2244
|
}
|
|
2227
2245
|
var DEFAULT_EMBEDDING_BATCH_SIZE = 100;
|
|
2228
2246
|
var DEFAULT_EMBEDDING_BATCH_CONCURRENCY = 3;
|