@@ -19,6 +19,9 @@ import {
1919 parseBase64DataUrl ,
2020 isDataContentBlock ,
2121 convertToProviderContentBlock ,
22+ InputTokenDetails ,
23+ OutputTokenDetails ,
24+ ModalitiesTokenDetails ,
2225} from "@langchain/core/messages" ;
2326import {
2427 ChatGeneration ,
@@ -47,6 +50,7 @@ import type {
4750 GeminiLogprobsResult ,
4851 GeminiLogprobsResultCandidate ,
4952 GeminiLogprobsTopCandidate ,
53+ ModalityTokenCount ,
5054} from "../types.js" ;
5155import { GoogleAISafetyError } from "./safety.js" ;
5256import { MediaBlob } from "../experimental/utils/media_core.js" ;
@@ -855,6 +859,58 @@ export function getGeminiAPI(config?: GeminiAPIConfig): GoogleAIAPI {
855859 } ;
856860 }
857861
862+ function addModalityCounts (
863+ modalityTokenCounts : ModalityTokenCount [ ] ,
864+ details : InputTokenDetails | OutputTokenDetails
865+ ) : void {
866+ modalityTokenCounts ?. forEach ( ( modalityTokenCount ) => {
867+ const { modality, tokenCount } = modalityTokenCount ;
868+ const modalityLc : keyof ModalitiesTokenDetails =
869+ modality . toLowerCase ( ) as keyof ModalitiesTokenDetails ;
870+ const currentCount = details [ modalityLc ] ?? 0 ;
871+ // eslint-disable-next-line no-param-reassign
872+ details [ modalityLc ] = currentCount + tokenCount ;
873+ } ) ;
874+ }
875+
876+ function responseToUsageMetadata (
877+ response : GoogleLLMResponse
878+ ) : UsageMetadata | undefined {
879+ if ( "usageMetadata" in response . data ) {
880+ const data : GenerateContentResponseData = response ?. data ;
881+ const usageMetadata = data ?. usageMetadata ;
882+
883+ const input_tokens = usageMetadata . promptTokenCount ?? 0 ;
884+ const candidatesTokenCount = usageMetadata . candidatesTokenCount ?? 0 ;
885+ const thoughtsTokenCount = usageMetadata . thoughtsTokenCount ?? 0 ;
886+ const output_tokens = candidatesTokenCount + thoughtsTokenCount ;
887+ const total_tokens =
888+ usageMetadata . totalTokenCount ?? input_tokens + output_tokens ;
889+
890+ const input_token_details : InputTokenDetails = { } ;
891+ addModalityCounts ( usageMetadata . promptTokensDetails , input_token_details ) ;
892+
893+ const output_token_details : OutputTokenDetails = { } ;
894+ addModalityCounts (
895+ usageMetadata ?. candidatesTokensDetails ,
896+ output_token_details
897+ ) ;
898+ if ( typeof usageMetadata ?. thoughtsTokenCount === "number" ) {
899+ output_token_details . reasoning = usageMetadata . thoughtsTokenCount ;
900+ }
901+
902+ const ret : UsageMetadata = {
903+ input_tokens,
904+ output_tokens,
905+ total_tokens,
906+ input_token_details,
907+ output_token_details,
908+ } ;
909+ return ret ;
910+ }
911+ return undefined ;
912+ }
913+
858914 function responseToGenerationInfo ( response : GoogleLLMResponse ) {
859915 const data =
860916 // eslint-disable-next-line no-nested-ternary
@@ -890,11 +946,7 @@ export function getGeminiAPI(config?: GeminiAPIConfig): GoogleAIAPI {
890946 // Only add the usage_metadata on the last chunk
891947 // sent while streaming (see issue 8102).
892948 if ( typeof finish_reason === "string" ) {
893- ret . usage_metadata = {
894- prompt_token_count : data . usageMetadata ?. promptTokenCount ,
895- candidates_token_count : data . usageMetadata ?. candidatesTokenCount ,
896- total_token_count : data . usageMetadata ?. totalTokenCount ,
897- } ;
949+ ret . usage_metadata = responseToUsageMetadata ( response ) ;
898950 }
899951
900952 return ret ;
@@ -1115,15 +1167,7 @@ export function getGeminiAPI(config?: GeminiAPIConfig): GoogleAIAPI {
11151167 const lastContent = gen . content [ gen . content . length - 1 ] ;
11161168
11171169 // Add usage metadata
1118- let usageMetadata : UsageMetadata | undefined ;
1119- if ( "usageMetadata" in response . data ) {
1120- usageMetadata = {
1121- input_tokens : response . data . usageMetadata . promptTokenCount as number ,
1122- output_tokens : response . data . usageMetadata
1123- . candidatesTokenCount as number ,
1124- total_tokens : response . data . usageMetadata . totalTokenCount as number ,
1125- } ;
1126- }
1170+ const usage_metadata = responseToUsageMetadata ( response ) ;
11271171
11281172 // Add thinking / reasoning
11291173 // if (gen.reasoning && gen.reasoning.length > 0) {
@@ -1134,7 +1178,7 @@ export function getGeminiAPI(config?: GeminiAPIConfig): GoogleAIAPI {
11341178 const message = new AIMessageChunk ( {
11351179 content : combinedContent ,
11361180 additional_kwargs : kwargs ,
1137- usage_metadata : usageMetadata ,
1181+ usage_metadata,
11381182 tool_calls : combinedToolCalls . tool_calls ,
11391183 invalid_tool_calls : combinedToolCalls . invalid_tool_calls ,
11401184 } ) ;
0 commit comments