diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.Response.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.Response.cs index 678010118..13190103f 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.Response.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.Response.cs @@ -25,7 +25,11 @@ private async Task InnerCreateResponse(Agent agent, List().FirstOrDefault(); + // Every call the model asked for, not only the first. It routinely asks for several + // independent ones at once, and keeping one made it re-ask for the rest next turn. + var functionCalls = value.OutputItems.OfType().ToList(); + var toolCalls = ToLlmToolCalls(functionCalls); + var functionCall = functionCalls.FirstOrDefault(); var reasoningItem = value.OutputItems.OfType().FirstOrDefault(); var text = value.GetOutputText() ?? string.Empty; var thinkingText = reasoningItem?.GetSummaryText(); @@ -33,7 +37,7 @@ private async Task InnerCreateResponse(Agent agent, List x.FunctionName))}"); responseMessage = new RoleDialogModel(AgentRole.Function, text) { @@ -42,6 +46,7 @@ private async Task InnerCreateResponse(Agent agent, List InnerCreateResponseAsync(Agent agent, var response = await responsesClient.CreateResponseAsync(options); var value = response.Value; - var functionCall = value.OutputItems.OfType().FirstOrDefault(); + // Every call the model asked for, not only the first. It routinely asks for several + // independent ones at once, and keeping one made it re-ask for the rest next turn. + var functionCalls = value.OutputItems.OfType().ToList(); + var toolCalls = ToLlmToolCalls(functionCalls); + var functionCall = functionCalls.FirstOrDefault(); var reasoningItem = value.OutputItems.OfType().FirstOrDefault(); var text = value.GetOutputText() ?? string.Empty; var thinkingText = reasoningItem?.GetSummaryText(); @@ -121,7 +130,7 @@ private async Task InnerCreateResponseAsync(Agent agent, RoleDialogModel responseMessage; if (functionCall != null) { - _logger.LogInformation($"Action: {nameof(InnerCreateResponseAsync)}, Agent: {agent.Name}, ToolCall: {functionCall.FunctionName}"); + _logger.LogInformation($"Action: {nameof(InnerCreateResponseAsync)}, Agent: {agent.Name}, ToolCalls: {string.Join(",", toolCalls.Select(x => x.FunctionName))}"); responseMessage = new RoleDialogModel(AgentRole.Function, text) { @@ -130,6 +139,7 @@ private async Task InnerCreateResponseAsync(Agent agent, ToolCallId = functionCall.CallId, FunctionName = functionCall.FunctionName.NormalizeFunctionName(), FunctionArgs = functionCall.FunctionArguments?.ToString(), + ToolCalls = toolCalls, RenderedInstruction = string.Join("\r\n", renderedInstructions) }; } @@ -221,7 +231,7 @@ private async Task InnerCreateResponseStreamingAsync(Agent agen using var textStream = new RealtimeTextStream(); using var thinkingStream = new RealtimeTextStream(); - FunctionCallResponseItem? functionCall = null; + var functionCalls = new List(); ResponseResult? finalResult = null; ResponseTokenUsage? tokenUsage = null; @@ -312,7 +322,7 @@ private async Task InnerCreateResponseStreamingAsync(Agent agen { if (itemDone.Item is FunctionCallResponseItem fc) { - functionCall = fc; + functionCalls.Add(fc); #if DEBUG _logger.LogDebug($"Tool Call (id: {fc.CallId}) => {fc.FunctionName}({fc.FunctionArguments})"); #endif @@ -342,9 +352,12 @@ private async Task InnerCreateResponseStreamingAsync(Agent agen var allText = textStream.GetText(); var thinkingText = thinkingStream.GetText(); + var toolCalls = ToLlmToolCalls(functionCalls); + var functionCall = functionCalls.FirstOrDefault(); + if (functionCall != null) { - _logger.LogInformation($"Action: {nameof(InnerCreateResponseStreamingAsync)}, Agent: {agent.Name}, ToolCall: {functionCall.FunctionName}"); + _logger.LogInformation($"Action: {nameof(InnerCreateResponseStreamingAsync)}, Agent: {agent.Name}, ToolCalls: {string.Join(",", toolCalls.Select(x => x.FunctionName))}"); responseMessage = new RoleDialogModel(AgentRole.Function, allText) { @@ -353,6 +366,7 @@ private async Task InnerCreateResponseStreamingAsync(Agent agen ToolCallId = functionCall.CallId, FunctionName = functionCall.FunctionName.NormalizeFunctionName(), FunctionArgs = functionCall.FunctionArguments?.ToString(), + ToolCalls = toolCalls, RenderedInstruction = string.Join("\r\n", renderedInstructions) }; } @@ -822,4 +836,9 @@ private void SetResponseFormat(CreateResponseOptions options, AgentLlmConfig? ll } : null; } #endregion + + private static List ToLlmToolCalls(IEnumerable? functionCalls) + => (functionCalls ?? []) + .Select(x => new LlmToolCall(x.CallId, x.FunctionName, x.FunctionArguments?.ToString())) + .ToList(); }