11/* eslint-disable no-useless-catch */
2- import { ModelScaler } from "@/services/LLM/ModelScaler" ;
32import { openRouterClient } from "@constants/openRouterClient" ;
43import { ILLMProvider , IMessage } from "@services/LLM/ILLMProvider" ;
54import { MessageContextManager } from "@services/LLM/MessageContextManager" ;
65import { ModelInfo } from "@services/LLM/ModelInfo" ;
76import { ModelManager } from "@services/LLM/ModelManager" ;
7+ import { ModelScaler } from "@services/LLM/ModelScaler" ;
88import {
99 formatMessageContent ,
1010 IMessageContent ,
@@ -35,6 +35,23 @@ interface IStreamError {
3535 details ?: Record < string , unknown > ;
3636}
3737
38+ interface PriceInfo {
39+ prompt : string ;
40+ completion : string ;
41+ image : string ;
42+ request : string ;
43+ }
44+
45+ interface UsageEntry {
46+ prompt_tokens : number ;
47+ completion_tokens : number ;
48+ total_tokens : number ;
49+ }
50+
51+ interface UsageHistory {
52+ [ modelName : string ] : UsageEntry [ ] ;
53+ }
54+
3855@autoInjectable ( )
3956export class OpenRouterAPI implements ILLMProvider {
4057 private readonly httpClient : typeof openRouterClient ;
@@ -150,6 +167,56 @@ export class OpenRouterAPI implements ILLMProvider {
150167 } ) ) ;
151168 }
152169
170+ private calculateCosts ( priceAll : PriceInfo , usage : UsageHistory ) {
171+ const promptRate = parseFloat ( priceAll . prompt ) ;
172+ const completionRate = parseFloat ( priceAll . completion ) ;
173+
174+ let currentCost = 0 ;
175+ let totalCost = 0 ;
176+
177+ for ( const modelKey in usage ) {
178+ const modelUsage = usage [ modelKey ] ;
179+ if ( modelUsage . length > 0 ) {
180+ // Calculate total cost for the model
181+ const modelTotalCost = modelUsage . reduce ( ( sum , entry ) => {
182+ const cost =
183+ entry . prompt_tokens * promptRate +
184+ entry . completion_tokens * completionRate ;
185+ return sum + cost ;
186+ } , 0 ) ;
187+
188+ totalCost += modelTotalCost ;
189+
190+ // Calculate current cost (last usage entry for the model)
191+ const lastUsage = modelUsage [ modelUsage . length - 1 ] ;
192+ currentCost =
193+ lastUsage . prompt_tokens * promptRate +
194+ lastUsage . completion_tokens * completionRate ;
195+ }
196+ }
197+
198+ return {
199+ currentCost,
200+ totalCost,
201+ } ;
202+ }
203+
204+ private logChatCosts (
205+ priceAll : PriceInfo | undefined ,
206+ usage : UsageHistory ,
207+ ) : void {
208+ if ( priceAll && usage ) {
209+ const { currentCost, totalCost } = this . calculateCosts ( priceAll , usage ) ;
210+
211+ console . log ( "Current Chat Cost: $" , currentCost . toFixed ( 10 ) ) ;
212+ console . log ( "Total Chat Cost: $" , totalCost . toFixed ( 10 ) ) ;
213+ } else {
214+ console . log (
215+ "PriceInfo or UsageHistory is undefined, cannot calculate costs." ,
216+ ) ;
217+ }
218+ }
219+
153220 async sendMessage (
154221 model : string ,
155222 message : string ,
@@ -177,9 +244,9 @@ export class OpenRouterAPI implements ILLMProvider {
177244 this . messageContextManager . addMessage ( "user" , message ) ;
178245 this . messageContextManager . addMessage ( "assistant" , assistantMessage ) ;
179246
180- await this . modelInfo . logCurrentModelUsage (
181- this . messageContextManager . getTotalTokenCount ( ) ,
182- ) ;
247+ const priceAll = this . modelInfo . getCurrentModelInfo ( ) ?. pricing ;
248+ const usage = this . modelInfo . getUsageHistory ( ) ;
249+ this . logChatCosts ( priceAll , usage ) ;
183250
184251 return assistantMessage ;
185252 } catch ( error ) {
@@ -296,10 +363,10 @@ export class OpenRouterAPI implements ILLMProvider {
296363 ) ;
297364 }
298365
299- private processCompleteMessage ( message : string ) : {
366+ private async processCompleteMessage ( message : string ) : Promise < {
300367 content : string ;
301368 error ?: IStreamError ;
302- } {
369+ } > {
303370 try {
304371 const jsonStr = message . replace ( / ^ d a t a : / , "" ) . trim ( ) ;
305372 if (
@@ -316,6 +383,10 @@ export class OpenRouterAPI implements ILLMProvider {
316383 return { content : "" , error : parsed . error } ;
317384 }
318385
386+ if ( parsed . usage ) {
387+ await this . modelInfo . logDetailedUsage ( parsed . usage ) ;
388+ }
389+
319390 const deltaContent = parsed . choices ?. [ 0 ] ?. delta ?. content ;
320391 if ( ! deltaContent ) {
321392 return { content : "" } ;
@@ -330,10 +401,10 @@ export class OpenRouterAPI implements ILLMProvider {
330401 }
331402 }
332403
333- private parseStreamChunk ( chunk : string ) : {
404+ private async parseStreamChunk ( chunk : string ) : Promise < {
334405 content : string ;
335406 error ?: IStreamError ;
336- } {
407+ } > {
337408 this . streamBuffer += chunk ;
338409
339410 let content = "" ;
@@ -343,7 +414,7 @@ export class OpenRouterAPI implements ILLMProvider {
343414 this . streamBuffer = messages . pop ( ) || "" ;
344415
345416 for ( const message of messages ) {
346- const result = this . processCompleteMessage ( message ) ;
417+ const result = await this . processCompleteMessage ( message ) ;
347418 if ( result . error ) error = result . error ;
348419 content += result . content ;
349420 }
@@ -390,8 +461,8 @@ export class OpenRouterAPI implements ILLMProvider {
390461 const stream = response . data ;
391462
392463 await new Promise < void > ( ( resolve , reject ) => {
393- stream . on ( "data" , ( chunk : Buffer ) => {
394- const { content, error } = this . parseStreamChunk (
464+ stream . on ( "data" , async ( chunk : Buffer ) => {
465+ const { content, error } = await this . parseStreamChunk (
395466 chunk . toString ( ) ,
396467 ) ;
397468
@@ -400,7 +471,7 @@ export class OpenRouterAPI implements ILLMProvider {
400471 const llmError = new LLMError (
401472 error . message || "Stream error" ,
402473 "STREAM_ERROR" ,
403- error . details ,
474+ error . details || { } ,
404475 ) ;
405476 this . handleStreamError ( llmError , message , callback ) ;
406477 reject ( llmError ) ;
@@ -413,17 +484,17 @@ export class OpenRouterAPI implements ILLMProvider {
413484 }
414485 } ) ;
415486
416- stream . on ( "end" , ( ) => {
487+ stream . on ( "end" , async ( ) => {
417488 if ( this . streamBuffer ) {
418- const { content, error } = this . processCompleteMessage (
489+ const { content, error } = await this . parseStreamChunk (
419490 this . streamBuffer ,
420491 ) ;
421492 if ( error ) {
422493 console . log ( JSON . stringify ( error , null , 2 ) ) ;
423494 const llmError = new LLMError (
424495 error . message || "Stream error" ,
425496 "STREAM_ERROR" ,
426- error . details ,
497+ error . details || { } ,
427498 ) ;
428499 this . handleStreamError ( llmError , message , callback ) ;
429500 reject ( llmError ) ;
@@ -449,9 +520,9 @@ export class OpenRouterAPI implements ILLMProvider {
449520 assistantMessage ,
450521 ) ;
451522
452- await this . modelInfo . logCurrentModelUsage (
453- this . messageContextManager . getTotalTokenCount ( ) ,
454- ) ;
523+ const priceAll = this . modelInfo . getCurrentModelInfo ( ) ?. pricing ;
524+ const usage = this . modelInfo . getUsageHistory ( ) ;
525+ this . logChatCosts ( priceAll , usage ) ;
455526 }
456527 } catch ( error ) {
457528 throw error ;
@@ -470,9 +541,9 @@ export class OpenRouterAPI implements ILLMProvider {
470541 this . messageContextManager . addMessage ( "user" , message ) ;
471542 this . messageContextManager . addMessage ( "assistant" , assistantMessage ) ;
472543
473- await this . modelInfo . logCurrentModelUsage (
474- this . messageContextManager . getTotalTokenCount ( ) ,
475- ) ;
544+ const priceAll = this . modelInfo . getCurrentModelInfo ( ) ?. pricing ;
545+ const usage = this . modelInfo . getUsageHistory ( ) ;
546+ this . logChatCosts ( priceAll , usage ) ;
476547 }
477548 }
478549 }
0 commit comments