Ensure non-streaming usage data from function calling is in history (#5676)

It's already yielded during streaming, but it's not being surfaced for non-streaming. Do so by manufacturing a new UsageContent for the UsageDetails and adding that to the response message that's added to the history.
This commit is contained in:
Stephen Toub 2024-11-20 10:27:18 -05:00
Родитель e12f1d76e4
Коммит e985af6c85
2 изменённых файлов: 20 добавлений и 1 удалений

Просмотреть файл

@ -252,6 +252,13 @@ public partial class FunctionInvokingChatClient : DelegatingChatClient
}
}
// If the original chat completion included usage data,
// add that into the message so it's available in the history.
if (KeepFunctionCallingMessages && response.Usage is { } usage)
{
response.Message.Contents = [.. response.Message.Contents, new UsageContent(usage)];
}
// Add the responses from the function calls into the history.
var modeAndMessages = await ProcessFunctionCallsAsync(chatMessages, options, functionCallContents, iteration, cancellationToken).ConfigureAwait(false);
if (modeAndMessages.MessagesAdded is not null)

Просмотреть файл

@ -163,13 +163,25 @@ public abstract class ChatClientIntegrationTests : IDisposable
int secretNumber = 42;
var response = await chatClient.CompleteAsync("What is the current secret number?", new()
List<ChatMessage> messages =
[
new(ChatRole.User, "What is the current secret number?")
];
var response = await chatClient.CompleteAsync(messages, new()
{
Tools = [AIFunctionFactory.Create(() => secretNumber, "GetSecretNumber")]
});
Assert.Single(response.Choices);
Assert.Contains(secretNumber.ToString(), response.Message.Text);
if (response.Usage is { } finalUsage)
{
UsageContent? intermediate = messages.SelectMany(m => m.Contents).OfType<UsageContent>().FirstOrDefault();
Assert.NotNull(intermediate);
Assert.True(finalUsage.TotalTokenCount > intermediate.Details.TotalTokenCount);
}
}
[ConditionalFact]