Skip to content

Commit 386c634

Browse files
authored
Augment FunctionInvokingChatClient's span with token counts (#6296)
1 parent 68b25ae commit 386c634

File tree

1 file changed

+36
-1
lines changed

1 file changed

+36
-1
lines changed

src/Libraries/Microsoft.Extensions.AI/ChatCompletion/FunctionInvokingChatClient.cs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)