@@ -12,6 +12,7 @@ export interface EmbeddingResult {
12
12
embeddings : Array < {
13
13
chunkId : string ;
14
14
embedding : number [ ] ;
15
+ tokens ?: number ;
15
16
} > ;
16
17
error ?: string ;
17
18
processingTime : number ;
@@ -63,7 +64,7 @@ export class EmbeddingGenerator {
63
64
64
65
async generateEmbeddings ( chunks : EmbeddingBatch [ ] ) : Promise < EmbeddingResult > {
65
66
const startTime = Date . now ( ) ;
66
- const results : Array < { chunkId : string ; embedding : number [ ] } > = [ ] ;
67
+ const results : Array < { chunkId : string ; embedding : number [ ] ; tokens ?: number } > = [ ] ;
67
68
let totalTokens = 0 ;
68
69
let cachedCount = 0 ;
69
70
let newCount = 0 ;
@@ -89,14 +90,14 @@ export class EmbeddingGenerator {
89
90
else groups . set ( key , { content : chunk . content , chunkIds : [ chunk . chunkId ] } ) ;
90
91
}
91
92
92
- const cachedEmbeds : Array < { chunkId : string ; embedding : number [ ] } > = [ ] ;
93
+ const cachedEmbeds : Array < { chunkId : string ; embedding : number [ ] ; tokens ?: number } > = [ ] ;
93
94
const newGroups : Array < { content : string ; chunkIds : string [ ] } > = [ ] ;
94
95
95
96
for ( const [ , g ] of groups ) {
96
97
const e = await this . getCachedEmbedding ( g . content ) ;
97
98
if ( e ) {
98
99
for ( const id of g . chunkIds ) {
99
- cachedEmbeds . push ( { chunkId : id , embedding : e } ) ;
100
+ cachedEmbeds . push ( { chunkId : id , embedding : e , tokens : undefined } ) ;
100
101
cachedCount ++ ;
101
102
}
102
103
} else {
@@ -141,7 +142,11 @@ export class EmbeddingGenerator {
141
142
const r = batchResult . results [ i ] ;
142
143
const g = batchGroups [ i ] ;
143
144
for ( const id of g . chunkIds ) {
144
- results . push ( { chunkId : id , embedding : r . embedding } ) ;
145
+ results . push ( {
146
+ chunkId : id ,
147
+ embedding : r . embedding ,
148
+ tokens : r . tokens
149
+ } ) ;
145
150
}
146
151
}
147
152
totalTokens += batchResult . totalTokens ;
@@ -228,7 +233,7 @@ export class EmbeddingGenerator {
228
233
}
229
234
230
235
private async processBatch ( chunks : EmbeddingBatch [ ] ) : Promise < {
231
- results : Array < { chunkId : string ; embedding : number [ ] } > ;
236
+ results : Array < { chunkId : string ; embedding : number [ ] ; tokens ?: number } > ;
232
237
totalTokens : number ;
233
238
} > {
234
239
const inputs = chunks . map ( ( c ) => c . content ) ;
@@ -251,14 +256,19 @@ export class EmbeddingGenerator {
251
256
252
257
const totalTokens = response . usage ?. total_tokens ?? 0 ;
253
258
259
+
254
260
const results = response . data . map ( ( item , idx ) => {
255
261
const chunk = chunks [ idx ] ;
256
262
if ( ! chunk ) {
257
263
throw RAGError . create ( 'validation' , `No chunk found for index ${ idx } ` , { field : 'chunk' } ) ;
258
264
}
265
+
266
+ const chunkTokens = this . calculateChunkTokens ( chunk . content , totalTokens , inputs ) ;
267
+
259
268
return {
260
269
chunkId : chunk . chunkId ,
261
270
embedding : item . embedding ,
271
+ tokens : chunkTokens ,
262
272
} ;
263
273
} ) ;
264
274
@@ -273,6 +283,16 @@ export class EmbeddingGenerator {
273
283
}
274
284
}
275
285
286
+ private calculateChunkTokens ( chunkContent : string , totalTokens : number , allInputs : string [ ] ) : number {
287
+ if ( totalTokens === 0 || allInputs . length === 0 ) return 0 ;
288
+ let totalChars = 0 ;
289
+ for ( const input of allInputs ) {
290
+ totalChars += input . length ;
291
+ }
292
+ if ( totalChars === 0 ) return 0 ;
293
+ return Math . round ( ( chunkContent . length / totalChars ) * totalTokens ) ;
294
+ }
295
+
276
296
private async enforceRateLimit ( ) : Promise < void > {
277
297
const now = Date . now ( ) ;
278
298
0 commit comments