@@ -35,24 +35,32 @@ export function extractToolCalls(messages: Array<Record<string, unknown>> | null
3535}
3636
3737/**
38- * Extract token usage from a message's usage_metadata
38+ * Extract token usage from a message's usage_metadata or response_metadata
39+ * Returns token counts without setting span attributes
3940 */
40- export function extractTokenUsageFromMetadata ( span : Span , message : LangChainMessage ) : void {
41+ export function extractTokenUsageFromMessage ( message : LangChainMessage ) : {
42+ inputTokens : number ;
43+ outputTokens : number ;
44+ totalTokens : number ;
45+ } {
4146 const msg = message as Record < string , unknown > ;
47+ let inputTokens = 0 ;
48+ let outputTokens = 0 ;
49+ let totalTokens = 0 ;
4250
4351 // Extract from usage_metadata (newer format)
4452 if ( msg . usage_metadata && typeof msg . usage_metadata === 'object' ) {
4553 const usage = msg . usage_metadata as Record < string , unknown > ;
4654 if ( typeof usage . input_tokens === 'number' ) {
47- span . setAttribute ( GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE , usage . input_tokens ) ;
55+ inputTokens = usage . input_tokens ;
4856 }
4957 if ( typeof usage . output_tokens === 'number' ) {
50- span . setAttribute ( GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE , usage . output_tokens ) ;
58+ outputTokens = usage . output_tokens ;
5159 }
5260 if ( typeof usage . total_tokens === 'number' ) {
53- span . setAttribute ( GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE , usage . total_tokens ) ;
61+ totalTokens = usage . total_tokens ;
5462 }
55- return ; // Found usage_metadata, no need to check fallback
63+ return { inputTokens , outputTokens , totalTokens } ;
5664 }
5765
5866 // Fallback: Extract from response_metadata.tokenUsage
@@ -61,16 +69,18 @@ export function extractTokenUsageFromMetadata(span: Span, message: LangChainMess
6169 if ( metadata . tokenUsage && typeof metadata . tokenUsage === 'object' ) {
6270 const tokenUsage = metadata . tokenUsage as Record < string , unknown > ;
6371 if ( typeof tokenUsage . promptTokens === 'number' ) {
64- span . setAttribute ( GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE , tokenUsage . promptTokens ) ;
72+ inputTokens = tokenUsage . promptTokens ;
6573 }
6674 if ( typeof tokenUsage . completionTokens === 'number' ) {
67- span . setAttribute ( GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE , tokenUsage . completionTokens ) ;
75+ outputTokens = tokenUsage . completionTokens ;
6876 }
6977 if ( typeof tokenUsage . totalTokens === 'number' ) {
70- span . setAttribute ( GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE , tokenUsage . totalTokens ) ;
78+ totalTokens = tokenUsage . totalTokens ;
7179 }
7280 }
7381 }
82+
83+ return { inputTokens, outputTokens, totalTokens } ;
7484}
7585
7686/**
@@ -147,9 +157,31 @@ export function setResponseAttributes(span: Span, inputMessages: LangChainMessag
147157 const normalizedNewMessages = normalizeLangChainMessages ( newMessages ) ;
148158 span . setAttribute ( GEN_AI_RESPONSE_TEXT_ATTRIBUTE , JSON . stringify ( normalizedNewMessages ) ) ;
149159
160+ // Accumulate token usage across all messages
161+ let totalInputTokens = 0 ;
162+ let totalOutputTokens = 0 ;
163+ let totalTokens = 0 ;
164+
150165 // Extract metadata from messages
151166 for ( const message of newMessages ) {
152- extractTokenUsageFromMetadata ( span , message ) ;
167+ // Accumulate token usage
168+ const tokens = extractTokenUsageFromMessage ( message ) ;
169+ totalInputTokens += tokens . inputTokens ;
170+ totalOutputTokens += tokens . outputTokens ;
171+ totalTokens += tokens . totalTokens ;
172+
173+ // Extract model metadata (last message's metadata wins for model/finish_reason)
153174 extractModelMetadata ( span , message ) ;
154175 }
176+
177+ // Set accumulated token usage on span
178+ if ( totalInputTokens > 0 ) {
179+ span . setAttribute ( GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE , totalInputTokens ) ;
180+ }
181+ if ( totalOutputTokens > 0 ) {
182+ span . setAttribute ( GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE , totalOutputTokens ) ;
183+ }
184+ if ( totalTokens > 0 ) {
185+ span . setAttribute ( GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE , totalTokens ) ;
186+ }
155187}
0 commit comments