@@ -19,6 +19,7 @@ import {
1919 type MemorySource ,
2020} from "openclaw/plugin-sdk/memory-core-host-engine-storage" ;
2121import { MAX_TIMER_TIMEOUT_MS , resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime" ;
22+ import type { EmbeddingProvider } from "./embeddings.js" ;
2223import {
2324 MEMORY_BATCH_FAILURE_LIMIT ,
2425 recordMemoryBatchFailure ,
@@ -74,6 +75,7 @@ function resolveEmbeddingSecondsTimeoutMs(seconds: number): number {
7475}
7576
7677type MemoryIndexEntry = MemoryIndexWorkItem [ "entry" ] ;
78+ type MemoryEmbeddingBatchFn = NonNullable < MemoryEmbeddingProviderRuntime [ "batchEmbed" ] > ;
7779
7880type PreparedMemoryIndexEntry = {
7981 entry : MemoryIndexEntry ;
@@ -304,19 +306,15 @@ export abstract class MemoryManagerEmbeddingOps extends MemoryManagerSyncOps {
304306
305307 private async embedChunksWithBatch (
306308 chunks : MemoryChunk [ ] ,
309+ provider : EmbeddingProvider ,
310+ batchEmbed : MemoryEmbeddingBatchFn ,
307311 _entry : MemoryIndexEntry ,
308312 source : string ,
309313 debugContext : Record < string , unknown > = { } ,
310314 ) : Promise < number [ ] [ ] > {
311- const provider = this . provider ;
312- const batchEmbed = this . providerRuntime ?. batchEmbed ;
313- if ( ! provider || ! batchEmbed ) {
314- return this . embedChunksInBatches ( chunks ) ;
315- }
316- if ( chunks . length === 0 ) {
317- return [ ] ;
318- }
319- const { embeddings, missing } = this . collectCachedEmbeddings ( chunks ) ;
315+ const cached = this . collectCachedEmbeddings ( chunks ) ;
316+ // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- Node 24 CI oxlint misreports this cache result destructuring; keep the suppression scoped to the no-op assertion.
317+ const { embeddings, missing } = cached as typeof cached ;
320318 if ( missing . length === 0 ) {
321319 return embeddings ;
322320 }
@@ -888,13 +886,20 @@ export abstract class MemoryManagerEmbeddingOps extends MemoryManagerSyncOps {
888886 for ( let requestIndex = 0 ; requestIndex < chunkBatches . length ; requestIndex += 1 ) {
889887 const chunkBatch = chunkBatches [ requestIndex ] ?? [ ] ;
890888 embeddings . push (
891- ...( await this . embedChunksWithBatch ( chunkBatch , firstEntry , source , {
892- sourceWideFiles : current . length ,
893- sourceWideSources : sourceCounts ,
894- sourceWideBatchGroup,
895- sourceWideRequestGroup : requestIndex + 1 ,
896- sourceWideRequestGroups : chunkBatches . length ,
897- } ) ) ,
889+ ...( await this . embedChunksWithBatch (
890+ chunkBatch ,
891+ provider ,
892+ batchEmbed ,
893+ firstEntry ,
894+ source ,
895+ {
896+ sourceWideFiles : current . length ,
897+ sourceWideSources : sourceCounts ,
898+ sourceWideBatchGroup,
899+ sourceWideRequestGroup : requestIndex + 1 ,
900+ sourceWideRequestGroups : chunkBatches . length ,
901+ } ,
902+ ) ) ,
898903 ) ;
899904 }
900905 const sample = embeddings . find ( ( embedding ) => embedding . length > 0 ) ;
@@ -950,7 +955,8 @@ export abstract class MemoryManagerEmbeddingOps extends MemoryManagerSyncOps {
950955 options : { source : MemorySource ; content ?: string } ,
951956 ) {
952957 // FTS-only mode: no embedding provider, but we can still build a FTS index
953- if ( ! this . provider ) {
958+ const provider = this . provider ;
959+ if ( ! provider ) {
954960 // Multimodal files require an embedding provider; skip in FTS-only mode.
955961 if ( "kind" in entry && entry . kind === "multimodal" ) {
956962 return ;
@@ -967,9 +973,17 @@ export abstract class MemoryManagerEmbeddingOps extends MemoryManagerSyncOps {
967973
968974 let embeddings : number [ ] [ ] ;
969975 try {
970- embeddings = this . batch . enabled
971- ? await this . embedChunksWithBatch ( prepared . chunks , entry , options . source )
972- : await this . embedChunksInBatches ( prepared . chunks ) ;
976+ const batchEmbed = this . providerRuntime ?. batchEmbed ;
977+ embeddings =
978+ this . batch . enabled && batchEmbed
979+ ? await this . embedChunksWithBatch (
980+ prepared . chunks ,
981+ provider ,
982+ batchEmbed ,
983+ entry ,
984+ options . source ,
985+ )
986+ : await this . embedChunksInBatches ( prepared . chunks ) ;
973987 } catch ( err ) {
974988 const message = formatErrorMessage ( err ) ;
975989 if (
@@ -982,8 +996,8 @@ export abstract class MemoryManagerEmbeddingOps extends MemoryManagerSyncOps {
982996 log . warn ( "memory embeddings: skipping multimodal file rejected as too large" , {
983997 path : entry . path ,
984998 bytes : prepared . structuredInputBytes ,
985- provider : this . provider . id ,
986- model : this . provider . model ,
999+ provider : provider . id ,
1000+ model : provider . model ,
9871001 error : message ,
9881002 } ) ;
9891003 this . clearIndexedFileData ( entry . path , options . source ) ;
@@ -997,7 +1011,7 @@ export abstract class MemoryManagerEmbeddingOps extends MemoryManagerSyncOps {
9971011 this . writeChunks (
9981012 entry ,
9991013 options . source ,
1000- this . provider . model ,
1014+ provider . model ,
10011015 prepared . chunks ,
10021016 embeddings ,
10031017 vectorReady ,
0 commit comments