@@ -294,6 +294,8 @@ public override async Task<ChatResponse> GetResponseAsync(
294
294
response . Messages = responseMessages ! ;
295
295
response . Usage = totalUsage ;
296
296
297
+ AddUsageTags ( activity , totalUsage ) ;
298
+
297
299
return response ;
298
300
}
299
301
@@ -306,6 +308,7 @@ public override async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseA
306
308
// A single request into this GetStreamingResponseAsync may result in multiple requests to the inner client.
307
309
// Create an activity to group them together for better observability.
308
310
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
309
312
310
313
// Copy the original messages in order to avoid enumerating the original messages multiple times.
311
314
// The IEnumerable can represent an arbitrary amount of work.
@@ -335,6 +338,19 @@ public override async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseA
335
338
336
339
_ = CopyFunctionCalls ( update . Contents , ref functionCallContents ) ;
337
340
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
+
338
354
yield return update ;
339
355
Activity . Current = activity ; // workaround for https://github.com/dotnet/runtime/issues/47802
340
356
}
@@ -389,11 +405,30 @@ public override async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseA
389
405
390
406
if ( modeAndMessages . ShouldTerminate )
391
407
{
392
- yield break ;
408
+ break ;
393
409
}
394
410
395
411
UpdateOptionsForNextIteration ( ref options , response . ChatThreadId ) ;
396
412
}
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
+ }
397
432
}
398
433
399
434
/// <summary>Prepares the various chat message lists after a response from the inner client and before invoking functions.</summary>
0 commit comments