From f8f4f386e905cbf914452c7ede4f4db83a7143aa Mon Sep 17 00:00:00 2001 From: "aden.chen" Date: Wed, 28 Jan 2026 14:13:23 +0800 Subject: [PATCH 1/4] Add retry for missing FunctionName in agent invocation Introduce a retry mechanism in RoutingService.InvokeAgent to handle cases where the FunctionName is null or whitespace. Add IsRetry property to InvokeAgentOptions to prevent infinite retry loops. If a retry still results in a missing FunctionName, log an error and return a user-facing error message. Set IsRetry default to false. This improves robustness and user feedback for invalid function call requests. --- .../Routing/Models/InvokeOptions.cs | 4 +- .../Routing/RoutingService.InvokeAgent.cs | 45 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/InvokeOptions.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/InvokeOptions.cs index 5e0317872..35b69dfc7 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/InvokeOptions.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/InvokeOptions.cs @@ -8,13 +8,15 @@ public abstract class InvokeOptions public class InvokeAgentOptions : InvokeOptions { public bool UseStream { get; set; } + public bool IsRetry { get; set; } public static InvokeAgentOptions Default() { return new() { From = InvokeSource.Manual, - UseStream = false + UseStream = false, + IsRetry = false }; } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs index fdaddc85e..25c6cbbe2 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs @@ -62,6 +62,13 @@ public async Task InvokeAgent( message.IsStreaming = response.IsStreaming; message.MessageLabel = response.MessageLabel; + // Handle case when FunctionName is null or whitespace - retry once + var retryResult = await HandleEmptyFunctionNameRetry(agentId, message, dialogs, options); + if (retryResult.HasValue) + { + return retryResult.Value; + } + await InvokeFunction(message, dialogs, options); } else @@ -140,4 +147,42 @@ private async Task InvokeFunction( return true; } + + /// + /// Handles the case when FunctionName is null or whitespace by retrying once. + /// Returns null if FunctionName is valid (no action needed), + /// otherwise returns the result of handling (retry result or true if error was set). + /// + private async Task HandleEmptyFunctionNameRetry( + string agentId, + RoleDialogModel message, + List dialogs, + InvokeAgentOptions? options) + { + if (string.IsNullOrWhiteSpace(message.FunctionName)) + { + if (!(options?.IsRetry ?? false)) + { + // Retry once by recursively calling InvokeAgent + _logger.LogWarning($"Function name is empty, retrying InvokeAgent for agent {agentId}"); + options ??= InvokeAgentOptions.Default(); + options.IsRetry = true; + var retryResult = await InvokeAgent(agentId, dialogs, options); + return retryResult; + } + else + { + // Already retried once, avoid infinite loop + _logger.LogError($"Function name is still empty after retry for agent {agentId}, stopping to avoid infinite loop"); + message.StopCompletion = true; + message.Content = "I received a function call request but the function name is missing. Please try again."; + message.Role = AgentRole.Assistant; + dialogs.Add(message); + Context.AddDialogs([message]); + return true; + } + } + + return null; + } } From 37d2a02c9ec7f138cf2d022a5d88181b490b0b06 Mon Sep 17 00:00:00 2001 From: "aden.chen" Date: Thu, 29 Jan 2026 15:19:40 +0800 Subject: [PATCH 2/4] Revert "Add retry for missing FunctionName in agent invocation" This reverts commit f8f4f386e905cbf914452c7ede4f4db83a7143aa. --- .../Routing/Models/InvokeOptions.cs | 4 +- .../Routing/RoutingService.InvokeAgent.cs | 45 ------------------- 2 files changed, 1 insertion(+), 48 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/InvokeOptions.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/InvokeOptions.cs index 35b69dfc7..5e0317872 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/InvokeOptions.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/InvokeOptions.cs @@ -8,15 +8,13 @@ public abstract class InvokeOptions public class InvokeAgentOptions : InvokeOptions { public bool UseStream { get; set; } - public bool IsRetry { get; set; } public static InvokeAgentOptions Default() { return new() { From = InvokeSource.Manual, - UseStream = false, - IsRetry = false + UseStream = false }; } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs index 25c6cbbe2..fdaddc85e 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs @@ -62,13 +62,6 @@ public async Task InvokeAgent( message.IsStreaming = response.IsStreaming; message.MessageLabel = response.MessageLabel; - // Handle case when FunctionName is null or whitespace - retry once - var retryResult = await HandleEmptyFunctionNameRetry(agentId, message, dialogs, options); - if (retryResult.HasValue) - { - return retryResult.Value; - } - await InvokeFunction(message, dialogs, options); } else @@ -147,42 +140,4 @@ private async Task InvokeFunction( return true; } - - /// - /// Handles the case when FunctionName is null or whitespace by retrying once. - /// Returns null if FunctionName is valid (no action needed), - /// otherwise returns the result of handling (retry result or true if error was set). - /// - private async Task HandleEmptyFunctionNameRetry( - string agentId, - RoleDialogModel message, - List dialogs, - InvokeAgentOptions? options) - { - if (string.IsNullOrWhiteSpace(message.FunctionName)) - { - if (!(options?.IsRetry ?? false)) - { - // Retry once by recursively calling InvokeAgent - _logger.LogWarning($"Function name is empty, retrying InvokeAgent for agent {agentId}"); - options ??= InvokeAgentOptions.Default(); - options.IsRetry = true; - var retryResult = await InvokeAgent(agentId, dialogs, options); - return retryResult; - } - else - { - // Already retried once, avoid infinite loop - _logger.LogError($"Function name is still empty after retry for agent {agentId}, stopping to avoid infinite loop"); - message.StopCompletion = true; - message.Content = "I received a function call request but the function name is missing. Please try again."; - message.Role = AgentRole.Assistant; - dialogs.Add(message); - Context.AddDialogs([message]); - return true; - } - } - - return null; - } } From 9e93f8ef996f5d404172fc8ff9d413c8cbba5b4b Mon Sep 17 00:00:00 2001 From: "aden.chen" Date: Thu, 29 Jan 2026 15:42:44 +0800 Subject: [PATCH 3/4] logging infomation when LLM response function call --- .../Providers/Chat/ChatCompletionProvider.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs index 198836d39..7fb533c79 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs @@ -54,6 +54,8 @@ public async Task GetChatCompletions(Agent agent, List x.FunctionName))}"); + var toolCall = value.ToolCalls.FirstOrDefault(); responseMessage = new RoleDialogModel(AgentRole.Function, text) { From 6acf254c32efb51c84b83bbc3c361eb32818a157 Mon Sep 17 00:00:00 2001 From: "aden.chen" Date: Thu, 29 Jan 2026 15:49:47 +0800 Subject: [PATCH 4/4] logging function call response for azure openAI provider --- .../Providers/Chat/ChatCompletionProvider.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs index dc9a0fbc5..f10748c7a 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs @@ -61,6 +61,8 @@ public async Task GetChatCompletions(Agent agent, List x.FunctionName))}"); + var toolCall = value.ToolCalls.FirstOrDefault(); responseMessage = new RoleDialogModel(AgentRole.Function, text) {