From 9b2b32e01351905b69b905837766d401ba05900a Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 22 Jan 2024 23:04:06 -0600 Subject: [PATCH 01/12] add agent pagination --- .../Agents/IAgentService.cs | 4 ++-- .../Repositories/Filters/AgentFilter.cs | 1 + .../Agents/Services/AgentService.GetAgents.cs | 10 +++++++--- .../BotSharp.Core/Planning/NaivePlanner.cs | 2 +- .../FileRepository.Conversation.cs | 20 +++++++++---------- .../Controllers/AgentController.cs | 14 +++++++++---- .../MongoRepository.Conversation.cs | 3 ++- 7 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs index 76019e47..5f3b1b87 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs @@ -10,7 +10,7 @@ public interface IAgentService { Task CreateAgent(Agent agent); Task RefreshAgents(); - Task> GetAgents(AgentFilter filter); + Task> GetAgents(AgentFilter filter); /// /// Load agent configurations and trigger hooks @@ -29,7 +29,7 @@ public interface IAgentService /// /// Original agent information Task GetAgent(string id); - + Task DeleteAgent(string id); Task UpdateAgent(Agent agent, AgentField updateField); Task UpdateAgentFromFile(string id); diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/AgentFilter.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/AgentFilter.cs index 1b4e232b..860a7dc9 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/AgentFilter.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/AgentFilter.cs @@ -2,6 +2,7 @@ namespace BotSharp.Abstraction.Repositories.Filters; public class AgentFilter { + public Pagination Pager { get; set; } = new Pagination(); public string? AgentName { get; set; } public bool? Disabled { get; set; } public bool? Installed { get; set; } diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs index aba053a0..486c756b 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs @@ -9,7 +9,7 @@ public partial class AgentService #if !DEBUG [MemoryCache(10 * 60)] #endif - public async Task> GetAgents(AgentFilter filter) + public async Task> GetAgents(AgentFilter filter) { var agents = _db.GetAgents(filter); @@ -22,8 +22,12 @@ public partial class AgentService } agents = agents.Where(x => x.Installed).ToList(); - - return agents; + var pager = filter?.Pager ?? new Pagination(); + return new PagedItems + { + Items = agents.Skip(pager.Offset).Take(pager.Size), + Count = agents.Count() + }; } #if !DEBUG diff --git a/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs b/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs index 9ad95121..6db3a40a 100644 --- a/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs +++ b/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs @@ -126,7 +126,7 @@ public class NaivePlanner : IPlaner var agents = agentService.GetAgents(new AgentFilter { AllowRouting = true - }).Result; + }).Result.Items.ToList(); var malformed = false; // Sometimes it populate malformed Function in Agent name diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs index d9e276e5..76122be6 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs @@ -204,10 +204,10 @@ namespace BotSharp.Core.Repository { var records = new List(); var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir); + var pager = filter?.Pager ?? new Pagination(); var totalDirs = Directory.GetDirectories(dir); - var dirs = totalDirs.Skip(filter.Pager.Offset).Take(filter.Pager.Size).ToList(); - foreach (var d in dirs) + foreach (var d in totalDirs) { var path = Path.Combine(d, CONVERSATION_FILE); if (!File.Exists(path)) continue; @@ -217,20 +217,20 @@ namespace BotSharp.Core.Repository if (record == null) continue; var matched = true; - if (filter.Id != null) matched = matched && record.Id == filter.Id; - if (filter.AgentId != null) matched = matched && record.AgentId == filter.AgentId; - if (filter.Status != null) matched = matched && record.Status == filter.Status; - if (filter.Channel != null) matched = matched && record.Channel == filter.Channel; - if (filter.UserId != null) matched = matched && record.UserId == filter.UserId; + if (filter?.Id != null) matched = matched && record.Id == filter.Id; + if (filter?.AgentId != null) matched = matched && record.AgentId == filter.AgentId; + if (filter?.Status != null) matched = matched && record.Status == filter.Status; + if (filter?.Channel != null) matched = matched && record.Channel == filter.Channel; + if (filter?.UserId != null) matched = matched && record.UserId == filter.UserId; if (!matched) continue; records.Add(record); } - + return new PagedItems { - Items = records.OrderByDescending(x => x.CreatedTime), - Count = totalDirs.Count(), + Items = records.OrderByDescending(x => x.CreatedTime).Skip(pager.Offset).Take(pager.Size), + Count = records.Count(), }; } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs index 9259cb54..eccf8316 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs @@ -1,3 +1,5 @@ +using BotSharp.Abstraction.Agents.Models; + namespace BotSharp.OpenAPI.Controllers; [Authorize] @@ -27,11 +29,15 @@ public class AgentController : ControllerBase return AgentViewModel.FromAgent(agent); } - [HttpGet("/agents")] - public async Task> GetAgents([FromQuery] AgentFilter filter) + [HttpPost("/agents")] + public async Task> GetAgents([FromBody] AgentFilter filter) { - var agents = await _agentService.GetAgents(filter); - return agents.Select(x => AgentViewModel.FromAgent(x)).ToList(); + var pagedAgents = await _agentService.GetAgents(filter); + return new PagedItems + { + Items = pagedAgents.Items.Select(x => AgentViewModel.FromAgent(x)).ToList(), + Count = pagedAgents.Count + }; } [HttpPost("/agent")] diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs index 84ccce6d..18fe98d6 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs @@ -217,7 +217,8 @@ public partial class MongoRepository var filterDef = builder.And(filters); var sortDefinition = Builders.Sort.Descending(x => x.CreatedTime); - var conversationDocs = _dc.Conversations.Find(filterDef).Sort(sortDefinition).Skip(filter.Pager.Offset).Limit(filter.Pager.Size).ToList(); + var pager = filter?.Pager ?? new Pagination(); + var conversationDocs = _dc.Conversations.Find(filterDef).Sort(sortDefinition).Skip(pager.Offset).Limit(pager.Size).ToList(); var count = _dc.Conversations.CountDocuments(filterDef); foreach (var conv in conversationDocs) From 88743d151b5cd64fcfa9089fa11eb1519b832cfa Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 23 Jan 2024 11:32:28 -0600 Subject: [PATCH 02/12] change to get --- .../BotSharp.OpenAPI/Controllers/AgentController.cs | 4 ++-- .../BotSharp.OpenAPI/Controllers/ConversationController.cs | 4 ++-- .../BotSharp.OpenAPI/Controllers/PluginController.cs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs index eccf8316..dfc41e79 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs @@ -29,8 +29,8 @@ public class AgentController : ControllerBase return AgentViewModel.FromAgent(agent); } - [HttpPost("/agents")] - public async Task> GetAgents([FromBody] AgentFilter filter) + [HttpGet("/agents")] + public async Task> GetAgents([FromQuery] AgentFilter filter) { var pagedAgents = await _agentService.GetAgents(filter); return new PagedItems diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index 46a1b721..58378ba5 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -30,8 +30,8 @@ public class ConversationController : ControllerBase return ConversationViewModel.FromSession(conv); } - [HttpPost("/conversations")] - public async Task> GetConversations([FromBody] ConversationFilter filter) + [HttpGet("/conversations")] + public async Task> GetConversations([FromQuery] ConversationFilter filter) { var service = _services.GetRequiredService(); var conversations = await service.GetConversations(filter); diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs index 36612852..54cdae14 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs @@ -16,8 +16,8 @@ public class PluginController : ControllerBase _settings = settings; } - [HttpPost("/plugins")] - public PagedItems GetPlugins([FromBody] PluginFilter filter) + [HttpGet("/plugins")] + public PagedItems GetPlugins([FromQuery] PluginFilter filter) { var loader = _services.GetRequiredService(); return loader.GetPagedPlugins(_services, filter); From 5fbf32daa8c1624452a02e8fda5bea088d4106c3 Mon Sep 17 00:00:00 2001 From: Visagan Guruparan <103048@smsassist.com> Date: Tue, 23 Jan 2024 13:32:35 -0600 Subject: [PATCH 03/12] Add attributes to user view model --- .../ViewModels/Users/UserViewModel.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserViewModel.cs index f3083abd..1a4706a3 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserViewModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserViewModel.cs @@ -16,6 +16,12 @@ public class UserViewModel public string Role { get; set; } = UserRole.Client; [JsonPropertyName("full_name")] public string FullName => $"{FirstName} {LastName}"; + [JsonPropertyName("external_id")] + public string? ExternalId { get; set; } + [JsonPropertyName("create_date")] + public DateTime CreateDate { get; set; } + [JsonPropertyName("update_date")] + public DateTime UpdateDate { get; set; } public static UserViewModel FromUser(User user) { @@ -36,7 +42,10 @@ public class UserViewModel FirstName = user.FirstName, LastName = user.LastName, Email = user.Email, - Role = user.Role + Role = user.Role, + ExternalId = user.ExternalId, + CreateDate = user.CreatedTime, + UpdateDate = user.UpdatedTime }; } } From 091e7b4212a77e7161af682711f25b03fd66d672 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Tue, 23 Jan 2024 17:14:57 -0600 Subject: [PATCH 04/12] SequentialPlanner draft. --- .../BotSharp.Core/BotSharp.Core.csproj | 12 +- .../BotSharp.Core/Planning/HFPlanner.cs | 2 +- .../BotSharp.Core/Planning/NaivePlanner.cs | 2 +- .../Planning/SequentialPlanner.cs | 112 ++++++++++++++++++ .../BotSharp.Core/Routing/RoutingPlugin.cs | 4 + ...lanner.liquid => planner_prompt.hf.liquid} | 0 ...mpt.liquid => planner_prompt.naive.liquid} | 0 .../planner_prompt.sequential.liquid | 3 + .../BotSharp.Plugin.Selenium.csproj | 13 -- .../BotSharp.Plugin.WebDriver.csproj | 2 +- .../PlaywrightWebDriver.ChangeListValue.cs | 87 +++++++++++--- .../PlaywrightDriver/PlaywrightWebDriver.cs | 1 + .../Functions/ChangeListValueFn.cs | 3 +- .../Functions/ClickButtonFn.cs | 3 +- .../Functions/ExtractDataFn.cs | 1 + .../Functions/InputUserPasswordFn.cs | 1 + .../Functions/InputUserTextFn.cs | 3 +- .../Functions/OpenBrowserFn.cs | 4 +- .../LlmContexts/HtmlElementContextOut.cs | 3 + .../agent.json | 16 +-- .../functions.json | 4 +- .../instruction.liquid | 4 +- .../templates/html_parser.liquid | 5 +- 23 files changed, 232 insertions(+), 53 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Core/Planning/SequentialPlanner.cs rename src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/{next_step_prompt.hf_planner.liquid => planner_prompt.hf.liquid} (100%) rename src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/{next_step_prompt.liquid => planner_prompt.naive.liquid} (100%) create mode 100644 src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.sequential.liquid delete mode 100644 src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.Selenium.csproj diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index 2ad912dc..fadeacd1 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -50,8 +50,9 @@ - - + + + @@ -70,10 +71,13 @@ PreserveNewest - + PreserveNewest - + + PreserveNewest + + PreserveNewest diff --git a/src/Infrastructure/BotSharp.Core/Planning/HFPlanner.cs b/src/Infrastructure/BotSharp.Core/Planning/HFPlanner.cs index 206c22d5..0e965bad 100644 --- a/src/Infrastructure/BotSharp.Core/Planning/HFPlanner.cs +++ b/src/Infrastructure/BotSharp.Core/Planning/HFPlanner.cs @@ -91,7 +91,7 @@ public class HFPlanner : IPlaner private string GetNextStepPrompt(Agent router) { - var template = router.Templates.First(x => x.Name == "next_step_prompt.hf_planner").Content; + var template = router.Templates.First(x => x.Name == "planner_prompt.hf").Content; var render = _services.GetRequiredService(); var prompt = render.Render(template, router.TemplateDict); return prompt.Trim(); diff --git a/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs b/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs index 6db3a40a..d7e33451 100644 --- a/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs +++ b/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs @@ -108,7 +108,7 @@ public class NaivePlanner : IPlaner private string GetNextStepPrompt(Agent router) { - var template = router.Templates.First(x => x.Name == "next_step_prompt").Content; + var template = router.Templates.First(x => x.Name == "planner_prompt.naive").Content; var render = _services.GetRequiredService(); return render.Render(template, new Dictionary diff --git a/src/Infrastructure/BotSharp.Core/Planning/SequentialPlanner.cs b/src/Infrastructure/BotSharp.Core/Planning/SequentialPlanner.cs new file mode 100644 index 00000000..fd85863c --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Planning/SequentialPlanner.cs @@ -0,0 +1,112 @@ +using BotSharp.Abstraction.Agents.Models; +using BotSharp.Abstraction.Functions.Models; +using BotSharp.Abstraction.Planning; +using BotSharp.Abstraction.Routing; +using BotSharp.Abstraction.Routing.Models; +using BotSharp.Abstraction.Templating; + +namespace BotSharp.Core.Planning; + +public class SequentialPlanner : IPlaner +{ + private readonly IServiceProvider _services; + private readonly ILogger _logger; + + public SequentialPlanner(IServiceProvider services, ILogger logger) + { + _services = services; + _logger = logger; + } + + public async Task GetNextInstruction(Agent router, string messageId) + { + var next = GetNextStepPrompt(router); + + var inst = new FunctionCallFromLlm(); + + // text completion + /*var agentService = _services.GetRequiredService(); + var instruction = agentService.RenderedInstruction(router); + var content = $"{instruction}\r\n###\r\n{next}"; + content = content + "\r\nResponse: "; + var completion = CompletionProvider.GetTextCompletion(_services);*/ + + // chat completion + var completion = CompletionProvider.GetChatCompletion(_services, + provider: router?.LlmConfig?.Provider, + model: router?.LlmConfig?.Model); + + int retryCount = 0; + while (retryCount < 3) + { + string text = string.Empty; + try + { + // text completion + // text = await completion.GetCompletion(content, router.Id, messageId); + var dialogs = new List + { + new RoleDialogModel(AgentRole.User, next) + { + MessageId = messageId + } + }; + var response = await completion.GetChatCompletions(router, dialogs); + + inst = response.Content.JsonContent(); + break; + } + catch (Exception ex) + { + _logger.LogError($"{ex.Message}: {text}"); + inst.Function = "response_to_user"; + inst.Response = ex.Message; + inst.AgentName = "Router"; + } + finally + { + retryCount++; + } + } + + return inst; + } + + public async Task AgentExecuting(Agent router, FunctionCallFromLlm inst, RoleDialogModel message) + { + // Set user content as Planner's question + message.FunctionName = inst.Function; + message.FunctionArgs = inst.Arguments == null ? "{}" : JsonSerializer.Serialize(inst.Arguments); + + return true; + } + + public async Task AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message) + { + var context = _services.GetRequiredService(); + + if (message.StopCompletion) + { + context.Empty(); + return false; + } + + // Handover to Router; + context.Pop(); + + var routing = _services.GetRequiredService(); + routing.ResetRecursiveCounter(); + + return true; + } + + private string GetNextStepPrompt(Agent router) + { + var template = router.Templates.First(x => x.Name == "planner_prompt.sequential").Content; + + var render = _services.GetRequiredService(); + return render.Render(template, new Dictionary + { + }); + } +} diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingPlugin.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingPlugin.cs index 7b4dc318..c0881ed2 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingPlugin.cs @@ -37,12 +37,16 @@ public class RoutingPlugin : IBotSharpPlugin services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(provider => { var settingService = provider.GetRequiredService(); var routingSettings = settingService.Bind("Router"); if (routingSettings.Planner == nameof(HFPlanner)) return provider.GetRequiredService(); + else if (routingSettings.Planner == nameof(SequentialPlanner)) + return provider.GetRequiredService(); else return provider.GetRequiredService(); }); diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/next_step_prompt.hf_planner.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.hf.liquid similarity index 100% rename from src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/next_step_prompt.hf_planner.liquid rename to src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.hf.liquid diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/next_step_prompt.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.naive.liquid similarity index 100% rename from src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/next_step_prompt.liquid rename to src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.naive.liquid 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 new file mode 100644 index 00000000..33f8db9f --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.sequential.liquid @@ -0,0 +1,3 @@ +In order to execute the instructions listed by the user in the order specified by the user. +What is the next step based on the CONVERSATION? +Response must be in required JSON format. \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.Selenium.csproj b/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.Selenium.csproj deleted file mode 100644 index 20ca5175..00000000 --- a/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.Selenium.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - netstandard2.1 - enable - $(MSBuildProjectName.Replace(" ", "_"))s - - - - - - - diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj b/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj index d78a7e12..205f32bf 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj +++ b/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeListValue.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeListValue.cs index 2c08a0ad..f3a453ad 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeListValue.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeListValue.cs @@ -1,4 +1,5 @@ using BotSharp.Plugin.WebDriver.Services; +using System.Threading; namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; @@ -10,22 +11,49 @@ public partial class PlaywrightWebDriver var body = await _instance.Page.QuerySelectorAsync("body"); var str = new List(); - var inputs = await body.QuerySelectorAllAsync("input"); + var inputs = await body.QuerySelectorAllAsync("select"); foreach (var input in inputs) { - var text = await input.TextContentAsync(); + var html = "{text}"); - } + if (!string.IsNullOrEmpty(name)) + { + html += $" name='{id}'"; + } + html += ">"; - inputs = await body.QuerySelectorAllAsync("textarea"); - foreach (var input in inputs) - { - var text = await input.TextContentAsync(); - var name = await input.GetAttributeAsync("name"); - var type = await input.GetAttributeAsync("type"); - str.Add($""); + var options = await input.QuerySelectorAllAsync("option"); + if (options != null) + { + foreach (var option in options) + { + html += "(); @@ -36,10 +64,41 @@ public partial class PlaywrightWebDriver throw new Exception($"Can't locate the web element {context.ElementName}."); } - var element = _instance.Page.Locator(htmlElementContextOut.TagName).Nth(htmlElementContextOut.Index); + ILocator element = default; + if (!string.IsNullOrEmpty(htmlElementContextOut.ElementId)) + { + // await _instance.Page.WaitForSelectorAsync($"#{htmlElementContextOut.ElementId}", new PageWaitForSelectorOptions { Timeout = 3 }); + element = _instance.Page.Locator($"#{htmlElementContextOut.ElementId}"); + } + else + { + element = _instance.Page.Locator(htmlElementContextOut.TagName).Nth(htmlElementContextOut.Index); + } + try { - await element.FillAsync(context.InputText); + var isVisible = await element.IsVisibleAsync(); + + if (!isVisible) + { + // Select the element you want to make visible (replace with your own selector) + var control = await _instance.Page.QuerySelectorAsync($"#{htmlElementContextOut.ElementId}"); + + // Show the element by modifying its CSS styles + await _instance.Page.EvaluateAsync(@"(element) => { + element.style.display = 'block'; + element.style.visibility = 'visible'; + }", control); + } + + await element.FocusAsync(); + await element.SelectOptionAsync(new SelectOptionValue + { + Label = context.UpdateValue + }); + + // Click on the blank area to activate posting + await body.ClickAsync(); } catch (Exception ex) { diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.cs index a325a076..abfc300f 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.cs @@ -4,6 +4,7 @@ public partial class PlaywrightWebDriver { private readonly IServiceProvider _services; private readonly PlaywrightInstance _instance; + public PlaywrightInstance Instance => _instance; public PlaywrightWebDriver(IServiceProvider services, PlaywrightInstance instance) { diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ChangeListValueFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ChangeListValueFn.cs index 4431e1e3..1002351b 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ChangeListValueFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ChangeListValueFn.cs @@ -23,9 +23,10 @@ public class ChangeListValueFn : IFunctionCallback var agentService = _services.GetRequiredService(); var agent = await agentService.LoadAgent(message.CurrentAgentId); + await _driver.Instance.Page.WaitForLoadStateAsync(LoadState.Load); await _driver.ChangeListValue(agent, args, message.MessageId); - message.Content = "Update successfully."; + message.Content = $"Updat the value of \"${args.ElementName}\" to \"{args.UpdateValue}\" successfully."; return true; } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ClickButtonFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ClickButtonFn.cs index bb4b81f9..afa8da5d 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ClickButtonFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ClickButtonFn.cs @@ -23,9 +23,10 @@ public class ClickButtonFn : IFunctionCallback var agentService = _services.GetRequiredService(); var agent = await agentService.LoadAgent(message.CurrentAgentId); + await _driver.Instance.Page.WaitForLoadStateAsync(LoadState.Load); await _driver.ClickElement(agent, args, message.MessageId); - message.Content = "Executed successfully."; + message.Content = $"Click button {args.ElementName} successfully."; return true; } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ExtractDataFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ExtractDataFn.cs index 82692bfb..71c3fa39 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ExtractDataFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ExtractDataFn.cs @@ -23,6 +23,7 @@ public class ExtractDataFn : IFunctionCallback var args = JsonSerializer.Deserialize(message.FunctionArgs); var agentService = _services.GetRequiredService(); var agent = await agentService.LoadAgent(message.CurrentAgentId); + await _driver.Instance.Page.WaitForLoadStateAsync(LoadState.Load); message.Content = await _driver.ExtractData(agent, args, message.MessageId); return true; } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserPasswordFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserPasswordFn.cs index 30aae332..ccafd721 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserPasswordFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserPasswordFn.cs @@ -23,6 +23,7 @@ public class InputUserPasswordFn : IFunctionCallback var agentService = _services.GetRequiredService(); var agent = await agentService.LoadAgent(message.CurrentAgentId); + await _driver.Instance.Page.WaitForLoadStateAsync(LoadState.Load); await _driver.InputUserPassword(agent, args, message.MessageId); message.Content = "Input password successfully"; diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserTextFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserTextFn.cs index 246c6011..b5052965 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserTextFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserTextFn.cs @@ -23,9 +23,10 @@ public class InputUserTextFn : IFunctionCallback var agentService = _services.GetRequiredService(); var agent = await agentService.LoadAgent(message.CurrentAgentId); + await _driver.Instance.Page.WaitForLoadStateAsync(LoadState.Load); await _driver.InputUserText(agent, args, message.MessageId); - message.Content = "Input text successfully."; + message.Content = $"Input text \"{args.InputText}\" successfully."; return true; } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/OpenBrowserFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/OpenBrowserFn.cs index 94e18cae..8a7323da 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/OpenBrowserFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/OpenBrowserFn.cs @@ -20,9 +20,7 @@ public class OpenBrowserFn : IFunctionCallback { var args = JsonSerializer.Deserialize(message.FunctionArgs); var browser = await _driver.LaunchBrowser(args.Url); - message.Content = string.IsNullOrEmpty(args.Url) ? "Launch browser successfully." : $"Open website successfully."; - message.Content += "\r\nWhat would you like to do next?"; - message.StopCompletion = true; + message.Content = string.IsNullOrEmpty(args.Url) ? $"Launch browser with blank page successfully." : $"Open website {args.Url} successfully."; return true; } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/LlmContexts/HtmlElementContextOut.cs b/src/Plugins/BotSharp.Plugin.WebDriver/LlmContexts/HtmlElementContextOut.cs index a325a4f6..a0fbfaf6 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/LlmContexts/HtmlElementContextOut.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/LlmContexts/HtmlElementContextOut.cs @@ -4,6 +4,9 @@ namespace BotSharp.Plugin.WebDriver.LlmContexts; public class HtmlElementContextOut { + [JsonPropertyName("element_id")] + public string ElementId { get; set; } + [JsonPropertyName("tag_name")] public string TagName { 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 d2250da1..5a12078d 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 @@ -1,9 +1,9 @@ { - "name": "Web Driver", - "description": "Perform a specific action on a web browser", - "createdDateTime": "2024-01-02T00:00:00Z", - "updatedDateTime": "2024-01-02T00:00:00Z", - "id": "f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b", - "allowRouting": true, - "isPublic": true - } \ No newline at end of file + "name": "Web Driver", + "description": "Perform a specific action on a web browser", + "createdDateTime": "2024-01-02T00:00:00Z", + "updatedDateTime": "2024-01-02T00:00:00Z", + "id": "f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b", + "allowRouting": true, + "isPublic": true +} \ 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 78128f01..49e4770b 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 @@ -7,7 +7,7 @@ "properties": { "url": { "type": "string", - "description": "website url." + "description": "website url starts with https://" } }, "required": ["url"] @@ -67,7 +67,7 @@ "properties": { "element_name": { "type": "string", - "description": "the html input box element name." + "description": "the html selection element name." }, "update_value": { "type": "string", diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/instruction.liquid b/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/instruction.liquid index 4f6d65dd..c41ef235 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/instruction.liquid +++ b/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/instruction.liquid @@ -3,6 +3,8 @@ You are a Web Driver that can manipulate web elements through automation tools. Follow below steps to response: 1. Analyze user's latest request in the conversation. 2. Call appropriate function to execute the instruction. +3. If user requests execute multiple steps, execute them sequentially. Additional response requirements: -* Call function input_user_password if user wants to input password. \ No newline at end of file +* Call function input_user_password if user wants to input password. +* Don't do extra steps if user didn't ask. \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/templates/html_parser.liquid b/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/templates/html_parser.liquid index 2e9c8bf5..c1991861 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/templates/html_parser.liquid +++ b/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/templates/html_parser.liquid @@ -1,5 +1,6 @@ {{ html_content }} === According to above HTML === -Find the html element tag name of "{{ element_name }}". -Output in JSON format {"tag_name": "", "index": -1} with appropriate values, the "index" starts with 0. \ No newline at end of file +Find the html element in the similar meaning of "{{ element_name }}". +Output in JSON format {"tag_name": "", "element_id": "populated if element has id", "index": -1} with appropriate values. +The index is the position of the element which starts with 0. \ No newline at end of file From 9d3cd6b6e95b1cdef275b051b33a086abf2b7f32 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Wed, 24 Jan 2024 17:02:59 -0600 Subject: [PATCH 05/12] Support multi-agent with different profile. --- docs/agent/intro.md | 2 +- docs/architecture/routing.md | 6 +++- .../Agents/Models/Agent.cs | 3 ++ .../Routing/IRoutingService.cs | 4 +-- .../{RoutingItem.cs => RoutableAgent.cs} | 6 +++- .../Agents/Services/AgentService.GetAgents.cs | 7 +++++ .../ConversationService.SendMessage.cs | 2 +- .../BotSharp.Core/Planning/NaivePlanner.cs | 2 +- .../Routing/Hooks/RoutingAgentHook.cs | 3 +- .../BotSharp.Core/Routing/RoutingService.cs | 28 +++++++++++-------- .../Templating/TemplateRender.cs | 2 +- .../ViewModels/Agents/AgentViewModel.cs | 10 ++++++- .../MongoRepository.Conversation.cs | 1 + .../BotSharp.Plugin.PizzaBot.csproj | 18 +----------- .../agent.json | 1 + .../agent.json | 3 +- .../agents.json | 2 -- .../agents.json | 10 ------- .../agents.json | 2 -- .../agents.json | 2 -- 20 files changed, 58 insertions(+), 56 deletions(-) rename src/Infrastructure/BotSharp.Abstraction/Routing/Models/{RoutingItem.cs => RoutableAgent.cs} (84%) delete mode 100644 tests/BotSharp.Plugin.PizzaBot/data/users/10d12798-08fb-4aa6-977b-5dd94d82dbfe/agents.json delete mode 100644 tests/BotSharp.Plugin.PizzaBot/data/users/456e35c5-caf0-4d45-9084-b44a8ca717e4/agents.json delete mode 100644 tests/BotSharp.Plugin.PizzaBot/data/users/d0e6680d-03d5-4ed8-bdcd-aa7d86f2a1bc/agents.json delete mode 100644 tests/BotSharp.Plugin.PizzaBot/data/users/e465af5f-044f-414b-b670-92834929b96c/agents.json diff --git a/docs/agent/intro.md b/docs/agent/intro.md index 7507ef2e..00484fe8 100644 --- a/docs/agent/intro.md +++ b/docs/agent/intro.md @@ -2,7 +2,7 @@ An agent helps you process user sentences (unstructure data) into structure data that you can use to return an appropriate response. -Agent is a collection that contains prompt words and function Json Schema definitions, few-shot examples and knowledge base data. You can create multiple different Agents to perform specific operations in specific domains. BotSharp has built-in maintenance for Agents, including creating, updating and deleting, importing and exporting. +Agent is a collection that contains prompt words and function Json Schema definitions, few-shot examples and knowledge base data. You can create multiple different Agents to perform specific operations in specific domains. BotSharp has built-in maintenance for Agents, including creating, updating and deleting, importing and exporting. Agents are divided into task agents and routing (non-task) agents. Business domain agents belong to task agents, and routers belong to non-task agents. ## My Agent After creating the platform account, you can start to enter the steps of creating the Agent. diff --git a/docs/architecture/routing.md b/docs/architecture/routing.md index a4bbb6ea..8732cd59 100644 --- a/docs/architecture/routing.md +++ b/docs/architecture/routing.md @@ -14,4 +14,8 @@ For simple questions raised by users, the ordinary routing function can already ![routing with reasoning](./assets/routing-reasoner.png) -For more **Routing** related information, please go to [Agent Routing](../agent/router.md). \ No newline at end of file +For more **Routing** related information, please go to [Agent Routing](../agent/router.md). + +## Profile + +There is an array field called `Profile` in the Agent data model, which is used to store the current profiles. When this attribute is set in the `Router`, only matching Task Agents can be included in the routing candidate Agents list, which means that the Task Agent also To set the same profile name. Profiles allows you to enter multiple profiles, and the system will automatically combine them for processing. \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs index 1280c865..6a134cef 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs @@ -60,6 +60,9 @@ public class Agent [JsonIgnore] public bool IsRouter { get; set; } + [JsonIgnore] + public bool IsHost { get; set; } + [JsonIgnore] public PluginDef Plugin { get; set; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs index 17027652..c9f40fdc 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs @@ -5,7 +5,7 @@ namespace BotSharp.Abstraction.Routing; public interface IRoutingService { Agent Router { get; } - RoutingItem[] GetRoutingItems(); + RoutableAgent[] GetRoutableAgents(List profiles); RoutingRule[] GetRulesByName(string name); RoutingRule[] GetRulesByAgentId(string id); List GetHandlers(); @@ -20,5 +20,5 @@ public interface IRoutingService /// /// /// - Task ExecuteDirectly(Agent agent, RoleDialogModel message); + Task InstructDirect(Agent agent, RoleDialogModel message); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingItem.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutableAgent.cs similarity index 84% rename from src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingItem.cs rename to src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutableAgent.cs index b08edfed..14fd6826 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingItem.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutableAgent.cs @@ -2,7 +2,7 @@ using BotSharp.Abstraction.Functions.Models; namespace BotSharp.Abstraction.Routing.Models; -public class RoutingItem +public class RoutableAgent { [JsonPropertyName("agent_id")] public string AgentId { get; set; } = string.Empty; @@ -13,6 +13,10 @@ public class RoutingItem [JsonPropertyName("description")] public string Description { get; set; } = string.Empty; + [JsonPropertyName("profiles")] + public List Profiles { get; set; } + = new List(); + [JsonPropertyName("required_fields")] public List RequiredFields { get; set; } = new List(); diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs index 486c756b..7e7493d1 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs @@ -21,6 +21,13 @@ public partial class AgentService agent.Plugin = GetPlugin(agent.Id); } + // Set IsHost + var agentSetting = _services.GetRequiredService(); + foreach (var agent in agents) + { + agent.IsHost = agentSetting.HostAgentId == agent.Id; + } + agents = agents.Where(x => x.Installed).ToList(); var pager = filter?.Pager ?? new Pagination(); return new PagedItems diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs index fd427562..eb46d5ec 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -61,7 +61,7 @@ public partial class ConversationService response = settings.AgentIds.Contains(agentId) ? await routing.InstructLoop(message) : - await routing.ExecuteDirectly(agent, message); + await routing.InstructDirect(agent, message); routing.ResetRecursiveCounter(); } diff --git a/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs b/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs index d7e33451..c7e34ff8 100644 --- a/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs +++ b/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs @@ -93,7 +93,7 @@ public class NaivePlanner : IPlaner var unmatchedAgentId = context.GetCurrentAgentId(); // Exclude the wrong routed agent - var agents = router.TemplateDict["routing_agents"] as RoutingItem[]; + var agents = router.TemplateDict["routing_agents"] as RoutableAgent[]; router.TemplateDict["routing_agents"] = agents.Where(x => x.AgentId != unmatchedAgentId).ToArray(); // Handover to Router; diff --git a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs index 47cfcc38..84de68b0 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs @@ -24,7 +24,8 @@ public class RoutingAgentHook : AgentHookBase dict["router"] = _agent; var routing = _services.GetRequiredService(); - dict["routing_agents"] = routing.GetRoutingItems(); + var agents = routing.GetRoutableAgents(_agent.Profiles); + dict["routing_agents"] = agents; dict["routing_handlers"] = routing.GetHandlers(); return base.OnInstructionLoaded(template, dict); diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index d70cc15f..888a9eb5 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -32,7 +32,7 @@ public partial class RoutingService : IRoutingService _logger = logger; } - public async Task ExecuteDirectly(Agent agent, RoleDialogModel message) + public async Task InstructDirect(Agent agent, RoleDialogModel message) { var handlers = _services.GetServices(); @@ -147,22 +147,13 @@ public partial class RoutingService : IRoutingService return x.RoutingRules; }).ToArray(); - // Filter agents by profile - var state = _services.GetRequiredService(); - var channel = state.GetState("channel"); - var specifiedProfile = agents.FirstOrDefault(x => x.Profiles.Contains(channel)); - if (specifiedProfile != null) - { - records = records.Where(x => specifiedProfile.Profiles.Contains(channel)).ToArray(); - } - return records; } #if !DEBUG [MemoryCache(10 * 60)] #endif - public RoutingItem[] GetRoutingItems() + public RoutableAgent[] GetRoutableAgents(List profiles) { var db = _services.GetRequiredService(); @@ -171,12 +162,14 @@ public partial class RoutingService : IRoutingService Disabled = false, AllowRouting = true }; + var agents = db.GetAgents(filter); - return agents.Select(x => new RoutingItem + var routableAgents = agents.Select(x => new RoutableAgent { AgentId = x.Id, Description = x.Description, Name = x.Name, + Profiles = x.Profiles, RequiredFields = x.RoutingRules .Where(p => p.Required) .Select(p => new ParameterPropertyDef(p.Field, p.Description, type: p.Type) @@ -190,6 +183,17 @@ public partial class RoutingService : IRoutingService Required = p.Required }).ToList() }).ToArray(); + + // Handle profile. + // Router profile must match the agent profile + if (routableAgents.Length > 0 && profiles.Count > 0) + { + routableAgents = routableAgents.Where(x => x.Profiles != null && + x.Profiles.Exists(x1 => profiles.Exists(y => x1 == y))) + .ToArray(); + } + + return routableAgents; } public RoutingRule[] GetRulesByName(string name) diff --git a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs index 3aba223f..62d5c3e0 100644 --- a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs +++ b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs @@ -25,7 +25,7 @@ public class TemplateRender : ITemplateRender _options.MemberAccessStrategy.Register(); _options.MemberAccessStrategy.Register(); _options.MemberAccessStrategy.Register(); - _options.MemberAccessStrategy.Register(); + _options.MemberAccessStrategy.Register(); _options.MemberAccessStrategy.Register(); } diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs index 663dfd4a..6a41a66b 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs @@ -22,12 +22,19 @@ public class AgentViewModel [JsonPropertyName("is_router")] public bool IsRouter { get; set; } + [JsonPropertyName("is_host")] + public bool IsHost { get; set; } + [JsonPropertyName("allow_routing")] public bool AllowRouting { get; set; } + public bool Disabled { get; set; } + [JsonPropertyName("icon_url")] public string IconUrl { get; set; } + public List Profiles { get; set; } + = new List(); [JsonPropertyName("routing_rules")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] @@ -59,10 +66,11 @@ public class AgentViewModel Samples = agent.Samples, IsPublic= agent.IsPublic, IsRouter = agent.IsRouter, + IsHost = agent.IsHost, Disabled = agent.Disabled, IconUrl = agent.IconUrl, AllowRouting = agent.AllowRouting, - Profiles = agent.Profiles, + Profiles = agent.Profiles ?? new List(), RoutingRules = agent.RoutingRules, LlmConfig = agent.LlmConfig, Plugin = agent.Plugin, diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs index 18fe98d6..15a2406b 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs @@ -210,6 +210,7 @@ public partial class MongoRepository var builder = Builders.Filter; var filters = new List>() { builder.Empty }; + if (!string.IsNullOrEmpty(filter.Id)) filters.Add(builder.Eq(x => x.Id, filter.Id)); if (!string.IsNullOrEmpty(filter.AgentId)) filters.Add(builder.Eq(x => x.AgentId, filter.AgentId)); if (!string.IsNullOrEmpty(filter.Status)) filters.Add(builder.Eq(x => x.Status, filter.Status)); if (!string.IsNullOrEmpty(filter.Channel)) filters.Add(builder.Eq(x => x.Channel, filter.Channel)); diff --git a/tests/BotSharp.Plugin.PizzaBot/BotSharp.Plugin.PizzaBot.csproj b/tests/BotSharp.Plugin.PizzaBot/BotSharp.Plugin.PizzaBot.csproj index 041ba8e5..77ba7e96 100644 --- a/tests/BotSharp.Plugin.PizzaBot/BotSharp.Plugin.PizzaBot.csproj +++ b/tests/BotSharp.Plugin.PizzaBot/BotSharp.Plugin.PizzaBot.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 @@ -35,13 +35,9 @@ - - - - @@ -91,27 +87,15 @@ PreserveNewest - - PreserveNewest - PreserveNewest - - PreserveNewest - PreserveNewest - - PreserveNewest - PreserveNewest - - PreserveNewest - PreserveNewest diff --git a/tests/BotSharp.Plugin.PizzaBot/data/agents/b284db86-e9c2-4c25-a59e-4649797dd130/agent.json b/tests/BotSharp.Plugin.PizzaBot/data/agents/b284db86-e9c2-4c25-a59e-4649797dd130/agent.json index 6b2a2771..09c08cb6 100644 --- a/tests/BotSharp.Plugin.PizzaBot/data/agents/b284db86-e9c2-4c25-a59e-4649797dd130/agent.json +++ b/tests/BotSharp.Plugin.PizzaBot/data/agents/b284db86-e9c2-4c25-a59e-4649797dd130/agent.json @@ -6,6 +6,7 @@ "id": "b284db86-e9c2-4c25-a59e-4649797dd130", "allowRouting": true, "isPublic": true, + "profiles": [ "pizza" ], "routingRules": [ { "field": "order_number", diff --git a/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/agent.json b/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/agent.json index bed04b3f..23323f63 100644 --- a/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/agent.json +++ b/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/agent.json @@ -5,5 +5,6 @@ "updatedDateTime": "2023-07-26T02:29:25.123274Z", "id": "c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd", "allowRouting": true, - "isPublic": true + "isPublic": true, + "profiles": [ "pizza" ] } \ No newline at end of file diff --git a/tests/BotSharp.Plugin.PizzaBot/data/users/10d12798-08fb-4aa6-977b-5dd94d82dbfe/agents.json b/tests/BotSharp.Plugin.PizzaBot/data/users/10d12798-08fb-4aa6-977b-5dd94d82dbfe/agents.json deleted file mode 100644 index 32960f8c..00000000 --- a/tests/BotSharp.Plugin.PizzaBot/data/users/10d12798-08fb-4aa6-977b-5dd94d82dbfe/agents.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] \ No newline at end of file diff --git a/tests/BotSharp.Plugin.PizzaBot/data/users/456e35c5-caf0-4d45-9084-b44a8ca717e4/agents.json b/tests/BotSharp.Plugin.PizzaBot/data/users/456e35c5-caf0-4d45-9084-b44a8ca717e4/agents.json deleted file mode 100644 index 9ed093a2..00000000 --- a/tests/BotSharp.Plugin.PizzaBot/data/users/456e35c5-caf0-4d45-9084-b44a8ca717e4/agents.json +++ /dev/null @@ -1,10 +0,0 @@ -[ - { - "userId": "456e35c5-caf0-4d45-9084-b44a8ca717e4", - "agentId": "01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a", - "updatedTime": "2023-08-14T18:14:11.6833783Z", - "createdTime": "2023-08-14T18:14:11.6829767Z", - "editable": true, - "id": "1273379c-4419-460a-b0a2-5695afd097f5" - } -] \ No newline at end of file diff --git a/tests/BotSharp.Plugin.PizzaBot/data/users/d0e6680d-03d5-4ed8-bdcd-aa7d86f2a1bc/agents.json b/tests/BotSharp.Plugin.PizzaBot/data/users/d0e6680d-03d5-4ed8-bdcd-aa7d86f2a1bc/agents.json deleted file mode 100644 index 32960f8c..00000000 --- a/tests/BotSharp.Plugin.PizzaBot/data/users/d0e6680d-03d5-4ed8-bdcd-aa7d86f2a1bc/agents.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] \ No newline at end of file diff --git a/tests/BotSharp.Plugin.PizzaBot/data/users/e465af5f-044f-414b-b670-92834929b96c/agents.json b/tests/BotSharp.Plugin.PizzaBot/data/users/e465af5f-044f-414b-b670-92834929b96c/agents.json deleted file mode 100644 index 32960f8c..00000000 --- a/tests/BotSharp.Plugin.PizzaBot/data/users/e465af5f-044f-414b-b670-92834929b96c/agents.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] \ No newline at end of file From 264fe3c22aa09232fb11c733aca2c4db0d10f8f3 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Wed, 24 Jan 2024 17:47:57 -0600 Subject: [PATCH 06/12] Optimize StreamingLog. --- .../BotSharp.Core/Planning/HFPlanner.cs | 1 + .../BotSharp.Core/Planning/NaivePlanner.cs | 1 + .../Planning/SequentialPlanner.cs | 1 + .../Providers/ChatCompletionProvider.cs | 11 ++++- .../BotSharp.Plugin.ChatHub/ChatHubPlugin.cs | 1 + .../Hooks/StreamingLogHook.cs | 43 +++++++++++++------ 6 files changed, 43 insertions(+), 15 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Planning/HFPlanner.cs b/src/Infrastructure/BotSharp.Core/Planning/HFPlanner.cs index 0e965bad..4551ae82 100644 --- a/src/Infrastructure/BotSharp.Core/Planning/HFPlanner.cs +++ b/src/Infrastructure/BotSharp.Core/Planning/HFPlanner.cs @@ -43,6 +43,7 @@ public class HFPlanner : IPlaner { new RoleDialogModel(AgentRole.User, next) { + FunctionName = nameof(NaivePlanner), MessageId = messageId } }; diff --git a/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs b/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs index c7e34ff8..a0d9f190 100644 --- a/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs +++ b/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs @@ -49,6 +49,7 @@ public class NaivePlanner : IPlaner { new RoleDialogModel(AgentRole.User, next) { + FunctionName = nameof(NaivePlanner), MessageId = messageId } }; diff --git a/src/Infrastructure/BotSharp.Core/Planning/SequentialPlanner.cs b/src/Infrastructure/BotSharp.Core/Planning/SequentialPlanner.cs index fd85863c..8f571266 100644 --- a/src/Infrastructure/BotSharp.Core/Planning/SequentialPlanner.cs +++ b/src/Infrastructure/BotSharp.Core/Planning/SequentialPlanner.cs @@ -48,6 +48,7 @@ public class SequentialPlanner : IPlaner { new RoleDialogModel(AgentRole.User, next) { + FunctionName = nameof(NaivePlanner), MessageId = messageId } }; diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs index 34cfe762..bf563670 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs @@ -235,7 +235,11 @@ public class ChatCompletionProvider : IChatCompletion } else if (message.Role == ChatRole.User) { - chatCompletionsOptions.Messages.Add(new ChatRequestUserMessage(message.Content)); + chatCompletionsOptions.Messages.Add(new ChatRequestUserMessage(message.Content) + { + // To display Planner name in log + Name = message.FunctionName + }); } else if (message.Role == ChatRole.Assistant) { @@ -268,6 +272,11 @@ public class ChatCompletionProvider : IChatCompletion .Where(x => x.Role == AgentRole.System) .Select(x => x as ChatRequestSystemMessage).Select(x => { + if (!string.IsNullOrEmpty(x.Name)) + { + // To display Agent name in log + return $"[{x.Name}]: {x.Content}"; + } return $"{x.Role}: {x.Content}"; })); prompt += $"{verbose}\r\n"; diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs b/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs index 710d1a41..bec0a01a 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs @@ -19,5 +19,6 @@ public class ChatHubPlugin : IBotSharpPlugin // Register hooks services.AddScoped(); services.AddScoped(); + services.AddScoped(); } } diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs index b6a91787..bdf43c13 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs @@ -5,21 +5,27 @@ using Microsoft.AspNetCore.SignalR; namespace BotSharp.Plugin.ChatHub.Hooks; -public class StreamingLogHook : IContentGeneratingHook +public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook { private readonly ConversationSetting _convSettings; + private readonly JsonSerializerOptions _serializerOptions; private readonly IServiceProvider _services; private readonly IHubContext _chatHub; - private readonly JsonSerializerOptions _serializerOptions; + private readonly IConversationStateService _state; + private readonly IUserIdentity _user; public StreamingLogHook( ConversationSetting convSettings, IServiceProvider serivces, - IHubContext chatHub) + IHubContext chatHub, + IConversationStateService state, + IUserIdentity user) { _convSettings = convSettings; _services = serivces; _chatHub = chatHub; + _state = state; + _user = user; _serializerOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, @@ -27,17 +33,27 @@ public class StreamingLogHook : IContentGeneratingHook AllowTrailingCommas = true }; } + public override async Task OnMessageReceived(RoleDialogModel message) + { + var conversationId = _state.GetConversationId(); + var log = $"MessageId: {message.MessageId} ==>\r\n{message.Role}: {message.Content}"; + await _chatHub.Clients.User(_user.Id).SendAsync("OnContentLogGenerated", BuildLog(conversationId, log)); + } public async Task BeforeGenerating(Agent agent, List conversations) { if (!_convSettings.ShowVerboseLog) return; - var user = _services.GetRequiredService(); - var states = _services.GetRequiredService(); - var conversationId = states.GetConversationId(); + /*var _state = _services.GetRequiredService(); + var conversationId = _state.GetConversationId(); var dialog = conversations.Last(); var log = $"{dialog.Role}: {dialog.Content} [msg_id: {dialog.MessageId}] ==>"; - await _chatHub.Clients.User(user.Id).SendAsync("OnContentLogGenerated", BuildLog(conversationId, log)); + await _chatHub.Clients.User(_user.Id).SendAsync("OnContentLogGenerated", BuildLog(conversationId, log));*/ + } + + public override async Task OnFunctionExecuted(RoleDialogModel message) + { + } public async Task AfterGenerated(RoleDialogModel message, TokenStatsModel tokenStats) @@ -45,17 +61,16 @@ public class StreamingLogHook : IContentGeneratingHook if (!_convSettings.ShowVerboseLog) return; var agentService = _services.GetRequiredService(); - var states = _services.GetRequiredService(); - var conversationId = states.GetConversationId(); + var conversationId = _state.GetConversationId(); var agent = await agentService.LoadAgent(message.CurrentAgentId); + await _chatHub.Clients.User(_user.Id).SendAsync("OnContentLogGenerated", BuildLog(conversationId, tokenStats.Prompt)); + var log = message.Role == AgentRole.Function ? $"[{agent?.Name}]: {message.FunctionName}({message.FunctionArgs})" : - $"[{agent?.Name}]: {message.Content}" + $" <== [msg_id: {message.MessageId}]"; - - var user = _services.GetRequiredService(); - await _chatHub.Clients.User(user.Id).SendAsync("OnContentLogGenerated", BuildLog(conversationId, tokenStats.Prompt)); - await _chatHub.Clients.User(user.Id).SendAsync("OnContentLogGenerated", BuildLog(conversationId, log)); + $"[{agent?.Name}]: {message.Content}"; + log += $"\r\n<== MessageId: {message.MessageId}"; + await _chatHub.Clients.User(_user.Id).SendAsync("OnContentLogGenerated", BuildLog(conversationId, log)); } private string BuildLog(string conversationId, string content) From 69633c8a20ef6303cc190448e78fe76237beff8f Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Thu, 25 Jan 2024 22:32:48 -0600 Subject: [PATCH 07/12] Standarderize agent type. --- docs/agent/intro.md | 2 +- .../Agents/Enums/AgentField.cs | 2 +- .../Agents/Enums/AgentType.cs | 22 +++++++++ .../Agents/Models/Agent.cs | 18 +++----- .../Repositories/Filters/AgentFilter.cs | 4 +- .../Routing/IRoutingService.cs | 7 +++ .../Routing/Models/RoutingContext.cs | 26 ++++++++++- .../Routing/Settings/RoutingSettings.cs | 5 -- .../Services/AgentService.CreateAgent.cs | 2 +- .../Agents/Services/AgentService.GetAgents.cs | 4 -- .../Services/AgentService.UpdateAgent.cs | 4 +- .../ConversationService.SendMessage.cs | 2 +- .../BotSharp.Core/Planning/NaivePlanner.cs | 2 +- .../FileRepository/FileRepository.Agent.cs | 30 +++--------- .../Routing/Hooks/RoutingAgentHook.cs | 2 +- .../BotSharp.Core/Routing/RoutingService.cs | 10 +++- .../agent.json | 6 ++- .../agent.json | 3 +- .../ViewModels/Agents/AgentCreationModel.cs | 3 +- .../ViewModels/Agents/AgentUpdateModel.cs | 4 +- .../ViewModels/Agents/AgentViewModel.cs | 4 +- .../Collections/AgentDocument.cs | 2 +- .../Repository/MongoRepository.Agent.cs | 46 ++++--------------- .../Repository/MongoRepository.Transaction.cs | 4 +- .../RoutingConversationHook.cs | 2 +- .../agent.json | 4 +- src/WebStarter/appsettings.json | 3 -- 27 files changed, 112 insertions(+), 111 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentType.cs diff --git a/docs/agent/intro.md b/docs/agent/intro.md index 00484fe8..7e750ce3 100644 --- a/docs/agent/intro.md +++ b/docs/agent/intro.md @@ -2,7 +2,7 @@ An agent helps you process user sentences (unstructure data) into structure data that you can use to return an appropriate response. -Agent is a collection that contains prompt words and function Json Schema definitions, few-shot examples and knowledge base data. You can create multiple different Agents to perform specific operations in specific domains. BotSharp has built-in maintenance for Agents, including creating, updating and deleting, importing and exporting. Agents are divided into task agents and routing (non-task) agents. Business domain agents belong to task agents, and routers belong to non-task agents. +Agent is a collection that contains prompt words and function Json Schema definitions, few-shot examples and knowledge base data. You can create multiple different Agents to perform specific operations in specific domains. BotSharp has built-in maintenance for Agents, including creating, updating and deleting, importing and exporting. Agents are divided into `task agents`, `routing (non-task) agents`, `evaluating agents` and `static agents`. Business domain agents belong to task agents, and routers belong to non-task agents, static agents don't have capabilities to interact with external environment. ## My Agent After creating the platform account, you can start to enter the steps of creating the Agent. diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentField.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentField.cs index 669ebaba..0fdeffe3 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentField.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentField.cs @@ -7,7 +7,7 @@ public enum AgentField Description, IsPublic, Disabled, - AllowRouting, + Type, Profiles, RoutingRule, Instruction, diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentType.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentType.cs new file mode 100644 index 00000000..17689407 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentType.cs @@ -0,0 +1,22 @@ +namespace BotSharp.Abstraction.Agents.Enums; + +public class AgentType +{ + /// + /// Routing Agent + /// + public const string Routing = "routing"; + + public const string Evaluating = "evaluating"; + + /// + /// Routable task agent with capability of interaction with external environment + /// + public const string Task = "task"; + + /// + /// Agent that cannot use external tools + /// + public const string Static = "static"; +} + diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs index 6a134cef..25228bba 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs @@ -9,6 +9,10 @@ public class Agent public string Id { get; set; } = string.Empty; public string Name { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; + /// + /// Agent Type + /// + public string Type { get; set; } = AgentType.Task; public DateTime CreatedDateTime { get; set; } public DateTime UpdatedDateTime { get; set; } @@ -57,9 +61,6 @@ public class Agent public bool IsPublic { get; set; } - [JsonIgnore] - public bool IsRouter { get; set; } - [JsonIgnore] public bool IsHost { get; set; } @@ -69,11 +70,6 @@ public class Agent [JsonIgnore] public bool Installed => Plugin.Enabled; - /// - /// Allow to be routed - /// - public bool AllowRouting { get; set; } - /// /// Default is True, user will enable this by installing appropriate plugin. /// @@ -107,6 +103,7 @@ public class Agent Id = agent.Id, Name = agent.Name, Description = agent.Description, + Type = agent.Type, Instruction = agent.Instruction, Functions = agent.Functions, Responses = agent.Responses, @@ -114,7 +111,6 @@ public class Agent Knowledges = agent.Knowledges, IsPublic = agent.IsPublic, Disabled = agent.Disabled, - AllowRouting = agent.AllowRouting, Profiles = agent.Profiles, RoutingRules = agent.RoutingRules, LlmConfig = agent.LlmConfig, @@ -183,9 +179,9 @@ public class Agent return this; } - public Agent SetAllowRouting(bool allowRouting) + public Agent SetAgentType(string type) { - AllowRouting = allowRouting; + Type = type; return this; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/AgentFilter.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/AgentFilter.cs index 860a7dc9..fb2bfed2 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/AgentFilter.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/AgentFilter.cs @@ -6,9 +6,7 @@ public class AgentFilter public string? AgentName { get; set; } public bool? Disabled { get; set; } public bool? Installed { get; set; } - public bool? AllowRouting { get; set; } + public string? Type { get; set; } public bool? IsPublic { get; set; } - public bool? IsRouter { get; set; } - public bool? IsEvaluator { get; set; } public List? AgentIds { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs index c9f40fdc..f6a14ecb 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs @@ -5,7 +5,14 @@ namespace BotSharp.Abstraction.Routing; public interface IRoutingService { Agent Router { get; } + + /// + /// Get routable agents + /// + /// router's profile + /// RoutableAgent[] GetRoutableAgents(List profiles); + RoutingRule[] GetRulesByName(string name); RoutingRule[] GetRulesByAgentId(string id); List GetHandlers(); diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingContext.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingContext.cs index 9168b23f..085053e9 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingContext.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingContext.cs @@ -1,12 +1,19 @@ +using BotSharp.Abstraction.Agents; +using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Routing.Settings; +using Microsoft.Extensions.DependencyInjection; namespace BotSharp.Abstraction.Routing.Models; public class RoutingContext { + private readonly IServiceProvider _services; private readonly RoutingSettings _setting; - public RoutingContext(RoutingSettings setting) + private string[] _routerAgentIds; + + public RoutingContext(IServiceProvider services, RoutingSettings setting) { + _services = services; _setting = setting; } @@ -22,7 +29,22 @@ public class RoutingContext /// Agent that can handle user original goal. /// public string OriginAgentId - => _stack.Where(x => !_setting.AgentIds.Contains(x)).Last(); + { + get + { + if (_routerAgentIds == null) + { + var agentService = _services.GetRequiredService(); + _routerAgentIds = agentService.GetAgents(new AgentFilter + { + Type = AgentType.Routing + }).Result.Items + .Select(x => x.Id).ToArray(); + } + + return _stack.Where(x => !_routerAgentIds.Contains(x)).Last(); + } + } public bool IsEmpty => !_stack.Any(); public string GetCurrentAgentId() diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Settings/RoutingSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Settings/RoutingSettings.cs index eb7f099a..50cef510 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Settings/RoutingSettings.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Settings/RoutingSettings.cs @@ -2,10 +2,5 @@ namespace BotSharp.Abstraction.Routing.Settings; public class RoutingSettings { - /// - /// Router Agent Id - /// - public string[] AgentIds { get; set; } = new string[0]; - public string Planner { get; set; } = string.Empty; } diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs index 772c21f9..ce74d7c4 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs @@ -33,7 +33,7 @@ public partial class AgentService .SetDescription(foundAgent.Description) .SetIsPublic(foundAgent.IsPublic) .SetDisabled(foundAgent.Disabled) - .SetAllowRouting(foundAgent.AllowRouting) + .SetAgentType(foundAgent.Type) .SetProfiles(foundAgent.Profiles) .SetRoutingRules(foundAgent.RoutingRules) .SetInstruction(foundAgent.Instruction) diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs index 7e7493d1..15be18f9 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs @@ -17,7 +17,6 @@ public partial class AgentService var routeSetting = _services.GetRequiredService(); foreach (var agent in agents) { - agent.IsRouter = routeSetting.AgentIds.Contains(agent.Id); agent.Plugin = GetPlugin(agent.Id); } @@ -58,9 +57,6 @@ public partial class AgentService profile.LlmConfig.IsInherit = true; } - // Set IsRouter - var routeSetting = _services.GetRequiredService(); - profile.IsRouter = routeSetting.AgentIds.Contains(profile.Id); profile.Plugin = GetPlugin(profile.Id); return profile; diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs index 459cace4..4577af6d 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs @@ -19,7 +19,7 @@ public partial class AgentService record.Description = agent.Description ?? string.Empty; record.IsPublic = agent.IsPublic; record.Disabled = agent.Disabled; - record.AllowRouting = agent.AllowRouting; + record.Type = agent.Type; record.Profiles = agent.Profiles ?? new List(); record.RoutingRules = agent.RoutingRules ?? new List(); record.Instruction = agent.Instruction ?? string.Empty; @@ -60,7 +60,7 @@ public partial class AgentService .SetDescription(foundAgent.Description) .SetIsPublic(foundAgent.IsPublic) .SetDisabled(foundAgent.Disabled) - .SetAllowRouting(foundAgent.AllowRouting) + .SetAgentType(foundAgent.Type) .SetProfiles(foundAgent.Profiles) .SetRoutingRules(foundAgent.RoutingRules) .SetInstruction(foundAgent.Instruction) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs index eb46d5ec..92b840d7 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -59,7 +59,7 @@ public partial class ConversationService var routing = _services.GetRequiredService(); var settings = _services.GetRequiredService(); - response = settings.AgentIds.Contains(agentId) ? + response = agent.Type == AgentType.Routing ? await routing.InstructLoop(message) : await routing.InstructDirect(agent, message); diff --git a/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs b/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs index a0d9f190..561471e2 100644 --- a/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs +++ b/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs @@ -126,7 +126,7 @@ public class NaivePlanner : IPlaner var agentService = _services.GetRequiredService(); var agents = agentService.GetAgents(new AgentFilter { - AllowRouting = true + Type = AgentType.Task }).Result.Items.ToList(); var malformed = false; diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs index c72711ff..b2476921 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs @@ -28,8 +28,8 @@ namespace BotSharp.Core.Repository case AgentField.Disabled: UpdateAgentDisabled(agent.Id, agent.Disabled); break; - case AgentField.AllowRouting: - UpdateAgentAllowRouting(agent.Id, agent.AllowRouting); + case AgentField.Type: + UpdateAgentType(agent.Id, agent.Type); break; case AgentField.Profiles: UpdateAgentProfiles(agent.Id, agent.Profiles); @@ -112,12 +112,12 @@ namespace BotSharp.Core.Repository File.WriteAllText(agentFile, json); } - private void UpdateAgentAllowRouting(string agentId, bool allowRouting) + private void UpdateAgentType(string agentId, string type) { var (agent, agentFile) = GetAgentFromFile(agentId); if (agent == null) return; - agent.AllowRouting = allowRouting; + agent.Type = type; agent.UpdatedDateTime = DateTime.UtcNow; var json = JsonSerializer.Serialize(agent, _options); File.WriteAllText(agentFile, json); @@ -260,7 +260,7 @@ namespace BotSharp.Core.Repository agent.Description = inputAgent.Description; agent.IsPublic = inputAgent.IsPublic; agent.Disabled = inputAgent.Disabled; - agent.AllowRouting = inputAgent.AllowRouting; + agent.Type = inputAgent.Type; agent.Profiles = inputAgent.Profiles; agent.RoutingRules = inputAgent.RoutingRules; agent.LlmConfig = inputAgent.LlmConfig; @@ -336,9 +336,9 @@ namespace BotSharp.Core.Repository query = query.Where(x => x.Disabled == filter.Disabled); } - if (filter.AllowRouting.HasValue) + if (filter.Type != null) { - query = query.Where(x => x.AllowRouting == filter.AllowRouting); + query = query.Where(x => x.Type == filter.Type); } if (filter.IsPublic.HasValue) @@ -346,22 +346,6 @@ namespace BotSharp.Core.Repository query = query.Where(x => x.IsPublic == filter.IsPublic); } - if (filter.IsRouter.HasValue) - { - var route = _services.GetRequiredService(); - query = filter.IsRouter.Value ? - query.Where(x => route.AgentIds.Contains(x.Id)) : - query.Where(x => !route.AgentIds.Contains(x.Id)); - } - - if (filter.IsEvaluator.HasValue) - { - var evaluate = _services.GetRequiredService(); - query = filter.IsEvaluator.Value ? - query.Where(x => x.Id == evaluate.AgentId) : - query.Where(x => x.Id != evaluate.AgentId); - } - if (filter.AgentIds != null) { query = query.Where(x => filter.AgentIds.Contains(x.Id)); diff --git a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs index 84de68b0..bd11a5aa 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs @@ -17,7 +17,7 @@ public class RoutingAgentHook : AgentHookBase public override bool OnInstructionLoaded(string template, Dictionary dict) { - if (!_routingSetting.AgentIds.Contains(_agent.Id)) + if (_agent.Type != AgentType.Routing) { return base.OnInstructionLoaded(template, dict); } diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index 888a9eb5..d9cd266e 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -134,7 +134,7 @@ public partial class RoutingService : IRoutingService var filter = new AgentFilter { Disabled = false, - AllowRouting = true + Type = AgentType.Task }; var agents = db.GetAgents(filter); var records = agents.SelectMany(x => @@ -160,7 +160,7 @@ public partial class RoutingService : IRoutingService var filter = new AgentFilter { Disabled = false, - AllowRouting = true + Type = AgentType.Task }; var agents = db.GetAgents(filter); @@ -192,6 +192,12 @@ public partial class RoutingService : IRoutingService x.Profiles.Exists(x1 => profiles.Exists(y => x1 == y))) .ToArray(); } + else if (profiles == null || profiles.Count == 0) + { + routableAgents = routableAgents.Where(x => x.Profiles == null || + x.Profiles.Count == 0) + .ToArray(); + } return routableAgents; } diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/agent.json b/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/agent.json index b701d67b..b17531f4 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/agent.json +++ b/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/agent.json @@ -1,10 +1,12 @@ { + "id": "01e2fc5c-2c89-4ec7-8470-7688608b496c", "name": "Chatbot", "description": "AI chatbot that can do variaty of tasks", + "type": "task", "createdDateTime": "2024-01-15T10:39:32Z", "updatedDateTime": "2024-01-15T14:39:32Z", - "id": "01e2fc5c-2c89-4ec7-8470-7688608b496c", "iconUrl": "/images/users/bot.png", "disabled": false, - "isPublic": true + "isPublic": true, + "profiles": [ "standalone" ] } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/agent.json b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/agent.json index a92a4614..c8e4f4bd 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/agent.json +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/agent.json @@ -1,9 +1,10 @@ { + "id": "01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a", "name": "AI Assistant", "description": "AI assistant that can complete many different tasks", + "type": "routing", "createdDateTime": "2023-08-18T10:39:32.2349685Z", "updatedDateTime": "2023-08-18T14:39:32.2349686Z", - "id": "01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a", "iconUrl": "https://cdn.iconscout.com/icon/premium/png-256-thumb/route-1613278-1368497.png", "disabled": false, "isPublic": true diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs index fe6ce7e2..b5f8ea82 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs @@ -8,6 +8,7 @@ public class AgentCreationModel { public string Name { get; set; } public string Description { get; set; } + public string Type { get; set; } = AgentType.Task; /// /// LLM default system instructions @@ -57,7 +58,7 @@ public class AgentCreationModel Responses = Responses, Samples = Samples, IsPublic = IsPublic, - AllowRouting = AllowRouting, + Type = Type, Disabled = Disabled, Profiles = Profiles, RoutingRules = RoutingRules? diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs index 56b6d968..0a461ea6 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs @@ -9,7 +9,7 @@ public class AgentUpdateModel { public string Name { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; - + public string Type { get; set; } = AgentType.Task; /// /// Instruction /// @@ -62,7 +62,7 @@ public class AgentUpdateModel Description = Description ?? string.Empty, IsPublic = IsPublic, Disabled = Disabled, - AllowRouting = AllowRouting, + Type = Type, Profiles = Profiles ?? new List(), RoutingRules = RoutingRules? .Select(x => RoutingRuleUpdateModel.ToDomainElement(x))? diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs index 6a41a66b..e0d6c375 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs @@ -11,6 +11,7 @@ public class AgentViewModel public string Id { get; set; } public string Name { get; set; } public string Description { get; set; } + public string Type { get; set; } = AgentType.Task; public string Instruction { get; set; } public List Templates { get; set; } public List Functions { get; set; } @@ -59,17 +60,16 @@ public class AgentViewModel Id = agent.Id, Name = agent.Name, Description = agent.Description, + Type = agent.Type, Instruction = agent.Instruction, Templates = agent.Templates, Functions = agent.Functions, Responses = agent.Responses, Samples = agent.Samples, IsPublic= agent.IsPublic, - IsRouter = agent.IsRouter, IsHost = agent.IsHost, Disabled = agent.Disabled, IconUrl = agent.IconUrl, - AllowRouting = agent.AllowRouting, Profiles = agent.Profiles ?? new List(), RoutingRules = agent.RoutingRules, LlmConfig = agent.LlmConfig, diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs index b0f1d6e1..603f312e 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs @@ -6,6 +6,7 @@ public class AgentDocument : MongoBase { public string Name { get; set; } public string Description { get; set; } + public string Type { get; set; } public string? IconUrl { get; set; } public string Instruction { get; set; } public List Templates { get; set; } @@ -13,7 +14,6 @@ public class AgentDocument : MongoBase public List Responses { get; set; } public List Samples { get; set; } public bool IsPublic { get; set; } - public bool AllowRouting { get; set; } public bool Disabled { get; set; } public List Profiles { get; set; } public List RoutingRules { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs index 36fc9ff9..9a0f0cee 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs @@ -29,8 +29,8 @@ public partial class MongoRepository case AgentField.Disabled: UpdateAgentDisabled(agent.Id, agent.Disabled); break; - case AgentField.AllowRouting: - UpdateAgentAllowRouting(agent.Id, agent.AllowRouting); + case AgentField.Type: + UpdateAgentType(agent.Id, agent.Type); break; case AgentField.Profiles: UpdateAgentProfiles(agent.Id, agent.Profiles); @@ -109,11 +109,11 @@ public partial class MongoRepository _dc.Agents.UpdateOne(filter, update); } - private void UpdateAgentAllowRouting(string agentId, bool allowRouting) + private void UpdateAgentType(string agentId, string type) { var filter = Builders.Filter.Eq(x => x.Id, agentId); var update = Builders.Update - .Set(x => x.AllowRouting, allowRouting) + .Set(x => x.Type, type) .Set(x => x.UpdatedTime, DateTime.UtcNow); _dc.Agents.UpdateOne(filter, update); @@ -225,7 +225,7 @@ public partial class MongoRepository .Set(x => x.Name, agent.Name) .Set(x => x.Description, agent.Description) .Set(x => x.Disabled, agent.Disabled) - .Set(x => x.AllowRouting, agent.AllowRouting) + .Set(x => x.Type, agent.Type) .Set(x => x.Profiles, agent.Profiles) .Set(x => x.RoutingRules, agent.RoutingRules.Select(r => RoutingRuleMongoElement.ToMongoElement(r)).ToList()) .Set(x => x.Instruction, agent.Instruction) @@ -267,7 +267,7 @@ public partial class MongoRepository Samples = agent.Samples ?? new List(), IsPublic = agent.IsPublic, Disabled = agent.Disabled, - AllowRouting = agent.AllowRouting, + Type = agent.Type, Profiles = agent.Profiles, RoutingRules = !agent.RoutingRules.IsNullOrEmpty() ? agent.RoutingRules .Select(r => RoutingRuleMongoElement.ToDomainElement(agent.Id, agent.Name, r)) @@ -292,9 +292,9 @@ public partial class MongoRepository filters.Add(builder.Eq(x => x.Disabled, filter.Disabled.Value)); } - if (filter.AllowRouting.HasValue) + if (filter.Type != null) { - filters.Add(builder.Eq(x => x.AllowRouting, filter.AllowRouting.Value)); + filters.Add(builder.Eq(x => x.Type, filter.Type)); } if (filter.IsPublic.HasValue) @@ -302,32 +302,6 @@ public partial class MongoRepository filters.Add(builder.Eq(x => x.IsPublic, filter.IsPublic.Value)); } - if (filter.IsRouter.HasValue) - { - var route = _services.GetRequiredService(); - if (filter.IsRouter.Value) - { - filters.Add(builder.In(x => x.Id, route.AgentIds)); - } - else - { - filters.Add(builder.Nin(x => x.Id, route.AgentIds)); - } - } - - if (filter.IsEvaluator.HasValue) - { - var evaluate = _services.GetRequiredService(); - if (filter.IsEvaluator.Value) - { - filters.Add(builder.Eq(x => x.Id, evaluate.AgentId)); - } - else - { - filters.Add(builder.Ne(x => x.Id, evaluate.AgentId)); - } - } - if (filter.AgentIds != null) { filters.Add(builder.In(x => x.Id, filter.AgentIds)); @@ -354,7 +328,7 @@ public partial class MongoRepository Samples = x.Samples ?? new List(), IsPublic = x.IsPublic, Disabled = x.Disabled, - AllowRouting = x.AllowRouting, + Type = x.Type, Profiles = x.Profiles, RoutingRules = !x.RoutingRules.IsNullOrEmpty() ? x.RoutingRules .Select(r => RoutingRuleMongoElement.ToDomainElement(x.Id, x.Name, r)) @@ -418,7 +392,7 @@ public partial class MongoRepository .ToList() ?? new List(), Samples = x.Samples ?? new List(), IsPublic = x.IsPublic, - AllowRouting = x.AllowRouting, + Type = x.Type, Disabled = x.Disabled, Profiles = x.Profiles, RoutingRules = x.RoutingRules? diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Transaction.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Transaction.cs index 16b8080c..17e59c2e 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Transaction.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Transaction.cs @@ -54,7 +54,7 @@ public partial class MongoRepository .ToList() ?? new List(), Samples = x.Samples ?? new List(), IsPublic = x.IsPublic, - AllowRouting = x.AllowRouting, + Type = x.Type, Disabled = x.Disabled, Profiles = x.Profiles, RoutingRules = x.RoutingRules? @@ -77,7 +77,7 @@ public partial class MongoRepository .Set(x => x.Responses, agent.Responses) .Set(x => x.Samples, agent.Samples) .Set(x => x.IsPublic, agent.IsPublic) - .Set(x => x.AllowRouting, agent.AllowRouting) + .Set(x => x.Type, agent.Type) .Set(x => x.Disabled, agent.Disabled) .Set(x => x.Profiles, agent.Profiles) .Set(x => x.RoutingRules, agent.RoutingRules) diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingConversationHook.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingConversationHook.cs index d3ed4bdf..8c6d826e 100644 --- a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingConversationHook.cs +++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingConversationHook.cs @@ -53,7 +53,7 @@ public class RoutingConversationHook: ConversationHookBase public override async Task OnResponseGenerated(RoleDialogModel message) { var routerSettings = _services.GetRequiredService(); - bool saveFlag = !routerSettings.AgentIds.Contains(message.CurrentAgentId); + bool saveFlag = _agent.Type != AgentType.Routing; if (saveFlag) { 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 5a12078d..f9a7938a 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 @@ -1,9 +1,9 @@ { + "id": "f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b", "name": "Web Driver", "description": "Perform a specific action on a web browser", + "type": "task", "createdDateTime": "2024-01-02T00:00:00Z", "updatedDateTime": "2024-01-02T00:00:00Z", - "id": "f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b", - "allowRouting": true, "isPublic": true } \ No newline at end of file diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index 66b92dc0..61ff3a19 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -60,9 +60,6 @@ ], "Router": { - "AgentIds": [ - "01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a" - ], "Planner": "NaivePlanner" }, From 36e1ba481399ec975255f03e43afc6f5b9c9522b Mon Sep 17 00:00:00 2001 From: Haiping Chen <101423@smsassist.com> Date: Thu, 25 Jan 2024 22:42:57 -0600 Subject: [PATCH 08/12] MaxRecursionDepth --- .../BotSharp.Abstraction/Agents/Models/Agent.cs | 3 ++- .../Agents/Models/AgentLlmConfig.cs | 3 +++ .../Routing/RoutingService.InvokeAgent.cs | 17 +++++++---------- .../PlaywrightWebDriver.ChangeListValue.cs | 15 +++++++++++++-- .../agent.json | 5 ++++- 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs index 6a134cef..cb04e55e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs @@ -16,7 +16,8 @@ public class Agent /// Default LLM settings /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public AgentLlmConfig? LlmConfig { get; set; } + public AgentLlmConfig LlmConfig { get; set; } + = new AgentLlmConfig(); /// /// Instruction diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentLlmConfig.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentLlmConfig.cs index e902efc5..8025c2b8 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentLlmConfig.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentLlmConfig.cs @@ -21,4 +21,7 @@ public class AgentLlmConfig [JsonPropertyName("model")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Model { get; set; } + + [JsonPropertyName("max_recursion_depth")] + public int MaxRecursionDepth { get; set; } = 3; } diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs index 78b339fa..ccdee8d5 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs @@ -1,5 +1,3 @@ -using BotSharp.Abstraction.Agents.Models; -using BotSharp.Abstraction.MLTasks.Settings; using BotSharp.Abstraction.Routing.Models; using BotSharp.Abstraction.Templating; @@ -7,20 +5,19 @@ namespace BotSharp.Core.Routing; public partial class RoutingService { - const int MAXIMUM_RECURSION_DEPTH = 3; private int _currentRecursionDepth = 0; public async Task InvokeAgent(string agentId, List dialogs) { - _currentRecursionDepth++; - if (_currentRecursionDepth > MAXIMUM_RECURSION_DEPTH) - { - _logger.LogWarning($"Current recursive call depth greater than {MAXIMUM_RECURSION_DEPTH}, which will cause unexpected result."); - return false; - } - var agentService = _services.GetRequiredService(); var agent = await agentService.LoadAgent(agentId); + _currentRecursionDepth++; + if (_currentRecursionDepth > agent.LlmConfig.MaxRecursionDepth) + { + _logger.LogWarning($"Current recursive call depth greater than {agent.LlmConfig.MaxRecursionDepth}, which will cause unexpected result."); + return false; + } + var chatCompletion = CompletionProvider.GetChatCompletion(_services, agentConfig: agent.LlmConfig); diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeListValue.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeListValue.cs index f3a453ad..d3cb5c7d 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeListValue.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeListValue.cs @@ -96,9 +96,20 @@ public partial class PlaywrightWebDriver { Label = context.UpdateValue }); - + // Click on the blank area to activate posting - await body.ClickAsync(); + // await body.ClickAsync(); + if (!isVisible) + { + // Select the element you want to make visible (replace with your own selector) + var control = await _instance.Page.QuerySelectorAsync($"#{htmlElementContextOut.ElementId}"); + + // Show the element by modifying its CSS styles + await _instance.Page.EvaluateAsync(@"(element) => { + element.style.display = 'none'; + element.style.visibility = 'hidden'; + }", control); + } } catch (Exception ex) { 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 5a12078d..34890f9d 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,5 +5,8 @@ "updatedDateTime": "2024-01-02T00:00:00Z", "id": "f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b", "allowRouting": true, - "isPublic": true + "isPublic": true, + "llmConfig": { + "max_recursion_depth": 10 + } } \ No newline at end of file From 247ca09c29ca276e9c3052ddeefe5ecb793f13c2 Mon Sep 17 00:00:00 2001 From: Haiping Chen <101423@smsassist.com> Date: Thu, 25 Jan 2024 22:45:30 -0600 Subject: [PATCH 09/12] Update Web Driver. --- .../agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/agent.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 34890f9d..e06597f9 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 @@ -1,10 +1,10 @@ { + "id": "f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b", "name": "Web Driver", "description": "Perform a specific action on a web browser", + "type": "task", "createdDateTime": "2024-01-02T00:00:00Z", "updatedDateTime": "2024-01-02T00:00:00Z", - "id": "f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b", - "allowRouting": true, "isPublic": true, "llmConfig": { "max_recursion_depth": 10 From a768dc370109b00834bdee48ab639dbcb2995109 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Fri, 26 Jan 2024 15:47:06 -0600 Subject: [PATCH 10/12] add conv state log --- .../Models/ConversationStateLogModel.cs | 11 +++++++++++ .../Loggers/Models/StreamingLogModel.cs | 2 ++ .../Hooks/ChatHubConversationHook.cs | 14 ++++++++++++++ .../Hooks/StreamingLogHook.cs | 9 +++++---- 4 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationStateLogModel.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationStateLogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationStateLogModel.cs new file mode 100644 index 00000000..de80b485 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationStateLogModel.cs @@ -0,0 +1,11 @@ +namespace BotSharp.Abstraction.Conversations.Models; + +public class ConversationStateLogModel +{ + [JsonPropertyName("conversation_id")] + public string ConvsersationId { get; set; } + [JsonPropertyName("states")] + public string States { get; set; } + [JsonPropertyName("created_at")] + public DateTime CreateTime { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Loggers/Models/StreamingLogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Loggers/Models/StreamingLogModel.cs index d361bf59..51941138 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Loggers/Models/StreamingLogModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Loggers/Models/StreamingLogModel.cs @@ -4,6 +4,8 @@ public class StreamingLogModel { [JsonPropertyName("conversation_id")] public string ConversationId { get; set; } + [JsonPropertyName("name")] + public string? Name { get; set; } [JsonPropertyName("content")] public string Content { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs index 1b9a0089..bad8cf2b 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs @@ -100,6 +100,7 @@ public class ChatHubConversationHook : ConversationHookBase public override async Task OnResponseGenerated(RoleDialogModel message) { var conv = _services.GetRequiredService(); + var state = _services.GetRequiredService(); var json = JsonSerializer.Serialize(new ChatResponseModel() { @@ -115,7 +116,20 @@ public class ChatHubConversationHook : ConversationHookBase } }, _serializerOptions); await _chatHub.Clients.User(_user.Id).SendAsync("OnMessageReceivedFromAssistant", json); + await _chatHub.Clients.User(_user.Id).SendAsync("OnConversateStatesGenerated", BuildConversationStates(conv.ConversationId, state.GetStates())); await base.OnResponseGenerated(message); } + + private string BuildConversationStates(string conversationId, Dictionary states) + { + var model = new ConversationStateLogModel + { + ConvsersationId = conversationId, + States = JsonSerializer.Serialize(states, _serializerOptions), + CreateTime = DateTime.UtcNow + }; + + return JsonSerializer.Serialize(model, _serializerOptions); + } } diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs index bdf43c13..8b0aa2cd 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs @@ -37,7 +37,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook { var conversationId = _state.GetConversationId(); var log = $"MessageId: {message.MessageId} ==>\r\n{message.Role}: {message.Content}"; - await _chatHub.Clients.User(_user.Id).SendAsync("OnContentLogGenerated", BuildLog(conversationId, log)); + await _chatHub.Clients.User(_user.Id).SendAsync("OnContentLogGenerated", BuildLog(conversationId, _user.UserName, log)); } public async Task BeforeGenerating(Agent agent, List conversations) @@ -64,20 +64,21 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook var conversationId = _state.GetConversationId(); var agent = await agentService.LoadAgent(message.CurrentAgentId); - await _chatHub.Clients.User(_user.Id).SendAsync("OnContentLogGenerated", BuildLog(conversationId, tokenStats.Prompt)); + await _chatHub.Clients.User(_user.Id).SendAsync("OnContentLogGenerated", BuildLog(conversationId, agent?.Name, tokenStats.Prompt)); var log = message.Role == AgentRole.Function ? $"[{agent?.Name}]: {message.FunctionName}({message.FunctionArgs})" : $"[{agent?.Name}]: {message.Content}"; log += $"\r\n<== MessageId: {message.MessageId}"; - await _chatHub.Clients.User(_user.Id).SendAsync("OnContentLogGenerated", BuildLog(conversationId, log)); + await _chatHub.Clients.User(_user.Id).SendAsync("OnContentLogGenerated", BuildLog(conversationId, agent?.Name, log)); } - private string BuildLog(string conversationId, string content) + private string BuildLog(string conversationId, string? name, string content) { var log = new StreamingLogModel { ConversationId = conversationId, + Name = name, Content = content, CreateTime = DateTime.UtcNow }; From e1a948c9c71565e2f03dc2d48eb617f61eaf53f0 Mon Sep 17 00:00:00 2001 From: Haiping Chen <101423@smsassist.com> Date: Fri, 26 Jan 2024 16:23:10 -0600 Subject: [PATCH 11/12] Allow task agent to fallback to predefined router. --- .../Functions/Models/FunctionParametersDef.cs | 2 +- .../Routing/Enums/RuleType.cs | 14 ++++++ .../Routing/IRoutingService.cs | 14 +++++- .../Routing/Models/RoutingContext.cs | 13 ++++++ .../Routing/Models/RoutingRule.cs | 7 ++- .../Routing/Functions/FallbackToRouterFn.cs | 44 +++++++++++++++++++ .../Routing/Functions/RouteToAgentFn.cs | 2 +- .../Routing/Hooks/RoutingAgentHook.cs | 41 +++++++++++++++-- .../Routing/RoutingService.InvokeAgent.cs | 11 ++--- .../BotSharp.Core/Routing/RoutingService.cs | 6 +-- .../instruction.liquid | 3 +- .../templates/planner_prompt.hf.liquid | 2 +- .../templates/planner_prompt.naive.liquid | 1 - .../planner_prompt.sequential.liquid | 3 +- .../Functions/InputUserTextFn.cs | 1 + 15 files changed, 143 insertions(+), 21 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RuleType.cs create mode 100644 src/Infrastructure/BotSharp.Core/Routing/Functions/FallbackToRouterFn.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionParametersDef.cs b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionParametersDef.cs index d4e69e2a..975a17d6 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionParametersDef.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionParametersDef.cs @@ -5,7 +5,7 @@ namespace BotSharp.Abstraction.Functions.Models; public class FunctionParametersDef { [JsonPropertyName("type")] - public string Type { get; set; } = "string"; + public string Type { get; set; } = "object"; /// /// ParameterPropertyDef diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RuleType.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RuleType.cs new file mode 100644 index 00000000..8b910f25 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RuleType.cs @@ -0,0 +1,14 @@ +namespace BotSharp.Abstraction.Routing.Enums; + +public class RuleType +{ + /// + /// Fallback to redirect agent + /// + public const string Fallback = "fallback"; + + /// + /// Redirect to other agent if data validation failed + /// + public const string DataValidation = "data-validation"; +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs index f6a14ecb..8abcc9ca 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs @@ -13,8 +13,20 @@ public interface IRoutingService /// RoutableAgent[] GetRoutableAgents(List profiles); - RoutingRule[] GetRulesByName(string name); + /// + /// Get rules by agent name + /// + /// agent name + /// + RoutingRule[] GetRulesByAgentName(string name); + + /// + /// Get rules by agent id + /// + /// agent id + /// RoutingRule[] GetRulesByAgentId(string id); + List GetHandlers(); void ResetRecursiveCounter(); Task InvokeAgent(string agentId, List dialogs); diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingContext.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingContext.cs index 085053e9..d09658d8 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingContext.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingContext.cs @@ -68,6 +68,19 @@ public class RoutingContext _stack.Pop(); } + public void Replace(string agentId) + { + if (_stack.Count == 0) + { + _stack.Push(agentId); + } + else if (_stack.Peek() != agentId) + { + _stack.Pop(); + _stack.Push(agentId); + } + } + public void Empty() { _stack.Clear(); diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingRule.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingRule.cs index 88835216..b652ced1 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingRule.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingRule.cs @@ -1,3 +1,5 @@ +using BotSharp.Abstraction.Routing.Enums; + namespace BotSharp.Abstraction.Routing.Models; public class RoutingRule @@ -8,12 +10,15 @@ public class RoutingRule [JsonIgnore] public string AgentName { get; set; } + public string Type { get; set; } = RuleType.DataValidation; + public string Field { get; set; } public string Description { get; set; } + /// /// Field type: string, number, object /// - public string Type { get; set; } = "string"; + public string FieldType { get; set; } = "string"; public bool Required { get; set; } diff --git a/src/Infrastructure/BotSharp.Core/Routing/Functions/FallbackToRouterFn.cs b/src/Infrastructure/BotSharp.Core/Routing/Functions/FallbackToRouterFn.cs new file mode 100644 index 00000000..d6fb3dcb --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Routing/Functions/FallbackToRouterFn.cs @@ -0,0 +1,44 @@ +using BotSharp.Abstraction.Functions; +using BotSharp.Abstraction.Repositories.Filters; +using BotSharp.Abstraction.Routing.Models; +using BotSharp.Abstraction.Routing; + +namespace BotSharp.Core.Routing.Functions; + +public class FallbackToRouterFn : IFunctionCallback +{ + public string Name => "fallback_to_router"; + private readonly IServiceProvider _services; + public FallbackToRouterFn(IServiceProvider services) + { + _services = services; + } + + public async Task Execute(RoleDialogModel message) + { + var args = JsonSerializer.Deserialize(message.FunctionArgs); + var agentService = _services.GetRequiredService(); + var agents = await agentService.GetAgents(new AgentFilter + { + AgentName = args.AgentName + }); + var targetAgent = agents.Items.FirstOrDefault(); + if (targetAgent == null) + { + message.Content = $"Can't find routing agent {args.AgentName}"; + return false; + } + + var routing = _services.GetRequiredService(); + routing.Replace(targetAgent.Id); + + var router = _services.GetRequiredService(); + message.CurrentAgentId = targetAgent.Id; + var response = await router.InstructLoop(message); + + message.Content = response.Content; + message.StopCompletion = true; + + return true; + } +} diff --git a/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs b/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs index a197b9ba..26f14f45 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs @@ -91,7 +91,7 @@ public class RouteToAgentFn : IFunctionCallback var args = JsonSerializer.Deserialize(message.FunctionArgs); var routing = _services.GetRequiredService(); - var routingRules = routing.GetRulesByName(args.AgentName); + var routingRules = routing.GetRulesByAgentName(args.AgentName); if (routingRules == null || !routingRules.Any()) { diff --git a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs index bd11a5aa..314941c0 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs @@ -1,6 +1,8 @@ using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Routing; +using BotSharp.Abstraction.Routing.Enums; using BotSharp.Abstraction.Routing.Settings; +using System.Diagnostics.Metrics; namespace BotSharp.Core.Routing.Hooks; @@ -33,11 +35,42 @@ public class RoutingAgentHook : AgentHookBase public override bool OnFunctionsLoaded(List functions) { - /*functions.Add(new FunctionDef + if (_agent.Type == AgentType.Task) { - Name = "fallback_to_router", - Description = "If the user's request is beyond your capabilities, you can call this function for help." - });*/ + // check if enabled the routing rule + var routing = _services.GetRequiredService(); + var rule = routing.GetRulesByAgentId(_agent.Id) + .FirstOrDefault(x => x.Type == RuleType.Fallback); + if (rule != null) + { + var agentService = _services.GetRequiredService(); + var redirectAgent = agentService.GetAgent(rule.RedirectTo).Result; + + var json = JsonSerializer.Serialize(new + { + user_goal_agent = new + { + type = "string", + description = $"the fixed value is: {_agent.Name}" + }, + next_action_agent = new + { + type = "string", + description = $"the fixed value is: {redirectAgent.Name}" + } + }); + functions.Add(new FunctionDef + { + Name = "fallback_to_router", + Description = $"If the user's request is beyond your capabilities, you can call this function to handle by other agent ({redirectAgent.Name}).", + Parameters = + { + Properties = JsonSerializer.Deserialize(json) + } + }); + } + } + return base.OnFunctionsLoaded(functions); } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs index ccdee8d5..1c7789bd 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.Agents.Models; using BotSharp.Abstraction.Routing.Models; using BotSharp.Abstraction.Templating; @@ -70,7 +71,7 @@ public partial class RoutingService else if (!message.StopCompletion) { var routing = _services.GetRequiredService(); - + // Find response template var templateService = _services.GetRequiredService(); var responseTemplate = await templateService.RenderFunctionResponse(message.CurrentAgentId, message); @@ -83,8 +84,8 @@ public partial class RoutingService else { // Save to memory dialogs - dialogs.Add(RoleDialogModel.From(message, - role: AgentRole.Function, + dialogs.Add(RoleDialogModel.From(message, + role: AgentRole.Function, content: message.Content)); // Send to Next LLM @@ -94,8 +95,8 @@ public partial class RoutingService } else { - dialogs.Add(RoleDialogModel.From(message, - role: AgentRole.Assistant, + dialogs.Add(RoleDialogModel.From(message, + role: AgentRole.Assistant, content: message.Content)); } diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index d9cd266e..a241fbe2 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -172,13 +172,13 @@ public partial class RoutingService : IRoutingService Profiles = x.Profiles, RequiredFields = x.RoutingRules .Where(p => p.Required) - .Select(p => new ParameterPropertyDef(p.Field, p.Description, type: p.Type) + .Select(p => new ParameterPropertyDef(p.Field, p.Description, type: p.FieldType) { Required = p.Required }).ToList(), OptionalFields = x.RoutingRules .Where(p => !p.Required) - .Select(p => new ParameterPropertyDef(p.Field, p.Description, type: p.Type) + .Select(p => new ParameterPropertyDef(p.Field, p.Description, type: p.FieldType) { Required = p.Required }).ToList() @@ -202,7 +202,7 @@ public partial class RoutingService : IRoutingService return routableAgents; } - public RoutingRule[] GetRulesByName(string name) + public RoutingRule[] GetRulesByAgentName(string name) { return GetRoutingRecords() .Where(x => x.AgentName.ToLower() == name.ToLower()) diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid index eca5ac78..9147979f 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid @@ -4,6 +4,7 @@ You're {{router.name}} ({{router.description}}). Follow these steps to handle us 3. Determine which agent is suitable to handle this conversation. 4. Re-think on whether the function you chose matches the reason. 5. For agent required arguments, leave it as blank object if user doesn't provide it. +6. Response must be in JSON format. [FUNCTIONS] {% for handler in routing_handlers %} @@ -36,4 +37,4 @@ Optional args: {% endfor %} [CONVERSATION] -{{ conversation }} \ No newline at end of file +{{ conversation }} diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.hf.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.hf.liquid index 158bf965..20874a60 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.hf.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.hf.liquid @@ -1 +1 @@ -Break down the user’s most recent needs and figure out the next steps. Response must be in appropriate JSON format. \ No newline at end of file +Break down the user’s most recent needs and figure out the next steps. \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.naive.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.naive.liquid index 2f74b54b..449bd4d7 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.naive.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.naive.liquid @@ -1,4 +1,3 @@ What is the next step based on the CONVERSATION? -Response must be in required JSON format without any other contents. Route to the Agent that last handled the conversation if necessary. If user wants to speak to customer service, use function human_intervention_needed. \ 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 33f8db9f..a1025e50 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,3 +1,2 @@ In order to execute the instructions listed by the user in the order specified by the user. -What is the next step based on the CONVERSATION? -Response must be in required JSON format. \ No newline at end of file +What is the next step based on the CONVERSATION? \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserTextFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserTextFn.cs index b5052965..856203ed 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserTextFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserTextFn.cs @@ -27,6 +27,7 @@ public class InputUserTextFn : IFunctionCallback await _driver.InputUserText(agent, args, message.MessageId); message.Content = $"Input text \"{args.InputText}\" successfully."; + return true; } } From ab0b2004063a3bdade6303106c9740f831dbc2d8 Mon Sep 17 00:00:00 2001 From: Haiping Chen <101423@smsassist.com> Date: Fri, 26 Jan 2024 19:14:14 -0600 Subject: [PATCH 12/12] Change planner setting to agent level. --- .../Conversations/Models/RoleDialogModel.cs | 6 ------ .../Routing/Enums/RuleType.cs | 5 +++++ .../Routing/IRoutingService.cs | 2 +- .../Routing/Settings/RoutingSettings.cs | 1 - .../BotSharp.Core/Planning/HFPlanner.cs | 1 - .../BotSharp.Core/Planning/NaivePlanner.cs | 1 - .../Handlers/RouteToAgentRoutingHandler.cs | 1 - .../Routing/Hooks/RoutingAgentHook.cs | 21 ++++++++++++++----- .../BotSharp.Core/Routing/RoutingPlugin.cs | 12 ----------- .../Routing/RoutingService.GetPlanner.cs | 21 +++++++++++++++++++ .../Routing/RoutingService.InvokeAgent.cs | 12 +---------- .../BotSharp.Core/Routing/RoutingService.cs | 8 +++---- .../agent.json | 9 +++++++- .../Controllers/AgentController.cs | 8 ++++++- .../ViewModels/Agents/AgentViewModel.cs | 7 +------ .../Functions/SearchKnowledgesFn.cs | 1 - src/WebStarter/appsettings.json | 1 - 17 files changed, 64 insertions(+), 53 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetPlanner.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs index beeaf629..e9b40722 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs @@ -53,12 +53,6 @@ public class RoleDialogModel : ITrackableMessage [JsonIgnore(Condition = JsonIgnoreCondition.Always)] public bool StopCompletion { get; set; } - /// - /// Router routed to a wrong agent. - /// Set this flag as True will force router to re-route current request to a new agent. - /// - public bool UnmatchedAgent { get; set; } - public FunctionCallFromLlm Instruction { get; set; } private RoleDialogModel() diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RuleType.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RuleType.cs index 8b910f25..1d1913dd 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RuleType.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RuleType.cs @@ -11,4 +11,9 @@ public class RuleType /// Redirect to other agent if data validation failed /// public const string DataValidation = "data-validation"; + + /// + /// The planning approach name for next step + /// + public const string Planner = "planner"; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs index 8abcc9ca..a5369af9 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs @@ -27,7 +27,7 @@ public interface IRoutingService /// RoutingRule[] GetRulesByAgentId(string id); - List GetHandlers(); + List GetHandlers(Agent router); void ResetRecursiveCounter(); Task InvokeAgent(string agentId, List dialogs); Task InvokeFunction(string name, RoleDialogModel message); diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Settings/RoutingSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Settings/RoutingSettings.cs index 50cef510..6b7ef62c 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Settings/RoutingSettings.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Settings/RoutingSettings.cs @@ -2,5 +2,4 @@ namespace BotSharp.Abstraction.Routing.Settings; public class RoutingSettings { - public string Planner { get; set; } = string.Empty; } diff --git a/src/Infrastructure/BotSharp.Core/Planning/HFPlanner.cs b/src/Infrastructure/BotSharp.Core/Planning/HFPlanner.cs index 4551ae82..8f4e0033 100644 --- a/src/Infrastructure/BotSharp.Core/Planning/HFPlanner.cs +++ b/src/Infrastructure/BotSharp.Core/Planning/HFPlanner.cs @@ -4,7 +4,6 @@ using BotSharp.Abstraction.Planning; using BotSharp.Abstraction.Repositories; using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Routing.Models; -using BotSharp.Abstraction.Routing.Settings; using BotSharp.Abstraction.Templating; namespace BotSharp.Core.Planning; diff --git a/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs b/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs index 561471e2..78112559 100644 --- a/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs +++ b/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs @@ -3,7 +3,6 @@ using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Planning; using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Routing.Models; -using BotSharp.Abstraction.Routing.Settings; using BotSharp.Abstraction.Templating; namespace BotSharp.Core.Planning; diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs index 984df62c..0e1cce58 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs @@ -63,7 +63,6 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler var response = _dialogs.Last(); inst.Response = response.Content; - inst.UnmatchedAgent = response.UnmatchedAgent; return true; } diff --git a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs index 314941c0..5b42f5d8 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs @@ -28,7 +28,7 @@ public class RoutingAgentHook : AgentHookBase var routing = _services.GetRequiredService(); var agents = routing.GetRoutableAgents(_agent.Profiles); dict["routing_agents"] = agents; - dict["routing_handlers"] = routing.GetHandlers(); + dict["routing_handlers"] = routing.GetHandlers(_agent); return base.OnInstructionLoaded(template, dict); } @@ -51,13 +51,18 @@ public class RoutingAgentHook : AgentHookBase user_goal_agent = new { type = "string", - description = $"the fixed value is: {_agent.Name}" + description = $"{_agent.Name}" }, next_action_agent = new { type = "string", - description = $"the fixed value is: {redirectAgent.Name}" - } + description = $"{redirectAgent.Name}" + }, + reason = new + { + type = "string", + description = $"the reason why you need to fallback to [{redirectAgent.Name}] from [{_agent.Name}]" + }, }); functions.Add(new FunctionDef { @@ -65,7 +70,13 @@ public class RoutingAgentHook : AgentHookBase Description = $"If the user's request is beyond your capabilities, you can call this function to handle by other agent ({redirectAgent.Name}).", Parameters = { - Properties = JsonSerializer.Deserialize(json) + Properties = JsonSerializer.Deserialize(json), + Required = new List + { + "user_goal_agent", + "next_action_agent", + "reason" + } } }); } diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingPlugin.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingPlugin.cs index c0881ed2..946e6057 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingPlugin.cs @@ -38,17 +38,5 @@ public class RoutingPlugin : IBotSharpPlugin services.AddScoped(); services.AddScoped(); services.AddScoped(); - - services.AddScoped(provider => - { - var settingService = provider.GetRequiredService(); - var routingSettings = settingService.Bind("Router"); - if (routingSettings.Planner == nameof(HFPlanner)) - return provider.GetRequiredService(); - else if (routingSettings.Planner == nameof(SequentialPlanner)) - return provider.GetRequiredService(); - else - return provider.GetRequiredService(); - }); } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetPlanner.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetPlanner.cs new file mode 100644 index 00000000..01de9a29 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetPlanner.cs @@ -0,0 +1,21 @@ +using BotSharp.Abstraction.Agents.Models; +using BotSharp.Abstraction.Planning; +using BotSharp.Abstraction.Routing.Enums; +using BotSharp.Core.Planning; + +namespace BotSharp.Core.Routing; + +public partial class RoutingService +{ + public IPlaner GetPlanner(Agent router) + { + var planner = router.RoutingRules.FirstOrDefault(x => x.Type == RuleType.Planner); + + if (planner?.Field == nameof(HFPlanner)) + return _services.GetRequiredService(); + else if (planner?.Field == nameof(SequentialPlanner)) + return _services.GetRequiredService(); + else + return _services.GetRequiredService(); + } +} diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs index 1c7789bd..e144a688 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs @@ -57,18 +57,8 @@ public partial class RoutingService // Call functions await conversationService.CallFunctions(message); - // Router selected the wrong agent, handle this excluding the agent - if (message.UnmatchedAgent) - { - // Save to memory dialogs - var msg = RoleDialogModel.From(message, - role: AgentRole.Function, - content: message.Content); - msg.UnmatchedAgent = true; - dialogs.Add(msg); - } // Pass execution result to LLM to get response - else if (!message.StopCompletion) + if (!message.StopCompletion) { var routing = _services.GetRequiredService(); diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index a241fbe2..b94903af 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -73,9 +73,10 @@ public partial class RoutingService : IRoutingService var dialogs = conv.GetDialogHistory(); var context = _services.GetRequiredService(); - var planner = _services.GetRequiredService(); var executor = _services.GetRequiredService(); + var planner = GetPlanner(_router); + context.Push(_router.Id); int loopCount = 0; @@ -85,7 +86,6 @@ public partial class RoutingService : IRoutingService var conversation = await GetConversationContent(dialogs); _router.TemplateDict["conversation"] = conversation; - _router.TemplateDict["planner"] = _settings.Planner; // Get instruction from Planner var inst = await planner.GetNextInstruction(_router, message.MessageId); @@ -109,9 +109,9 @@ public partial class RoutingService : IRoutingService return response; } - public List GetHandlers() + public List GetHandlers(Agent router) { - var planer = _services.GetRequiredService(); + var planer = GetPlanner(router); return _services.GetServices() .Where(x => x.Planers == null || x.Planers.Contains(planer.GetType().Name)) diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/agent.json b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/agent.json index c8e4f4bd..18e856b7 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/agent.json +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/agent.json @@ -7,5 +7,12 @@ "updatedDateTime": "2023-08-18T14:39:32.2349686Z", "iconUrl": "https://cdn.iconscout.com/icon/premium/png-256-thumb/route-1613278-1368497.png", "disabled": false, - "isPublic": true + "isPublic": true, + "profiles": [ "default" ], + "routingRules": [ + { + "type": "planner", + "field": "HFPlanner" + } + ] } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs index dfc41e79..9183be01 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs @@ -33,9 +33,15 @@ public class AgentController : ControllerBase public async Task> GetAgents([FromQuery] AgentFilter filter) { var pagedAgents = await _agentService.GetAgents(filter); + var items = new List(); + foreach (var agent in pagedAgents.Items) + { + var renderedAgent = await _agentService.LoadAgent(agent.Id); + items.Add(renderedAgent); + } return new PagedItems { - Items = pagedAgents.Items.Select(x => AgentViewModel.FromAgent(x)).ToList(), + Items = items.Select(x => AgentViewModel.FromAgent(x)).ToList(), Count = pagedAgents.Count }; } diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs index e0d6c375..3c35b3af 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs @@ -17,18 +17,13 @@ public class AgentViewModel public List Functions { get; set; } public List Responses { get; set; } public List Samples { get; set; } + [JsonPropertyName("is_public")] public bool IsPublic { get; set; } - [JsonPropertyName("is_router")] - public bool IsRouter { get; set; } - [JsonPropertyName("is_host")] public bool IsHost { get; set; } - [JsonPropertyName("allow_routing")] - public bool AllowRouting { get; set; } - public bool Disabled { get; set; } [JsonPropertyName("icon_url")] diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/SearchKnowledgesFn.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/SearchKnowledgesFn.cs index f2bad9bf..68d5bd25 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/SearchKnowledgesFn.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/SearchKnowledgesFn.cs @@ -27,7 +27,6 @@ public class SearchKnowledgesFn : IFunctionCallback if (string.IsNullOrEmpty(knowledge)) { message.Content = "Can't find any relevant data in local knowledge base."; - message.UnmatchedAgent = true; } return true; diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index 61ff3a19..67952c9a 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -60,7 +60,6 @@ ], "Router": { - "Planner": "NaivePlanner" }, "Evaluator": {