@anuma/sdk 10.5.1 → 10.5.3
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 +105 -58
- package/dist/expo/index.d.mts +7 -0
- package/dist/expo/index.d.ts +7 -0
- package/dist/expo/index.mjs +105 -58
- package/dist/react/index.cjs +131 -58
- package/dist/react/index.d.mts +13 -0
- package/dist/react/index.d.ts +13 -0
- package/dist/react/index.mjs +131 -58
- package/dist/server/index.cjs +67 -58
- package/dist/server/index.d.mts +7 -0
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.mjs +67 -58
- package/package.json +1 -1
package/dist/react/index.cjs
CHANGED
|
@@ -5619,6 +5619,63 @@ async function chunkRowSource(ctx, filter) {
|
|
|
5619
5619
|
return fullScanChunkRowSource(ctx, filter);
|
|
5620
5620
|
}
|
|
5621
5621
|
}
|
|
5622
|
+
async function readChunkVectorRow(ctx, row, onUnreadable) {
|
|
5623
|
+
const version = Number(row.updated_at) || 0;
|
|
5624
|
+
const chunks3 = await readRawJsonField(row, "chunks", ctx.walletAddress);
|
|
5625
|
+
if (chunks3 && chunks3.length > 0) {
|
|
5626
|
+
return {
|
|
5627
|
+
entry: {
|
|
5628
|
+
version,
|
|
5629
|
+
chunks: chunks3.map((chunk) => decodeChunkVector(chunk.vector, onUnreadable))
|
|
5630
|
+
},
|
|
5631
|
+
chunks: chunks3
|
|
5632
|
+
};
|
|
5633
|
+
}
|
|
5634
|
+
const messageVector = await readRawJsonField(row, "vector", ctx.walletAddress);
|
|
5635
|
+
if (!messageVector || messageVector.length === 0) return null;
|
|
5636
|
+
return { entry: { version, chunks: [], fallback: Float32Array.from(messageVector) }, chunks: [] };
|
|
5637
|
+
}
|
|
5638
|
+
async function warmChunkVectorCacheOp(ctx, options) {
|
|
5639
|
+
const { chunkCache, embeddingModel, limit, signal } = options;
|
|
5640
|
+
const source = await chunkRowSource(ctx, {});
|
|
5641
|
+
const yieldIfDue = createTimeSliceYielder(CHUNK_SCAN_YIELD_BUDGET_MS);
|
|
5642
|
+
let keys = source.keys.filter(
|
|
5643
|
+
(key3) => !embeddingModel || key3.embeddingModel === null || key3.embeddingModel === embeddingModel
|
|
5644
|
+
);
|
|
5645
|
+
if (limit !== void 0 && keys.length > limit) {
|
|
5646
|
+
keys = keys.sort((a, b) => b.version - a.version).slice(0, limit);
|
|
5647
|
+
}
|
|
5648
|
+
const missIds = [];
|
|
5649
|
+
for (const key3 of keys) {
|
|
5650
|
+
await yieldIfDue();
|
|
5651
|
+
if (chunkCache.get(key3.id)?.version !== key3.version) missIds.push(key3.id);
|
|
5652
|
+
}
|
|
5653
|
+
let warmed = 0;
|
|
5654
|
+
let unreadableVectors = 0;
|
|
5655
|
+
for await (const row of loadVectorRowsInBatches(source, missIds)) {
|
|
5656
|
+
await yieldIfDue();
|
|
5657
|
+
const read = await readChunkVectorRow(ctx, row, () => {
|
|
5658
|
+
unreadableVectors++;
|
|
5659
|
+
});
|
|
5660
|
+
if (signal?.aborted) break;
|
|
5661
|
+
if (!read) continue;
|
|
5662
|
+
chunkCache.set(row.id, read.entry);
|
|
5663
|
+
warmed++;
|
|
5664
|
+
}
|
|
5665
|
+
warnUnreadableChunkVectors("warmChunkVectorCacheOp", unreadableVectors);
|
|
5666
|
+
return warmed;
|
|
5667
|
+
}
|
|
5668
|
+
async function* loadVectorRowsInBatches(source, ids) {
|
|
5669
|
+
for (let offset = 0; offset < ids.length; offset += CHUNK_VECTOR_BATCH_SIZE) {
|
|
5670
|
+
yield* await source.loadVectorRows(ids.slice(offset, offset + CHUNK_VECTOR_BATCH_SIZE));
|
|
5671
|
+
}
|
|
5672
|
+
}
|
|
5673
|
+
function warnUnreadableChunkVectors(operation, count2) {
|
|
5674
|
+
if (count2 === 0) return;
|
|
5675
|
+
getLogger().warn(
|
|
5676
|
+
`${operation}: could not read ${count2} 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`
|
|
5677
|
+
);
|
|
5678
|
+
}
|
|
5622
5679
|
async function searchChunksOp(ctx, queryVector, options) {
|
|
5623
5680
|
const {
|
|
5624
5681
|
limit = 10,
|
|
@@ -5687,45 +5744,39 @@ async function searchChunksOp(ctx, queryVector, options) {
|
|
|
5687
5744
|
for (const row of rows) {
|
|
5688
5745
|
await yieldIfDue();
|
|
5689
5746
|
const id2 = row.id;
|
|
5690
|
-
const version = Number(row.updated_at) || 0;
|
|
5691
5747
|
const order = orderById.get(id2) ?? Number.MAX_SAFE_INTEGER;
|
|
5692
|
-
const
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
similarity,
|
|
5710
|
-
chunkTextSource: { kind: "chunk", chunk: chunks3[ci], siblings: chunks3 }
|
|
5711
|
-
});
|
|
5712
|
-
}
|
|
5748
|
+
const read = await readChunkVectorRow(ctx, row, () => {
|
|
5749
|
+
unreadableVectors++;
|
|
5750
|
+
});
|
|
5751
|
+
if (!read) continue;
|
|
5752
|
+
const { entry, chunks: chunks3 } = read;
|
|
5753
|
+
const { version } = entry;
|
|
5754
|
+
chunkCache?.set(id2, entry);
|
|
5755
|
+
if (entry.fallback) {
|
|
5756
|
+
const similarity = cosineSimilarity(queryVector, entry.fallback);
|
|
5757
|
+
if (similarity >= minSimilarity) {
|
|
5758
|
+
candidates.push({
|
|
5759
|
+
id: id2,
|
|
5760
|
+
version,
|
|
5761
|
+
order,
|
|
5762
|
+
similarity,
|
|
5763
|
+
chunkTextSource: { kind: "message" }
|
|
5764
|
+
});
|
|
5713
5765
|
}
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
}
|
|
5766
|
+
continue;
|
|
5767
|
+
}
|
|
5768
|
+
for (let ci = 0; ci < chunks3.length; ci++) {
|
|
5769
|
+
const vec = entry.chunks[ci];
|
|
5770
|
+
if (vec.length === 0) continue;
|
|
5771
|
+
const similarity = cosineSimilarity(queryVector, vec);
|
|
5772
|
+
if (similarity >= minSimilarity) {
|
|
5773
|
+
candidates.push({
|
|
5774
|
+
id: id2,
|
|
5775
|
+
version,
|
|
5776
|
+
order,
|
|
5777
|
+
similarity,
|
|
5778
|
+
chunkTextSource: { kind: "chunk", chunk: chunks3[ci], siblings: chunks3 }
|
|
5779
|
+
});
|
|
5729
5780
|
}
|
|
5730
5781
|
}
|
|
5731
5782
|
}
|
|
@@ -5736,11 +5787,7 @@ async function searchChunksOp(ctx, queryVector, options) {
|
|
|
5736
5787
|
`searchChunksOp: skipped ${staleSkipped} messages whose embedding model differs from the current model (${embeddingModel}) \u2014 re-embed via chunkAndEmbedAllMessages`
|
|
5737
5788
|
);
|
|
5738
5789
|
}
|
|
5739
|
-
|
|
5740
|
-
getLogger().warn(
|
|
5741
|
-
`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`
|
|
5742
|
-
);
|
|
5743
|
-
}
|
|
5790
|
+
warnUnreadableChunkVectors("searchChunksOp", unreadableVectors);
|
|
5744
5791
|
const topK = candidates.sort((a, b) => b.similarity - a.similarity || a.order - b.order).slice(0, limit);
|
|
5745
5792
|
const topIds = Array.from(new Set(topK.map((c) => c.id)));
|
|
5746
5793
|
const fullRows = await source.loadFullRows(topIds);
|
|
@@ -18392,6 +18439,9 @@ var WatermelonChatStorageAdapter = class {
|
|
|
18392
18439
|
searchChunks(queryVector, options) {
|
|
18393
18440
|
return searchChunksOp(this.ctx, queryVector, options);
|
|
18394
18441
|
}
|
|
18442
|
+
warmChunkCache(options) {
|
|
18443
|
+
return warmChunkVectorCacheOp(this.ctx, options);
|
|
18444
|
+
}
|
|
18395
18445
|
getSummary(conversationId) {
|
|
18396
18446
|
return getConversationSummaryOp(
|
|
18397
18447
|
createSummaryContext(this.ctx.database, this.ctx),
|
|
@@ -23709,12 +23759,16 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
|
|
|
23709
23759
|
const currentModel = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
|
|
23710
23760
|
const uncachedTexts = [];
|
|
23711
23761
|
const uncachedIndices = [];
|
|
23762
|
+
const resolvedVectors = /* @__PURE__ */ new Map();
|
|
23712
23763
|
let staleReembedCount = 0;
|
|
23713
23764
|
for (let i = 0; queryEmbedding.length > 0 && i < memories.length; i++) {
|
|
23714
23765
|
const content = memories[i].content;
|
|
23715
23766
|
const memoryId = memories[i].uniqueId;
|
|
23716
23767
|
const cached = cachedRowVector(cache, memoryId, memories[i].updatedAt, content);
|
|
23717
|
-
if (cached && cached.length === queryEmbedding.length)
|
|
23768
|
+
if (cached && cached.length === queryEmbedding.length) {
|
|
23769
|
+
resolvedVectors.set(memoryId, cached);
|
|
23770
|
+
continue;
|
|
23771
|
+
}
|
|
23718
23772
|
if (cached) cache.delete(memoryId);
|
|
23719
23773
|
const storedModel = memories[i].embeddingModel;
|
|
23720
23774
|
const modelCompatible = (storedModel ?? currentModel) === currentModel;
|
|
@@ -23722,13 +23776,9 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
|
|
|
23722
23776
|
try {
|
|
23723
23777
|
const parsed = JSON.parse(memories[i].embedding);
|
|
23724
23778
|
if (Array.isArray(parsed) && parsed.length === queryEmbedding.length) {
|
|
23725
|
-
|
|
23726
|
-
|
|
23727
|
-
|
|
23728
|
-
Float32Array.from(parsed),
|
|
23729
|
-
memories[i].updatedAt,
|
|
23730
|
-
content
|
|
23731
|
-
);
|
|
23779
|
+
const vector2 = Float32Array.from(parsed);
|
|
23780
|
+
resolvedVectors.set(memoryId, vector2);
|
|
23781
|
+
cacheRowVector(cache, memoryId, vector2, memories[i].updatedAt, content);
|
|
23732
23782
|
continue;
|
|
23733
23783
|
}
|
|
23734
23784
|
} catch {
|
|
@@ -23761,20 +23811,17 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
|
|
|
23761
23811
|
}
|
|
23762
23812
|
if (newEmbeddings) {
|
|
23763
23813
|
for (let j = 0; j < uncachedTexts.length; j++) {
|
|
23764
|
-
|
|
23765
|
-
|
|
23766
|
-
|
|
23767
|
-
|
|
23768
|
-
memories[uncachedIndices[j]].updatedAt,
|
|
23769
|
-
memories[uncachedIndices[j]].content
|
|
23770
|
-
);
|
|
23814
|
+
const memory = memories[uncachedIndices[j]];
|
|
23815
|
+
const vector2 = Float32Array.from(newEmbeddings[j]);
|
|
23816
|
+
resolvedVectors.set(memory.uniqueId, vector2);
|
|
23817
|
+
cacheRowVector(cache, memory.uniqueId, vector2, memory.updatedAt, memory.content);
|
|
23771
23818
|
}
|
|
23772
23819
|
}
|
|
23773
23820
|
}
|
|
23774
23821
|
embeddedItems = memories.map((m) => ({
|
|
23775
23822
|
id: m.uniqueId,
|
|
23776
23823
|
content: m.content,
|
|
23777
|
-
embedding:
|
|
23824
|
+
embedding: resolvedVectors.get(m.uniqueId) ?? [],
|
|
23778
23825
|
createdAt: m.createdAt,
|
|
23779
23826
|
updatedAt: m.updatedAt,
|
|
23780
23827
|
proofCount: m.proofCount,
|
|
@@ -38286,6 +38333,7 @@ function useChatStorage(options) {
|
|
|
38286
38333
|
[vaultCtx, vaultEmbeddingCache, memoryStore]
|
|
38287
38334
|
);
|
|
38288
38335
|
const chunkVectorCacheRef = (0, import_react7.useRef)(createChunkVectorCache());
|
|
38336
|
+
const recallWarmsRef = (0, import_react7.useRef)(/* @__PURE__ */ new Set());
|
|
38289
38337
|
const clientToolEmbeddingsCacheRef = (0, import_react7.useRef)(/* @__PURE__ */ new Map());
|
|
38290
38338
|
(0, import_react7.useEffect)(() => {
|
|
38291
38339
|
if (!getToken || memoryStore !== void 0) return;
|
|
@@ -38299,6 +38347,8 @@ function useChatStorage(options) {
|
|
|
38299
38347
|
(0, import_react7.useEffect)(() => {
|
|
38300
38348
|
return onClearAllEncryptionState(() => {
|
|
38301
38349
|
vaultEmbeddingCache.clear();
|
|
38350
|
+
for (const warm of recallWarmsRef.current) warm.abort();
|
|
38351
|
+
recallWarmsRef.current.clear();
|
|
38302
38352
|
chunkVectorCacheRef.current.clear();
|
|
38303
38353
|
});
|
|
38304
38354
|
}, [vaultEmbeddingCache]);
|
|
@@ -38417,6 +38467,28 @@ function useChatStorage(options) {
|
|
|
38417
38467
|
storeMaskMissing
|
|
38418
38468
|
]
|
|
38419
38469
|
);
|
|
38470
|
+
const warmRecallCache = (0, import_react7.useCallback)(
|
|
38471
|
+
async (signal) => {
|
|
38472
|
+
if (!backend.warmChunkCache) return 0;
|
|
38473
|
+
const warm = new AbortController();
|
|
38474
|
+
const stop2 = () => warm.abort(signal?.reason);
|
|
38475
|
+
if (signal?.aborted) stop2();
|
|
38476
|
+
signal?.addEventListener("abort", stop2, { once: true });
|
|
38477
|
+
recallWarmsRef.current.add(warm);
|
|
38478
|
+
try {
|
|
38479
|
+
return await backend.warmChunkCache({
|
|
38480
|
+
chunkCache: chunkVectorCacheRef.current,
|
|
38481
|
+
embeddingModel,
|
|
38482
|
+
limit: DEFAULT_CHUNK_CACHE_SIZE,
|
|
38483
|
+
signal: warm.signal
|
|
38484
|
+
});
|
|
38485
|
+
} finally {
|
|
38486
|
+
recallWarmsRef.current.delete(warm);
|
|
38487
|
+
signal?.removeEventListener("abort", stop2);
|
|
38488
|
+
}
|
|
38489
|
+
},
|
|
38490
|
+
[backend, embeddingModel]
|
|
38491
|
+
);
|
|
38420
38492
|
const {
|
|
38421
38493
|
isLoading,
|
|
38422
38494
|
sendMessage: baseSendMessage,
|
|
@@ -39599,6 +39671,7 @@ function useChatStorage(options) {
|
|
|
39599
39671
|
createMemoryVaultSearchTool: createMemoryVaultSearchTool2,
|
|
39600
39672
|
createRecallTool: createRecallTool2,
|
|
39601
39673
|
recall: recallFn,
|
|
39674
|
+
warmRecallCache,
|
|
39602
39675
|
searchVaultMemories: searchVaultMemoriesFn,
|
|
39603
39676
|
vaultEmbeddingCache,
|
|
39604
39677
|
getVaultMemories,
|
package/dist/react/index.d.mts
CHANGED
|
@@ -4899,6 +4899,13 @@ interface ChatBackend extends ChatStorageAdapter {
|
|
|
4899
4899
|
uniqueId: string;
|
|
4900
4900
|
}): Promise<StoredMessage>;
|
|
4901
4901
|
searchChunks(queryVector: number[], options?: ChatChunkSearchOptions): Promise<ChunkSearchResult[]>;
|
|
4902
|
+
/** Fills a chunk vector cache ahead of the first search, for backends that score on the device. */
|
|
4903
|
+
warmChunkCache?(options: {
|
|
4904
|
+
chunkCache: ChunkVectorCache;
|
|
4905
|
+
embeddingModel?: string;
|
|
4906
|
+
limit?: number;
|
|
4907
|
+
signal?: AbortSignal;
|
|
4908
|
+
}): Promise<number>;
|
|
4902
4909
|
getSummary(conversationId: string): Promise<StoredConversationSummary | null>;
|
|
4903
4910
|
upsertSummary(conversationId: string, summary: string, summarizedUpTo: string, tokenCount: number): Promise<StoredConversationSummary>;
|
|
4904
4911
|
deleteSummary(conversationId: string): Promise<void>;
|
|
@@ -13709,6 +13716,7 @@ declare class WatermelonChatStorageAdapter implements ChatBackend {
|
|
|
13709
13716
|
uniqueId: string;
|
|
13710
13717
|
}): Promise<StoredMessage>;
|
|
13711
13718
|
searchChunks(queryVector: number[], options?: ChatChunkSearchOptions): Promise<ChunkSearchResult[]>;
|
|
13719
|
+
warmChunkCache(options: Parameters<NonNullable<ChatBackend["warmChunkCache"]>>[0]): Promise<number>;
|
|
13712
13720
|
getSummary(conversationId: string): Promise<StoredConversationSummary | null>;
|
|
13713
13721
|
upsertSummary(conversationId: string, summary: string, summarizedUpTo: string, tokenCount: number): Promise<StoredConversationSummary>;
|
|
13714
13722
|
deleteSummary(conversationId: string): Promise<void>;
|
|
@@ -15057,6 +15065,11 @@ interface UseChatStorageResult extends BaseUseChatStorageResult {
|
|
|
15057
15065
|
* @param options - Optional recall configuration (types, budget, limit, scopes, …)
|
|
15058
15066
|
*/
|
|
15059
15067
|
recall: (query: string, options?: RecallOptions) => Promise<RecallResult>;
|
|
15068
|
+
/**
|
|
15069
|
+
* Loads the most recent past messages' vectors, up to the recall cache's size, so the first recall does not read them all.
|
|
15070
|
+
* @returns How many it added. It resolves rather than rejects when aborted or signed out, with how many it added before stopping.
|
|
15071
|
+
*/
|
|
15072
|
+
warmRecallCache: (signal?: AbortSignal) => Promise<number>;
|
|
15060
15073
|
/**
|
|
15061
15074
|
* Search vault memories programmatically using semantic similarity.
|
|
15062
15075
|
* Returns structured results sorted by descending similarity.
|
package/dist/react/index.d.ts
CHANGED
|
@@ -4899,6 +4899,13 @@ interface ChatBackend extends ChatStorageAdapter {
|
|
|
4899
4899
|
uniqueId: string;
|
|
4900
4900
|
}): Promise<StoredMessage>;
|
|
4901
4901
|
searchChunks(queryVector: number[], options?: ChatChunkSearchOptions): Promise<ChunkSearchResult[]>;
|
|
4902
|
+
/** Fills a chunk vector cache ahead of the first search, for backends that score on the device. */
|
|
4903
|
+
warmChunkCache?(options: {
|
|
4904
|
+
chunkCache: ChunkVectorCache;
|
|
4905
|
+
embeddingModel?: string;
|
|
4906
|
+
limit?: number;
|
|
4907
|
+
signal?: AbortSignal;
|
|
4908
|
+
}): Promise<number>;
|
|
4902
4909
|
getSummary(conversationId: string): Promise<StoredConversationSummary | null>;
|
|
4903
4910
|
upsertSummary(conversationId: string, summary: string, summarizedUpTo: string, tokenCount: number): Promise<StoredConversationSummary>;
|
|
4904
4911
|
deleteSummary(conversationId: string): Promise<void>;
|
|
@@ -13709,6 +13716,7 @@ declare class WatermelonChatStorageAdapter implements ChatBackend {
|
|
|
13709
13716
|
uniqueId: string;
|
|
13710
13717
|
}): Promise<StoredMessage>;
|
|
13711
13718
|
searchChunks(queryVector: number[], options?: ChatChunkSearchOptions): Promise<ChunkSearchResult[]>;
|
|
13719
|
+
warmChunkCache(options: Parameters<NonNullable<ChatBackend["warmChunkCache"]>>[0]): Promise<number>;
|
|
13712
13720
|
getSummary(conversationId: string): Promise<StoredConversationSummary | null>;
|
|
13713
13721
|
upsertSummary(conversationId: string, summary: string, summarizedUpTo: string, tokenCount: number): Promise<StoredConversationSummary>;
|
|
13714
13722
|
deleteSummary(conversationId: string): Promise<void>;
|
|
@@ -15057,6 +15065,11 @@ interface UseChatStorageResult extends BaseUseChatStorageResult {
|
|
|
15057
15065
|
* @param options - Optional recall configuration (types, budget, limit, scopes, …)
|
|
15058
15066
|
*/
|
|
15059
15067
|
recall: (query: string, options?: RecallOptions) => Promise<RecallResult>;
|
|
15068
|
+
/**
|
|
15069
|
+
* Loads the most recent past messages' vectors, up to the recall cache's size, so the first recall does not read them all.
|
|
15070
|
+
* @returns How many it added. It resolves rather than rejects when aborted or signed out, with how many it added before stopping.
|
|
15071
|
+
*/
|
|
15072
|
+
warmRecallCache: (signal?: AbortSignal) => Promise<number>;
|
|
15060
15073
|
/**
|
|
15061
15074
|
* Search vault memories programmatically using semantic similarity.
|
|
15062
15075
|
* Returns structured results sorted by descending similarity.
|
package/dist/react/index.mjs
CHANGED
|
@@ -3773,6 +3773,63 @@ async function chunkRowSource(ctx, filter) {
|
|
|
3773
3773
|
return fullScanChunkRowSource(ctx, filter);
|
|
3774
3774
|
}
|
|
3775
3775
|
}
|
|
3776
|
+
async function readChunkVectorRow(ctx, row, onUnreadable) {
|
|
3777
|
+
const version = Number(row.updated_at) || 0;
|
|
3778
|
+
const chunks3 = await readRawJsonField(row, "chunks", ctx.walletAddress);
|
|
3779
|
+
if (chunks3 && chunks3.length > 0) {
|
|
3780
|
+
return {
|
|
3781
|
+
entry: {
|
|
3782
|
+
version,
|
|
3783
|
+
chunks: chunks3.map((chunk) => decodeChunkVector(chunk.vector, onUnreadable))
|
|
3784
|
+
},
|
|
3785
|
+
chunks: chunks3
|
|
3786
|
+
};
|
|
3787
|
+
}
|
|
3788
|
+
const messageVector = await readRawJsonField(row, "vector", ctx.walletAddress);
|
|
3789
|
+
if (!messageVector || messageVector.length === 0) return null;
|
|
3790
|
+
return { entry: { version, chunks: [], fallback: Float32Array.from(messageVector) }, chunks: [] };
|
|
3791
|
+
}
|
|
3792
|
+
async function warmChunkVectorCacheOp(ctx, options) {
|
|
3793
|
+
const { chunkCache, embeddingModel, limit, signal } = options;
|
|
3794
|
+
const source = await chunkRowSource(ctx, {});
|
|
3795
|
+
const yieldIfDue = createTimeSliceYielder(CHUNK_SCAN_YIELD_BUDGET_MS);
|
|
3796
|
+
let keys = source.keys.filter(
|
|
3797
|
+
(key3) => !embeddingModel || key3.embeddingModel === null || key3.embeddingModel === embeddingModel
|
|
3798
|
+
);
|
|
3799
|
+
if (limit !== void 0 && keys.length > limit) {
|
|
3800
|
+
keys = keys.sort((a, b) => b.version - a.version).slice(0, limit);
|
|
3801
|
+
}
|
|
3802
|
+
const missIds = [];
|
|
3803
|
+
for (const key3 of keys) {
|
|
3804
|
+
await yieldIfDue();
|
|
3805
|
+
if (chunkCache.get(key3.id)?.version !== key3.version) missIds.push(key3.id);
|
|
3806
|
+
}
|
|
3807
|
+
let warmed = 0;
|
|
3808
|
+
let unreadableVectors = 0;
|
|
3809
|
+
for await (const row of loadVectorRowsInBatches(source, missIds)) {
|
|
3810
|
+
await yieldIfDue();
|
|
3811
|
+
const read = await readChunkVectorRow(ctx, row, () => {
|
|
3812
|
+
unreadableVectors++;
|
|
3813
|
+
});
|
|
3814
|
+
if (signal?.aborted) break;
|
|
3815
|
+
if (!read) continue;
|
|
3816
|
+
chunkCache.set(row.id, read.entry);
|
|
3817
|
+
warmed++;
|
|
3818
|
+
}
|
|
3819
|
+
warnUnreadableChunkVectors("warmChunkVectorCacheOp", unreadableVectors);
|
|
3820
|
+
return warmed;
|
|
3821
|
+
}
|
|
3822
|
+
async function* loadVectorRowsInBatches(source, ids) {
|
|
3823
|
+
for (let offset = 0; offset < ids.length; offset += CHUNK_VECTOR_BATCH_SIZE) {
|
|
3824
|
+
yield* await source.loadVectorRows(ids.slice(offset, offset + CHUNK_VECTOR_BATCH_SIZE));
|
|
3825
|
+
}
|
|
3826
|
+
}
|
|
3827
|
+
function warnUnreadableChunkVectors(operation, count2) {
|
|
3828
|
+
if (count2 === 0) return;
|
|
3829
|
+
getLogger().warn(
|
|
3830
|
+
`${operation}: could not read ${count2} 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`
|
|
3831
|
+
);
|
|
3832
|
+
}
|
|
3776
3833
|
async function searchChunksOp(ctx, queryVector, options) {
|
|
3777
3834
|
const {
|
|
3778
3835
|
limit = 10,
|
|
@@ -3841,45 +3898,39 @@ async function searchChunksOp(ctx, queryVector, options) {
|
|
|
3841
3898
|
for (const row of rows) {
|
|
3842
3899
|
await yieldIfDue();
|
|
3843
3900
|
const id2 = row.id;
|
|
3844
|
-
const version = Number(row.updated_at) || 0;
|
|
3845
3901
|
const order = orderById.get(id2) ?? Number.MAX_SAFE_INTEGER;
|
|
3846
|
-
const
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
similarity,
|
|
3864
|
-
chunkTextSource: { kind: "chunk", chunk: chunks3[ci], siblings: chunks3 }
|
|
3865
|
-
});
|
|
3866
|
-
}
|
|
3902
|
+
const read = await readChunkVectorRow(ctx, row, () => {
|
|
3903
|
+
unreadableVectors++;
|
|
3904
|
+
});
|
|
3905
|
+
if (!read) continue;
|
|
3906
|
+
const { entry, chunks: chunks3 } = read;
|
|
3907
|
+
const { version } = entry;
|
|
3908
|
+
chunkCache?.set(id2, entry);
|
|
3909
|
+
if (entry.fallback) {
|
|
3910
|
+
const similarity = cosineSimilarity(queryVector, entry.fallback);
|
|
3911
|
+
if (similarity >= minSimilarity) {
|
|
3912
|
+
candidates.push({
|
|
3913
|
+
id: id2,
|
|
3914
|
+
version,
|
|
3915
|
+
order,
|
|
3916
|
+
similarity,
|
|
3917
|
+
chunkTextSource: { kind: "message" }
|
|
3918
|
+
});
|
|
3867
3919
|
}
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
}
|
|
3920
|
+
continue;
|
|
3921
|
+
}
|
|
3922
|
+
for (let ci = 0; ci < chunks3.length; ci++) {
|
|
3923
|
+
const vec = entry.chunks[ci];
|
|
3924
|
+
if (vec.length === 0) continue;
|
|
3925
|
+
const similarity = cosineSimilarity(queryVector, vec);
|
|
3926
|
+
if (similarity >= minSimilarity) {
|
|
3927
|
+
candidates.push({
|
|
3928
|
+
id: id2,
|
|
3929
|
+
version,
|
|
3930
|
+
order,
|
|
3931
|
+
similarity,
|
|
3932
|
+
chunkTextSource: { kind: "chunk", chunk: chunks3[ci], siblings: chunks3 }
|
|
3933
|
+
});
|
|
3883
3934
|
}
|
|
3884
3935
|
}
|
|
3885
3936
|
}
|
|
@@ -3890,11 +3941,7 @@ async function searchChunksOp(ctx, queryVector, options) {
|
|
|
3890
3941
|
`searchChunksOp: skipped ${staleSkipped} messages whose embedding model differs from the current model (${embeddingModel}) \u2014 re-embed via chunkAndEmbedAllMessages`
|
|
3891
3942
|
);
|
|
3892
3943
|
}
|
|
3893
|
-
|
|
3894
|
-
getLogger().warn(
|
|
3895
|
-
`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`
|
|
3896
|
-
);
|
|
3897
|
-
}
|
|
3944
|
+
warnUnreadableChunkVectors("searchChunksOp", unreadableVectors);
|
|
3898
3945
|
const topK = candidates.sort((a, b) => b.similarity - a.similarity || a.order - b.order).slice(0, limit);
|
|
3899
3946
|
const topIds = Array.from(new Set(topK.map((c) => c.id)));
|
|
3900
3947
|
const fullRows = await source.loadFullRows(topIds);
|
|
@@ -16522,6 +16569,9 @@ var WatermelonChatStorageAdapter = class {
|
|
|
16522
16569
|
searchChunks(queryVector, options) {
|
|
16523
16570
|
return searchChunksOp(this.ctx, queryVector, options);
|
|
16524
16571
|
}
|
|
16572
|
+
warmChunkCache(options) {
|
|
16573
|
+
return warmChunkVectorCacheOp(this.ctx, options);
|
|
16574
|
+
}
|
|
16525
16575
|
getSummary(conversationId) {
|
|
16526
16576
|
return getConversationSummaryOp(
|
|
16527
16577
|
createSummaryContext(this.ctx.database, this.ctx),
|
|
@@ -21809,12 +21859,16 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
|
|
|
21809
21859
|
const currentModel = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
|
|
21810
21860
|
const uncachedTexts = [];
|
|
21811
21861
|
const uncachedIndices = [];
|
|
21862
|
+
const resolvedVectors = /* @__PURE__ */ new Map();
|
|
21812
21863
|
let staleReembedCount = 0;
|
|
21813
21864
|
for (let i = 0; queryEmbedding.length > 0 && i < memories.length; i++) {
|
|
21814
21865
|
const content = memories[i].content;
|
|
21815
21866
|
const memoryId = memories[i].uniqueId;
|
|
21816
21867
|
const cached = cachedRowVector(cache, memoryId, memories[i].updatedAt, content);
|
|
21817
|
-
if (cached && cached.length === queryEmbedding.length)
|
|
21868
|
+
if (cached && cached.length === queryEmbedding.length) {
|
|
21869
|
+
resolvedVectors.set(memoryId, cached);
|
|
21870
|
+
continue;
|
|
21871
|
+
}
|
|
21818
21872
|
if (cached) cache.delete(memoryId);
|
|
21819
21873
|
const storedModel = memories[i].embeddingModel;
|
|
21820
21874
|
const modelCompatible = (storedModel ?? currentModel) === currentModel;
|
|
@@ -21822,13 +21876,9 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
|
|
|
21822
21876
|
try {
|
|
21823
21877
|
const parsed = JSON.parse(memories[i].embedding);
|
|
21824
21878
|
if (Array.isArray(parsed) && parsed.length === queryEmbedding.length) {
|
|
21825
|
-
|
|
21826
|
-
|
|
21827
|
-
|
|
21828
|
-
Float32Array.from(parsed),
|
|
21829
|
-
memories[i].updatedAt,
|
|
21830
|
-
content
|
|
21831
|
-
);
|
|
21879
|
+
const vector2 = Float32Array.from(parsed);
|
|
21880
|
+
resolvedVectors.set(memoryId, vector2);
|
|
21881
|
+
cacheRowVector(cache, memoryId, vector2, memories[i].updatedAt, content);
|
|
21832
21882
|
continue;
|
|
21833
21883
|
}
|
|
21834
21884
|
} catch {
|
|
@@ -21861,20 +21911,17 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
|
|
|
21861
21911
|
}
|
|
21862
21912
|
if (newEmbeddings) {
|
|
21863
21913
|
for (let j = 0; j < uncachedTexts.length; j++) {
|
|
21864
|
-
|
|
21865
|
-
|
|
21866
|
-
|
|
21867
|
-
|
|
21868
|
-
memories[uncachedIndices[j]].updatedAt,
|
|
21869
|
-
memories[uncachedIndices[j]].content
|
|
21870
|
-
);
|
|
21914
|
+
const memory = memories[uncachedIndices[j]];
|
|
21915
|
+
const vector2 = Float32Array.from(newEmbeddings[j]);
|
|
21916
|
+
resolvedVectors.set(memory.uniqueId, vector2);
|
|
21917
|
+
cacheRowVector(cache, memory.uniqueId, vector2, memory.updatedAt, memory.content);
|
|
21871
21918
|
}
|
|
21872
21919
|
}
|
|
21873
21920
|
}
|
|
21874
21921
|
embeddedItems = memories.map((m) => ({
|
|
21875
21922
|
id: m.uniqueId,
|
|
21876
21923
|
content: m.content,
|
|
21877
|
-
embedding:
|
|
21924
|
+
embedding: resolvedVectors.get(m.uniqueId) ?? [],
|
|
21878
21925
|
createdAt: m.createdAt,
|
|
21879
21926
|
updatedAt: m.updatedAt,
|
|
21880
21927
|
proofCount: m.proofCount,
|
|
@@ -36347,6 +36394,7 @@ function useChatStorage(options) {
|
|
|
36347
36394
|
[vaultCtx, vaultEmbeddingCache, memoryStore]
|
|
36348
36395
|
);
|
|
36349
36396
|
const chunkVectorCacheRef = useRef4(createChunkVectorCache());
|
|
36397
|
+
const recallWarmsRef = useRef4(/* @__PURE__ */ new Set());
|
|
36350
36398
|
const clientToolEmbeddingsCacheRef = useRef4(/* @__PURE__ */ new Map());
|
|
36351
36399
|
useEffect5(() => {
|
|
36352
36400
|
if (!getToken || memoryStore !== void 0) return;
|
|
@@ -36360,6 +36408,8 @@ function useChatStorage(options) {
|
|
|
36360
36408
|
useEffect5(() => {
|
|
36361
36409
|
return onClearAllEncryptionState(() => {
|
|
36362
36410
|
vaultEmbeddingCache.clear();
|
|
36411
|
+
for (const warm of recallWarmsRef.current) warm.abort();
|
|
36412
|
+
recallWarmsRef.current.clear();
|
|
36363
36413
|
chunkVectorCacheRef.current.clear();
|
|
36364
36414
|
});
|
|
36365
36415
|
}, [vaultEmbeddingCache]);
|
|
@@ -36478,6 +36528,28 @@ function useChatStorage(options) {
|
|
|
36478
36528
|
storeMaskMissing
|
|
36479
36529
|
]
|
|
36480
36530
|
);
|
|
36531
|
+
const warmRecallCache = useCallback6(
|
|
36532
|
+
async (signal) => {
|
|
36533
|
+
if (!backend.warmChunkCache) return 0;
|
|
36534
|
+
const warm = new AbortController();
|
|
36535
|
+
const stop2 = () => warm.abort(signal?.reason);
|
|
36536
|
+
if (signal?.aborted) stop2();
|
|
36537
|
+
signal?.addEventListener("abort", stop2, { once: true });
|
|
36538
|
+
recallWarmsRef.current.add(warm);
|
|
36539
|
+
try {
|
|
36540
|
+
return await backend.warmChunkCache({
|
|
36541
|
+
chunkCache: chunkVectorCacheRef.current,
|
|
36542
|
+
embeddingModel,
|
|
36543
|
+
limit: DEFAULT_CHUNK_CACHE_SIZE,
|
|
36544
|
+
signal: warm.signal
|
|
36545
|
+
});
|
|
36546
|
+
} finally {
|
|
36547
|
+
recallWarmsRef.current.delete(warm);
|
|
36548
|
+
signal?.removeEventListener("abort", stop2);
|
|
36549
|
+
}
|
|
36550
|
+
},
|
|
36551
|
+
[backend, embeddingModel]
|
|
36552
|
+
);
|
|
36481
36553
|
const {
|
|
36482
36554
|
isLoading,
|
|
36483
36555
|
sendMessage: baseSendMessage,
|
|
@@ -37660,6 +37732,7 @@ function useChatStorage(options) {
|
|
|
37660
37732
|
createMemoryVaultSearchTool: createMemoryVaultSearchTool2,
|
|
37661
37733
|
createRecallTool: createRecallTool2,
|
|
37662
37734
|
recall: recallFn,
|
|
37735
|
+
warmRecallCache,
|
|
37663
37736
|
searchVaultMemories: searchVaultMemoriesFn,
|
|
37664
37737
|
vaultEmbeddingCache,
|
|
37665
37738
|
getVaultMemories,
|