diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/DecomposedStep.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/DecomposedStep.cs new file mode 100644 index 00000000..d49f6f7d --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/DecomposedStep.cs @@ -0,0 +1,9 @@ +namespace BotSharp.Abstraction.Routing.Models; + +public class DecomposedStep +{ + public string Description { get; set; } + + [JsonPropertyName("total_remaining_steps")] + public int TotalRemainingSteps { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Planning/IPlaner.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Planning/IPlaner.cs index 4053f81c..cbe16210 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Planning/IPlaner.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Planning/IPlaner.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Functions.Models; +using BotSharp.Abstraction.Routing.Models; namespace BotSharp.Abstraction.Routing.Planning; @@ -11,4 +12,8 @@ public interface IPlaner Task GetNextInstruction(Agent router, string messageId); Task AgentExecuting(Agent router, FunctionCallFromLlm inst, RoleDialogModel message); Task AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message); + bool HideDialogContext => false; + Task GetDecomposedStepAsync(Agent router, string messageId, List dialogs) + => throw new NotImplementedException(""); + int MaxLoopCount => 5; } diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index e6df922d..75287927 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -53,6 +53,7 @@ + @@ -75,6 +76,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/src/Infrastructure/BotSharp.Core/Routing/Planning/HFPlanner.cs b/src/Infrastructure/BotSharp.Core/Routing/Planning/HFPlanner.cs index 9cff003a..d852ef77 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Planning/HFPlanner.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Planning/HFPlanner.cs @@ -42,7 +42,7 @@ public class HFPlanner : IPlaner { new RoleDialogModel(AgentRole.User, next) { - FunctionName = nameof(NaivePlanner), + FunctionName = nameof(HFPlanner), MessageId = messageId } }; diff --git a/src/Infrastructure/BotSharp.Core/Routing/Planning/SequentialPlanner.cs b/src/Infrastructure/BotSharp.Core/Routing/Planning/SequentialPlanner.cs index 1b24152a..616ee1da 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Planning/SequentialPlanner.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Planning/SequentialPlanner.cs @@ -4,6 +4,7 @@ using BotSharp.Abstraction.Routing; using BotSharp.Abstraction.Routing.Models; using BotSharp.Abstraction.Routing.Planning; using BotSharp.Abstraction.Templating; +using System.Drawing; namespace BotSharp.Core.Routing.Planning; @@ -12,6 +13,9 @@ public class SequentialPlanner : IPlaner private readonly IServiceProvider _services; private readonly ILogger _logger; + public bool HideDialogContext => true; + public int MaxLoopCount => 10; + public SequentialPlanner(IServiceProvider services, ILogger logger) { _services = services; @@ -48,7 +52,7 @@ public class SequentialPlanner : IPlaner { new RoleDialogModel(AgentRole.User, next) { - FunctionName = nameof(NaivePlanner), + FunctionName = nameof(SequentialPlanner), MessageId = messageId } }; @@ -110,4 +114,55 @@ public class SequentialPlanner : IPlaner { }); } + + public async Task GetDecomposedStepAsync(Agent router, string messageId, List dialogs) + { + var systemPrompt = GetDecomposeTaskPrompt(router); + + var inst = new DecomposedStep(); + + // chat completion + var completion = CompletionProvider.GetChatCompletion(_services, + model: "llm-gpt4"); + + int retryCount = 0; + while (retryCount < 2) + { + string text = string.Empty; + try + { + var response = await completion.GetChatCompletions(new Agent + { + Id = router.Id, + Name = nameof(SequentialPlanner), + Instruction = systemPrompt + }, dialogs); + + text = response.Content; + Console.WriteLine(text, Color.Red); + inst = response.Content.JsonContent(); + break; + } + catch (Exception ex) + { + _logger.LogError($"{ex.Message}: {text}"); + } + finally + { + retryCount++; + } + } + + return inst; + } + + private string GetDecomposeTaskPrompt(Agent router) + { + var template = router.Templates.First(x => x.Name == "planner_prompt.sequential.get_remaining_task").Content; + + var render = _services.GetRequiredService(); + return render.Render(template, new Dictionary + { + }); + } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index 8a34d684..90859f69 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -80,7 +80,7 @@ public partial class RoutingService : IRoutingService context.Push(_router.Id); int loopCount = 0; - while (loopCount < 5 && !context.IsEmpty) + while (loopCount < planner.MaxLoopCount && !context.IsEmpty) { loopCount++; @@ -101,7 +101,25 @@ public partial class RoutingService : IRoutingService await planner.AgentExecuting(_router, inst, message); // Handle instruction by Executor - response = await executor.Execute(this, inst, message, dialogs); + if (planner.HideDialogContext) + { + /*var args = JsonSerializer.Serialize(inst.Arguments); + if (args.Length > 3) + { + inst.Question += $"\r\nargs: {args}"; + }*/ + var step = await planner.GetDecomposedStepAsync(_router, message.MessageId, dialogs); + var maskDialogs = new List + { + new RoleDialogModel(AgentRole.User, step.Description) + }; + response = await executor.Execute(this, inst, message, maskDialogs); + dialogs.AddRange(maskDialogs.Skip(1)); + } + else + { + response = await executor.Execute(this, inst, message, dialogs); + } await planner.AgentExecuted(_router, inst, response); } diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.sequential.get_remaining_task.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.sequential.get_remaining_task.liquid new file mode 100644 index 00000000..3fefbf77 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.sequential.get_remaining_task.liquid @@ -0,0 +1,5 @@ +Use will give you a task list with steps, which is going to be executed. +If a specific step has been exectued, you will get something indicates the result. +If there is no any result provided, it means all the steps have not been executed yet. +You need to figure out which steps have not been completed. +Tell me what is the first remaining step from user steps that have not been completed based on the context. Output in JSON { "description": "", "total_remaining_steps": 0} \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.sequential.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.sequential.liquid index 77faafc2..d0d3a4ad 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.sequential.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.sequential.liquid @@ -1,2 +1,2 @@ -In order to sequentially execute user needs, +In order to sequentially execute user tasks, What is the next step based on the CONVERSATION? \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserText.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserText.cs index 28a652b7..7443c2ff 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserText.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserText.cs @@ -58,10 +58,6 @@ public partial class PlaywrightWebDriver try { await element.FillAsync(context.InputText); - if (context.PressEnter) - { - await element.PressAsync("Enter"); - } } catch (Exception ex) { diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/LlmContexts/BrowsingContextIn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/LlmContexts/BrowsingContextIn.cs index efd327eb..73c2753b 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/LlmContexts/BrowsingContextIn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/LlmContexts/BrowsingContextIn.cs @@ -13,9 +13,6 @@ public class BrowsingContextIn [JsonPropertyName("input_text")] public string? InputText { get; set; } - [JsonPropertyName("press_enter")] - public bool PressEnter { get; set; } - [JsonPropertyName("update_value")] public string? UpdateValue { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/agent.json b/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/agent.json index c87e2a3b..2c968f4e 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/agent.json +++ b/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/agent.json @@ -5,9 +5,10 @@ "type": "task", "createdDateTime": "2024-01-02T00:00:00Z", "updatedDateTime": "2024-01-02T00:00:00Z", + "disabled": false, "isPublic": true, "profiles": [ "web-driver" ], "llmConfig": { - "max_recursion_depth": 10 + "max_recursion_depth": 1 } } \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/functions.json b/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/functions.json index 59a678aa..5ccf6dd8 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/functions.json +++ b/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/functions.json @@ -78,10 +78,6 @@ "input_text": { "type": "string", "description": "non-sensitive text user provided." - }, - "press_enter": { - "type": "boolean", - "description": "whether to press ENTER" } }, "required": [ "element_name", "input_text" ]