@@ -294,6 +294,8 @@ public override async Task<ChatResponse> GetResponseAsync(
294294 response . Messages = responseMessages ! ;
295295 response . Usage = totalUsage ;
296296
297+ AddUsageTags ( activity , totalUsage ) ;
298+
297299 return response ;
298300 }
299301
@@ -306,6 +308,7 @@ public override async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseA
306308 // A single request into this GetStreamingResponseAsync may result in multiple requests to the inner client.
307309 // Create an activity to group them together for better observability.
308310 using Activity ? activity = _activitySource ? . StartActivity ( nameof ( FunctionInvokingChatClient ) ) ;
311+ UsageDetails ? totalUsage = activity is { IsAllDataRequested : true } ? new ( ) : null ; // tracked usage across all turns, to be used for activity purposes
309312
310313 // Copy the original messages in order to avoid enumerating the original messages multiple times.
311314 // The IEnumerable can represent an arbitrary amount of work.
@@ -335,6 +338,19 @@ public override async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseA
335338
336339 _ = CopyFunctionCalls ( update . Contents , ref functionCallContents ) ;
337340
341+ if ( totalUsage is not null )
342+ {
343+ IList < AIContent > contents = update . Contents ;
344+ int contentsCount = contents . Count ;
345+ for ( int i = 0 ; i < contentsCount ; i ++ )
346+ {
347+ if ( contents [ i ] is UsageContent uc )
348+ {
349+ totalUsage . Add ( uc . Details ) ;
350+ }
351+ }
352+ }
353+
338354 yield return update ;
339355 Activity . Current = activity ; // workaround for https://github.com/dotnet/runtime/issues/47802
340356 }
@@ -389,11 +405,30 @@ public override async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseA
389405
390406 if ( modeAndMessages . ShouldTerminate )
391407 {
392- yield break ;
408+ break ;
393409 }
394410
395411 UpdateOptionsForNextIteration ( ref options , response . ChatThreadId ) ;
396412 }
413+
414+ AddUsageTags ( activity , totalUsage ) ;
415+ }
416+
417+ /// <summary>Adds tags to <paramref name="activity"/> for usage details in <paramref name="usage"/>.</summary>
418+ private static void AddUsageTags ( Activity ? activity , UsageDetails ? usage )
419+ {
420+ if ( usage is not null && activity is { IsAllDataRequested : true } )
421+ {
422+ if ( usage . InputTokenCount is long inputTokens )
423+ {
424+ _ = activity . AddTag ( OpenTelemetryConsts . GenAI . Response . InputTokens , ( int ) inputTokens ) ;
425+ }
426+
427+ if ( usage . OutputTokenCount is long outputTokens )
428+ {
429+ _ = activity . AddTag ( OpenTelemetryConsts . GenAI . Response . OutputTokens , ( int ) outputTokens ) ;
430+ }
431+ }
397432 }
398433
399434 /// <summary>Prepares the various chat message lists after a response from the inner client and before invoking functions.</summary>
0 commit comments