diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs index 44d41f319..df2d63132 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs @@ -5,6 +5,9 @@ public class RoutingArgs [JsonPropertyName("function")] public string Function { get; set; } = "route_to_agent"; + [JsonPropertyName("function_args")] + public string? FunctionArgs { get; set; } + /// /// The reason why you select this function or agent /// diff --git a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/HFReasoner.cs b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/HFReasoner.cs index 7af5e63e9..9f1b5fd52 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/HFReasoner.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/HFReasoner.cs @@ -51,9 +51,21 @@ public async Task GetNextInstruction(Agent router, string m } }; var response = await completion.GetChatCompletions(router, dialogs); - var inst = response.Content.JsonContent(); + if (inst != null) + { + if (!string.IsNullOrEmpty(response.FunctionName)) + { + inst.Function = response.FunctionName; + } + + if (!string.IsNullOrEmpty(response.FunctionArgs)) + { + inst.FunctionArgs = response.FunctionArgs; + } + } + // Fix LLM malformed response await ReasonerHelper.FixMalformedResponse(_services, inst); @@ -73,7 +85,14 @@ public async Task AgentExecuting(Agent router, FunctionCallFromLlm inst, R // Set user content as Planner's question message.FunctionName = inst.Function; - message.FunctionArgs = inst.Arguments == null ? "{}" : JsonSerializer.Serialize(inst.Arguments); + if (!string.IsNullOrEmpty(inst.FunctionArgs)) + { + message.FunctionArgs = inst.FunctionArgs; + } + else + { + message.FunctionArgs = inst.Arguments == null ? "{}" : JsonSerializer.Serialize(inst.Arguments); + } } return true; diff --git a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/InstructExecutor.cs b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/InstructExecutor.cs index c71d0f07e..ad2d7370c 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/InstructExecutor.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/InstructExecutor.cs @@ -31,6 +31,11 @@ await HookEmitter.Emit(_services, async hook => await hook.OnRouti } message.FunctionArgs = JsonSerializer.Serialize(inst); + if (!string.IsNullOrEmpty(inst.FunctionArgs)) + { + message.FunctionArgs = inst.FunctionArgs; + } + if (!string.IsNullOrEmpty(message.FunctionName)) { var msg = RoleDialogModel.From(message, role: AgentRole.Function); @@ -56,7 +61,7 @@ await HookEmitter.Emit(_services, async hook => await hook.OnRouti message = RoleDialogModel.From(message, role: AgentRole.Assistant, content: content); dialogs.Add(message); } - else + else if (!message.StopCompletion) { var state = _services.GetRequiredService(); var useStreamMsg = state.GetState("use_stream_message"); @@ -71,6 +76,7 @@ await HookEmitter.Emit(_services, async hook => await hook.OnRouti var response = dialogs.Last(); response.Instruction = inst; + response.StopCompletion = message.StopCompletion; return response; } diff --git a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/NaiveReasoner.cs b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/NaiveReasoner.cs index 39e8be430..5cab06725 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/NaiveReasoner.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/NaiveReasoner.cs @@ -57,6 +57,18 @@ public async Task GetNextInstruction(Agent router, string m var response = await completion.GetChatCompletions(router, dialogs); var inst = (response.FunctionArgs ?? response.Content).JsonContent(); + if (inst != null) + { + if (!string.IsNullOrEmpty(response.FunctionName)) + { + inst.Function = response.FunctionName; + } + + if (!string.IsNullOrEmpty(response.FunctionArgs)) + { + inst.FunctionArgs = response.FunctionArgs; + } + } // Fix LLM malformed response await ReasonerHelper.FixMalformedResponse(_services, inst); @@ -68,7 +80,14 @@ public Task AgentExecuting(Agent router, FunctionCallFromLlm inst, RoleDia { // Set user content as Planner's question message.FunctionName = inst.Function; - message.FunctionArgs = inst.Arguments == null ? "{}" : JsonSerializer.Serialize(inst.Arguments); + if (!string.IsNullOrEmpty(inst.FunctionArgs)) + { + message.FunctionArgs = inst.FunctionArgs; + } + else + { + message.FunctionArgs = inst.Arguments == null ? "{}" : JsonSerializer.Serialize(inst.Arguments); + } return Task.FromResult(true); } diff --git a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/OneStepForwardReasoner.cs b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/OneStepForwardReasoner.cs index 3af0614da..49463238f 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/OneStepForwardReasoner.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/OneStepForwardReasoner.cs @@ -67,8 +67,22 @@ public async Task GetNextInstruction(Agent router, string m // eliminating format drift where the LLM completes with finishReason=stop and returns // free text or JSON in Content instead of a structured function call. var response = await GetChatCompletionsWithScopedState(completion, router, dialogs, "tool_choice", "required"); - var inst = response.FunctionArgs?.JsonContent(); + + if (inst != null) + { + if (!string.IsNullOrEmpty(response.FunctionName)) + { + inst.Function = response.FunctionName; + } + + if (!string.IsNullOrEmpty(response.FunctionArgs)) + { + inst.FunctionArgs = response.FunctionArgs; + } + } + + _logger.LogInformation("[OneStepForwardReasoner] ConversationId: {ConversationId}, MessageId: {MessageId}, Next instruction: {Instruction}", _services.GetRequiredService().ConversationId, messageId, response.FunctionArgs); @@ -82,7 +96,14 @@ public Task AgentExecuting(Agent router, FunctionCallFromLlm inst, RoleDia { // Set user content as Planner's question message.FunctionName = inst.Function; - message.FunctionArgs = inst.Arguments == null ? "{}" : JsonSerializer.Serialize(inst.Arguments); + if (!string.IsNullOrEmpty(inst.FunctionArgs)) + { + message.FunctionArgs = inst.FunctionArgs; + } + else + { + message.FunctionArgs = inst.Arguments == null ? "{}" : JsonSerializer.Serialize(inst.Arguments); + } return Task.FromResult(true); }