diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/AgentHookBase.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/AgentHookBase.cs index 808f7821..496466e2 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/AgentHookBase.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/AgentHookBase.cs @@ -1,5 +1,10 @@ using BotSharp.Abstraction.Agents.Settings; +using BotSharp.Abstraction.Conversations; using BotSharp.Abstraction.Functions.Models; +using BotSharp.Abstraction.Repositories; +using BotSharp.Abstraction.Routing; +using Microsoft.Extensions.DependencyInjection; +using System.Data; namespace BotSharp.Abstraction.Agents; @@ -49,7 +54,71 @@ public abstract class AgentHookBase : IAgentHook return true; } - public virtual void OnAgentLoaded(Agent agent) + public virtual void OnAgentLoaded(Agent agent) { } + + public virtual void OnAgentUtilityLoaded(Agent agent) + { + if (agent.Type == AgentType.Routing) return; + + var conv = _services.GetRequiredService(); + var isConvMode = conv.IsConversationMode(); + if (!isConvMode) return; + + agent.Functions ??= []; + agent.Utilities ??= []; + + var (functions, templates) = GetUtilityContent(agent); + + agent.Functions.AddRange(functions); + foreach (var prompt in templates) + { + agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n"; + } + } + + private (IEnumerable, IEnumerable) GetUtilityContent(Agent agent) + { + var db = _services.GetRequiredService(); + var (functionNames, templateNames) = GetUniqueContent(agent.Utilities); + + if (agent.MergeUtility) + { + var routing = _services.GetRequiredService(); + var entryAgentId = routing.EntryAgentId; + if (!string.IsNullOrEmpty(entryAgentId)) + { + var entryAgent = db.GetAgent(entryAgentId); + var (fns, tps) = GetUniqueContent(entryAgent?.Utilities); + functionNames = functionNames.Concat(fns).Distinct().ToList(); + templateNames = templateNames.Concat(tps).Distinct().ToList(); + } + } + + var ua = db.GetAgent(BuiltInAgentId.UtilityAssistant); + var functions = ua?.Functions?.Where(x => functionNames.Contains(x.Name, StringComparer.OrdinalIgnoreCase))?.ToList() ?? []; + var templates = ua?.Templates?.Where(x => templateNames.Contains(x.Name, StringComparer.OrdinalIgnoreCase))?.Select(x => x.Content)?.ToList() ?? []; + return (functions, templates); + } + + private (IEnumerable, IEnumerable) GetUniqueContent(IEnumerable? utilities) + { + if (utilities.IsNullOrEmpty()) + { + return ([], []); + } + + utilities = utilities?.Where(x => !string.IsNullOrEmpty(x.Name) && !x.Disabled)?.ToList() ?? []; + var functionNames = utilities.SelectMany(x => x.Functions) + .Where(x => !string.IsNullOrEmpty(x.Name)) + .Select(x => x.Name) + .Distinct().ToList(); + var templateNames = utilities.SelectMany(x => x.Templates) + .Where(x => !string.IsNullOrEmpty(x.Name)) + .Select(x => x.Name) + .Distinct().ToList(); + + return (functionNames, templateNames); + } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentHook.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentHook.cs index 99cdadca..a3f53fb6 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentHook.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentHook.cs @@ -25,6 +25,8 @@ public interface IAgentHook bool OnSamplesLoaded(List samples); + void OnAgentUtilityLoaded(Agent agent); + /// /// Triggered when agent is loaded completely. /// diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs index 1461cc22..b8945f40 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs @@ -59,5 +59,5 @@ public interface IAgentService PluginDef GetPlugin(string agentId); - IEnumerable GetAgentUtilities(); + IEnumerable GetAgentUtilityOptions(); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentUtilityHook.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentUtilityHook.cs index 9d5667b8..603ec64e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentUtilityHook.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentUtilityHook.cs @@ -2,5 +2,5 @@ namespace BotSharp.Abstraction.Agents; public interface IAgentUtilityHook { - void AddUtilities(List utilities); + void AddUtilities(List utilities); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs index 43f0f5ba..e9fd7c5e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs @@ -87,13 +87,17 @@ public class Agent /// /// Profile by channel /// - public List Profiles { get; set; } - = new List(); + public List Profiles { get; set; } = new(); + + /// + /// Merge utilities from entry agent + /// + public bool MergeUtility { get; set; } /// /// Agent utilities /// - public List Utilities { get; set; } = new(); + public List Utilities { get; set; } = new(); /// /// Inherit from agent @@ -173,9 +177,9 @@ public class Agent return this; } - public Agent SetUtilities(List utilities) + public Agent SetUtilities(List utilities) { - Utilities = utilities ?? new List(); + Utilities = utilities ?? new List(); return this; } @@ -215,6 +219,12 @@ public class Agent return this; } + public Agent SetMergeUtility(bool merge) + { + MergeUtility = merge; + return this; + } + public Agent SetAgentType(string type) { Type = type; diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentUtility.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentUtility.cs new file mode 100644 index 00000000..40828944 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentUtility.cs @@ -0,0 +1,56 @@ +namespace BotSharp.Abstraction.Agents.Models; + +public class AgentUtility +{ + public string Name { get; set; } + public bool Disabled { get; set; } + public IEnumerable Functions { get; set; } = []; + public IEnumerable Templates { get; set; } = []; + + public AgentUtility() + { + + } + + public AgentUtility( + string name, + IEnumerable? functions = null, + IEnumerable? templates = null) + { + Name = name; + Functions = functions ?? []; + Templates = templates ?? []; + } +} + + +public class UtilityFunction : UtilityBase +{ + public UtilityFunction() + { + + } + + public UtilityFunction(string name) + { + Name = name; + } +} + +public class UtilityTemplate : UtilityBase +{ + public UtilityTemplate() + { + + } + + public UtilityTemplate(string name) + { + Name = name; + } +} + +public class UtilityBase +{ + public string Name { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Planning/IExecutor.cs b/src/Infrastructure/BotSharp.Abstraction/Planning/IExecutor.cs similarity index 76% rename from src/Infrastructure/BotSharp.Abstraction/Routing/Planning/IExecutor.cs rename to src/Infrastructure/BotSharp.Abstraction/Planning/IExecutor.cs index c8bffe6c..3362f1a4 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Planning/IExecutor.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Planning/IExecutor.cs @@ -1,6 +1,7 @@ using BotSharp.Abstraction.Functions.Models; +using BotSharp.Abstraction.Routing; -namespace BotSharp.Abstraction.Routing.Planning; +namespace BotSharp.Abstraction.Planning; public interface IExecutor { diff --git a/src/Infrastructure/BotSharp.Abstraction/Planning/ITaskPlanner.cs b/src/Infrastructure/BotSharp.Abstraction/Planning/ITaskPlanner.cs index a5668a52..67f30fb1 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Planning/ITaskPlanner.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Planning/ITaskPlanner.cs @@ -1,9 +1,19 @@ +using BotSharp.Abstraction.Functions.Models; + namespace BotSharp.Abstraction.Planning; /// /// Planning process for Task Agent +/// https://www.promptingguide.ai/techniques/cot /// -public class ITaskPlanner +public interface ITaskPlanner { - + Task GetNextInstruction(Agent router, string messageId, List dialogs); + Task AgentExecuting(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List dialogs); + Task AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List dialogs); + List BeforeHandleContext(FunctionCallFromLlm inst, RoleDialogModel message, List dialogs) + => dialogs; + bool AfterHandleContext(List dialogs, List taskAgentDialogs) + => true; + int MaxLoopCount => 5; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index 73577f45..037e0037 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -33,9 +33,13 @@ public interface IBotSharpRepository : IHaveServiceProvider List GetUserByIds(List ids) => throw new NotImplementedException(); List GetUsersByAffiliateId(string affiliateId) => throw new NotImplementedException(); User? GetUserByUserName(string userName) => throw new NotImplementedException(); + Dashboard? GetDashboard(string id = null) => throw new NotImplementedException(); void CreateUser(User user) => throw new NotImplementedException(); void UpdateExistUser(string userId, User user) => throw new NotImplementedException(); void UpdateUserVerified(string userId) => throw new NotImplementedException(); + void AddDashboardConversation(string userId, string conversationId) => throw new NotImplementedException(); + void RemoveDashboardConversation(string userId, string conversationId) => throw new NotImplementedException(); + void UpdateDashboardConversation(string userId, DashboardConversation dashConv) => throw new NotImplementedException(); void UpdateUserVerificationCode(string userId, string verficationCode) => throw new NotImplementedException(); void UpdateUserPassword(string userId, string password) => throw new NotImplementedException(); void UpdateUserEmail(string userId, string email) => throw new NotImplementedException(); diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RuleType.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RuleType.cs index 1d1913dd..c595d59c 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RuleType.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RuleType.cs @@ -12,6 +12,11 @@ public class RuleType /// public const string DataValidation = "data-validation"; + /// + /// The reasoning approach name for next step + /// + public const string Reasoner = "reasoner"; + /// /// The planning approach name for next step /// diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingContext.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingContext.cs index f75e8e0e..1dd9f50a 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingContext.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingContext.cs @@ -6,6 +6,7 @@ public interface IRoutingContext string FirstGoalAgentId(); bool ContainsAgentId(string agentId); string OriginAgentId { get; } + string EntryAgentId { get; } string ConversationId { get; } string MessageId { get; } void SetMessageId(string conversationId, string messageId); diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Planning/IRoutingPlaner.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Planning/IRoutingPlaner.cs deleted file mode 100644 index 7f3abde9..00000000 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Planning/IRoutingPlaner.cs +++ /dev/null @@ -1,19 +0,0 @@ -using BotSharp.Abstraction.Functions.Models; - -namespace BotSharp.Abstraction.Routing.Planning; - -/// -/// Task breakdown and execution plan -/// https://www.promptingguide.ai/techniques/cot -/// -public interface IRoutingPlaner -{ - Task GetNextInstruction(Agent router, string messageId, List dialogs); - Task AgentExecuting(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List dialogs); - Task AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List dialogs); - List BeforeHandleContext(FunctionCallFromLlm inst, RoleDialogModel message, List dialogs) - => dialogs; - bool AfterHandleContext(List dialogs, List taskAgentDialogs) - => true; - int MaxLoopCount => 5; -} diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Reasoning/IRoutingReasoner.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Reasoning/IRoutingReasoner.cs new file mode 100644 index 00000000..f7bb5eb4 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Reasoning/IRoutingReasoner.cs @@ -0,0 +1,30 @@ +using BotSharp.Abstraction.Functions.Models; + +namespace BotSharp.Abstraction.Routing.Reasoning; + +/// +/// Reasoning approaches for large language models (LLMs) help enhance their ability to solve complex problems, +/// handle tasks requiring logic, and provide accurate and contextually appropriate responses. +/// +public interface IRoutingReasoner +{ + string Name => "Unnamed Reasoner"; + string Description => "Each of these approaches leverages the capabilities of LLMs to reason more effectively, " + + "ensuring better performance and more coherent outputs across various types of complex tasks."; + + int MaxLoopCount => 5; + + Task GetNextInstruction(Agent router, string messageId, List dialogs); + + Task AgentExecuting(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List dialogs) + => Task.FromResult(true); + + Task AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List dialogs) + => Task.FromResult(true); + + List BeforeHandleContext(FunctionCallFromLlm inst, RoleDialogModel message, List dialogs) + => dialogs; + + bool AfterHandleContext(List dialogs, List taskAgentDialogs) + => true; +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Templating/ITemplateRender.cs b/src/Infrastructure/BotSharp.Abstraction/Templating/ITemplateRender.cs index e5ddeb61..82eff1c1 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Templating/ITemplateRender.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Templating/ITemplateRender.cs @@ -3,4 +3,5 @@ namespace BotSharp.Abstraction.Templating; public interface ITemplateRender { string Render(string template, Dictionary dict); + void Register(Type type); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/Enums/UserAction.cs b/src/Infrastructure/BotSharp.Abstraction/Users/Enums/UserAction.cs index b565a260..b702ea01 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/Enums/UserAction.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/Enums/UserAction.cs @@ -1,5 +1,8 @@ namespace BotSharp.Abstraction.Users.Enums; +/// +/// User actions on agent level +/// public static class UserAction { public const string Edit = "edit"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/Enums/UserPermission.cs b/src/Infrastructure/BotSharp.Abstraction/Users/Enums/UserPermission.cs index 58dfc186..64212fe4 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/Enums/UserPermission.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/Enums/UserPermission.cs @@ -1,5 +1,8 @@ namespace BotSharp.Abstraction.Users.Enums; +/// +/// User permission +/// public static class UserPermission { public const string CreateAgent = "create-agent"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs index cc67c1f7..134ca25b 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs @@ -29,4 +29,8 @@ public interface IUserService Task UpdatePassword(string newPassword, string verificationCode); Task GetUserTokenExpires(); Task UpdateUsersIsDisable(List userIds, bool isDisable); + Task AddDashboardConversation(string userId, string conversationId); + Task RemoveDashboardConversation(string userId, string conversationId); + Task UpdateDashboardConversation(string userId, DashboardConversation dashConv); + Task GetDashboard(string userId); } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/Models/Dashboard.cs b/src/Infrastructure/BotSharp.Abstraction/Users/Models/Dashboard.cs new file mode 100644 index 00000000..753cb390 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Users/Models/Dashboard.cs @@ -0,0 +1,20 @@ + +namespace BotSharp.Abstraction.Users.Models; + +public class Dashboard +{ + public IList ConversationList { get; set; } = []; +} + +public class DashboardComponent +{ + public required string Id { get; set; } + public string? Name { get; set; } +} + +public class DashboardConversation : DashboardComponent +{ + public string? ConversationId { get; set; } + public string? Instruction { get; set; } = ""; +} + diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/Models/UserAuthorization.cs b/src/Infrastructure/BotSharp.Abstraction/Users/Models/UserAuthorization.cs index 56df6a7d..f3c4c7f0 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/Models/UserAuthorization.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/Models/UserAuthorization.cs @@ -22,4 +22,26 @@ public static class UserAuthorizationExtension var actions = found.Actions ?? []; return actions.Any(x => x == targetAction); } + + /// + /// Get allowed user actions on the agent. If user is admin, returns null; + /// + /// + /// + /// + public static IEnumerable? GetAllowedAgentActions(this UserAuthorization auth, string agentId) + { + if (auth == null || string.IsNullOrEmpty(agentId)) + { + return []; + } + + if (auth.IsAdmin) + { + return null; + } + + var found = auth.AgentActions.FirstOrDefault(x => x.AgentId == agentId); + return found?.Actions ?? []; + } } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/Using.cs b/src/Infrastructure/BotSharp.Abstraction/Using.cs index 825f7d8f..9f35c1c8 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Using.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Using.cs @@ -12,7 +12,6 @@ global using BotSharp.Abstraction.Agents.Enums; global using BotSharp.Abstraction.Infrastructures.Enums; global using BotSharp.Abstraction.Models; global using BotSharp.Abstraction.Routing.Models; -global using BotSharp.Abstraction.Routing.Planning; global using BotSharp.Abstraction.Templating; global using BotSharp.Abstraction.Translation.Attributes; global using BotSharp.Abstraction.Messaging.Enums; diff --git a/src/Infrastructure/BotSharp.Core.SideCar/BotSharp.Core.SideCar.csproj b/src/Infrastructure/BotSharp.Core.SideCar/BotSharp.Core.SideCar.csproj index 4b661c2a..b7f89568 100644 --- a/src/Infrastructure/BotSharp.Core.SideCar/BotSharp.Core.SideCar.csproj +++ b/src/Infrastructure/BotSharp.Core.SideCar/BotSharp.Core.SideCar.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs b/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs index fda96b17..ec3ad0fe 100644 --- a/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs +++ b/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs @@ -1,3 +1,5 @@ +using BotSharp.Core.Infrastructures; + namespace BotSharp.Core.SideCar.Services; public class BotSharpConversationSideCar : IConversationSideCar @@ -116,7 +118,7 @@ public class BotSharpConversationSideCar : IConversationSideCar state.ResetCurrentState(); routing.Context.ResetRecursiveCounter(); routing.Context.ResetAgentStack(); - + Utilities.ClearCache(); } private void AfterExecute() @@ -130,6 +132,7 @@ public class BotSharpConversationSideCar : IConversationSideCar state.SetCurrentState(node.State); routing.Context.SetRecursiveCounter(node.RecursiveCounter); routing.Context.SetAgentStack(node.RoutingStack); + Utilities.ClearCache(); enabled = false; } } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs b/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs index 2f51f1dd..cfbc541f 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs @@ -1,6 +1,7 @@ using BotSharp.Abstraction.MLTasks; using BotSharp.Abstraction.Plugins.Models; using BotSharp.Abstraction.Settings; +using BotSharp.Abstraction.Templating; using BotSharp.Abstraction.Users.Enums; using Microsoft.Extensions.Configuration; @@ -33,6 +34,8 @@ public class AgentPlugin : IBotSharpPlugin services.AddScoped(provider => { var settingService = provider.GetRequiredService(); + var render = provider.GetRequiredService(); + render.Register(typeof(AgentSettings)); return settingService.Bind("Agent"); }); } diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs index f0a864a2..396a7d15 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs @@ -67,6 +67,7 @@ public partial class AgentService hook.OnSamplesLoaded(agent.Samples); } + hook.OnAgentUtilityLoaded(agent); hook.OnAgentLoaded(agent); } diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs index e9617d17..ba76e310 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs @@ -9,13 +9,16 @@ public partial class AgentService public string RenderedInstruction(Agent agent) { var render = _services.GetRequiredService(); - // update states var conv = _services.GetRequiredService(); + + // update states foreach (var t in conv.States.GetStates()) { agent.TemplateDict[t.Key] = t.Value; } - return render.Render(agent.Instruction, agent.TemplateDict); + + var res = render.Render(agent.Instruction, agent.TemplateDict); + return res; } public bool RenderFunction(Agent agent, FunctionDef def) @@ -108,16 +111,18 @@ public partial class AgentService public string RenderedTemplate(Agent agent, string templateName) { - // render liquid template - var render = _services.GetRequiredService(); - var template = agent.Templates.First(x => x.Name == templateName).Content; - // update states var conv = _services.GetRequiredService(); + var render = _services.GetRequiredService(); + + var template = agent.Templates.First(x => x.Name == templateName).Content; + + // update states foreach (var t in conv.States.GetStates()) { agent.TemplateDict[t.Key] = t.Value; } + // render liquid template var content = render.Render(template, agent.TemplateDict); HookEmitter.Emit(_services, async hook => @@ -126,4 +131,4 @@ public partial class AgentService return content; } -} +} \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs index 18afb27f..f871a2fe 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs @@ -1,5 +1,4 @@ using BotSharp.Abstraction.Repositories.Enums; -using BotSharp.Abstraction.Routing.Models; using BotSharp.Abstraction.Users.Enums; using BotSharp.Abstraction.Users.Models; using System.IO; @@ -28,16 +27,17 @@ public partial class AgentService record.Description = agent.Description ?? string.Empty; record.IsPublic = agent.IsPublic; record.Disabled = agent.Disabled; + record.MergeUtility = agent.MergeUtility; record.Type = agent.Type; - record.Profiles = agent.Profiles ?? new List(); - record.RoutingRules = agent.RoutingRules ?? new List(); + record.Profiles = agent.Profiles ?? []; + record.RoutingRules = agent.RoutingRules ?? []; record.Instruction = agent.Instruction ?? string.Empty; - record.ChannelInstructions = agent.ChannelInstructions ?? new List(); - record.Functions = agent.Functions ?? new List(); - record.Templates = agent.Templates ?? new List(); - record.Responses = agent.Responses ?? new List(); - record.Samples = agent.Samples ?? new List(); - record.Utilities = agent.Utilities ?? new List(); + record.ChannelInstructions = agent.ChannelInstructions ?? []; + record.Functions = agent.Functions ?? []; + record.Templates = agent.Templates ?? []; + record.Responses = agent.Responses ?? []; + record.Samples = agent.Samples ?? []; + record.Utilities = agent.Utilities ?? []; if (agent.LlmConfig != null && !agent.LlmConfig.IsInherit) { record.LlmConfig = agent.LlmConfig; @@ -90,6 +90,7 @@ public partial class AgentService .SetDescription(foundAgent.Description) .SetIsPublic(foundAgent.IsPublic) .SetDisabled(foundAgent.Disabled) + .SetMergeUtility(foundAgent.MergeUtility) .SetAgentType(foundAgent.Type) .SetProfiles(foundAgent.Profiles) .SetRoutingRules(foundAgent.RoutingRules) diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs index a48201d8..1dd331af 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs @@ -57,14 +57,14 @@ public partial class AgentService : IAgentService return userAgents; } - public IEnumerable GetAgentUtilities() + public IEnumerable GetAgentUtilityOptions() { - var utilities = new List(); + var utilities = new List(); var hooks = _services.GetServices(); foreach (var hook in hooks) { hook.AddUtilities(utilities); } - return utilities.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().OrderBy(x => x).ToList(); + return utilities.Where(x => !string.IsNullOrWhiteSpace(x.Name)).OrderBy(x => x.Name).ToList(); } } diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index 32987e69..1c82c84f 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -59,6 +59,11 @@ + + + + + @@ -73,10 +78,6 @@ - - - - @@ -120,16 +121,19 @@ PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + + PreserveNewest + + PreserveNewest diff --git a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs index a60fb4fd..c5e33a4b 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs +++ b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs @@ -12,6 +12,8 @@ using BotSharp.Core.Processors; using StackExchange.Redis; using BotSharp.Core.Infrastructures.Events; using BotSharp.Core.Roles.Services; +using BotSharp.Abstraction.Templating; +using BotSharp.Core.Templating; namespace BotSharp.Core; @@ -24,6 +26,8 @@ public static class BotSharpCoreExtensions services.AddSingleton(x => interpreterSettings); services.AddSingleton(); + // Register template render + services.AddSingleton(); services.AddScoped(); services.AddScoped(); diff --git a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs index a9be6fd2..09936dec 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs @@ -1,13 +1,13 @@ using BotSharp.Abstraction.Google.Settings; using BotSharp.Abstraction.Instructs; using BotSharp.Abstraction.Messaging; +using BotSharp.Abstraction.Planning; using BotSharp.Abstraction.Plugins.Models; -using BotSharp.Abstraction.Routing.Planning; using BotSharp.Abstraction.Settings; using BotSharp.Abstraction.Templating; using BotSharp.Core.Instructs; using BotSharp.Core.Messaging; -using BotSharp.Core.Routing.Planning; +using BotSharp.Core.Routing.Reasoning; using BotSharp.Core.Templating; using BotSharp.Core.Translation; using Microsoft.Extensions.Configuration; @@ -30,6 +30,8 @@ public class ConversationPlugin : IBotSharpPlugin services.AddScoped(provider => { var settingService = provider.GetRequiredService(); + var render = provider.GetRequiredService(); + render.Register(typeof(ConversationSetting)); return settingService.Bind("Conversation"); }); @@ -48,8 +50,6 @@ public class ConversationPlugin : IBotSharpPlugin // Rich content messaging services.AddScoped(); - // Register template render - services.AddSingleton(); services.AddScoped(); services.AddScoped(); diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index 8ee4c662..152b74ca 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -1,5 +1,4 @@ using BotSharp.Abstraction.Conversations.Enums; -using BotSharp.Abstraction.Users.Enums; namespace BotSharp.Core.Conversations.Services; @@ -21,13 +20,14 @@ public class ConversationStateService : IConversationStateService, IDisposable /// private ConversationState _historyStates; - public ConversationStateService(ILogger logger, + public ConversationStateService( IServiceProvider services, - IBotSharpRepository db) + IBotSharpRepository db, + ILogger logger) { - _logger = logger; _services = services; _db = db; + _logger = logger; _curStates = new ConversationState(); _historyStates = new ConversationState(); } @@ -125,8 +125,13 @@ public class ConversationStateService : IConversationStateService, IDisposable var curMsgId = routingCtx.MessageId; _historyStates = _db.GetConversationStates(conversationId); + + var endNodes = new Dictionary(); + + if (_historyStates.IsNullOrEmpty()) return endNodes; + var dialogs = _db.GetConversationDialogs(conversationId); - var userDialogs = dialogs.Where(x => x.MetaData?.Role == AgentRole.User || x.MetaData?.Role == UserRole.User) + var userDialogs = dialogs.Where(x => x.MetaData?.Role == AgentRole.User) .GroupBy(x => x.MetaData?.MessageId) .Select(g => g.First()) .OrderBy(x => x.MetaData?.CreateTime) @@ -134,9 +139,6 @@ public class ConversationStateService : IConversationStateService, IDisposable var curMsgIndex = userDialogs.FindIndex(x => !string.IsNullOrEmpty(curMsgId) && x.MetaData?.MessageId == curMsgId); curMsgIndex = curMsgIndex < 0 ? userDialogs.Count() : curMsgIndex; - var endNodes = new Dictionary(); - if (_historyStates.IsNullOrEmpty()) return endNodes; - foreach (var state in _historyStates) { var key = state.Key; diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs index 7f545527..07915917 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs @@ -55,7 +55,7 @@ namespace BotSharp.Core.Repository UpdateAgentLlmConfig(agent.Id, agent.LlmConfig); break; case AgentField.Utility: - UpdateAgentUtilities(agent.Id, agent.Utilities); + UpdateAgentUtilities(agent.Id, agent.MergeUtility, agent.Utilities); break; case AgentField.All: UpdateAgentAllFields(agent); @@ -151,13 +151,14 @@ namespace BotSharp.Core.Repository File.WriteAllText(agentFile, json); } - private void UpdateAgentUtilities(string agentId, List utilities) + private void UpdateAgentUtilities(string agentId, bool mergeUtility, List utilities) { if (utilities == null) return; var (agent, agentFile) = GetAgentFromFile(agentId); if (agent == null) return; + agent.MergeUtility = mergeUtility; agent.Utilities = utilities; agent.UpdatedDateTime = DateTime.UtcNow; var json = JsonSerializer.Serialize(agent, _options); @@ -291,6 +292,7 @@ namespace BotSharp.Core.Repository agent.Description = inputAgent.Description; agent.IsPublic = inputAgent.IsPublic; agent.Disabled = inputAgent.Disabled; + agent.MergeUtility = inputAgent.MergeUtility; agent.Type = inputAgent.Type; agent.Profiles = inputAgent.Profiles; agent.Utilities = inputAgent.Utilities; diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs index 317d6fc2..9d5b42be 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs @@ -53,6 +53,11 @@ public partial class FileRepository return Users.FirstOrDefault(x => x.UserName == userName.ToLower()); } + public Dashboard? GetDashboard(string id = null) + { + return Dashboards.FirstOrDefault(); + } + public void CreateUser(User user) { var userId = Guid.NewGuid().ToString(); @@ -203,4 +208,68 @@ public partial class FileRepository _users = []; return true; } + + public void AddDashboardConversation(string userId, string conversationId) + { + var user = GetUserById(userId); + if (user == null) return; + + // one user only has one dashboard currently + var dash = Dashboards.FirstOrDefault(); + dash ??= new(); + var existingConv = dash.ConversationList.FirstOrDefault(x => string.Equals(x.ConversationId, conversationId, StringComparison.OrdinalIgnoreCase)); + if (existingConv != null) return; + + var dashconv = new DashboardConversation + { + Id = Guid.NewGuid().ToString(), + ConversationId = conversationId + }; + + dash.ConversationList.Add(dashconv); + + var dir = Path.Combine(_dbSettings.FileRepository, USERS_FOLDER, userId); + var path = Path.Combine(dir, DASHBOARD_FILE); + File.WriteAllText(path, JsonSerializer.Serialize(dash, _options)); + } + + public void RemoveDashboardConversation(string userId, string conversationId) + { + var user = GetUserById(userId); + if (user == null) return; + + // one user only has one dashboard currently + var dash = Dashboards.FirstOrDefault(); + if (dash == null) return; + + var dashconv = dash.ConversationList.FirstOrDefault( + c => string.Equals(c.ConversationId, conversationId, StringComparison.OrdinalIgnoreCase)); + if (dashconv == null) return; + + dash.ConversationList.Remove(dashconv); + + var dir = Path.Combine(_dbSettings.FileRepository, USERS_FOLDER, userId); + var path = Path.Combine(dir, DASHBOARD_FILE); + File.WriteAllText(path, JsonSerializer.Serialize(dash, _options)); + } + + public void UpdateDashboardConversation(string userId, DashboardConversation dashConv) + { + var user = GetUserById(userId); + if (user == null) return; + + // one user only has one dashboard currently + var dash = Dashboards.FirstOrDefault(); + if (dash == null) return; + + var curIdx = dash.ConversationList.ToList().FindIndex( + x => string.Equals(x.ConversationId, dashConv.ConversationId, StringComparison.OrdinalIgnoreCase)); + if (curIdx < 0) return; + + dash.ConversationList[curIdx] = dashConv; + + var dir = Path.Combine(_dbSettings.FileRepository, USERS_FOLDER, userId); + var path = Path.Combine(dir, DASHBOARD_FILE); + File.WriteAllText(path, JsonSerializer.Serialize(dash, _options)); + } } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs index 0edcb699..57fbcb91 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs @@ -22,6 +22,7 @@ public partial class FileRepository : IBotSharpRepository private const string AGENT_FILE = "agent.json"; private const string AGENT_INSTRUCTION_FILE = "instruction"; private const string AGENT_SAMPLES_FILE = "samples.txt"; + private const string DASHBOARD_FILE = "dashboard.json"; private const string AGENT_INSTRUCTIONS_FOLDER = "instructions"; private const string AGENT_FUNCTIONS_FOLDER = "functions"; private const string AGENT_TEMPLATES_FOLDER = "templates"; @@ -83,6 +84,7 @@ public partial class FileRepository : IBotSharpRepository private List _roles = new List(); private List _users = new List(); + private List _dashboards = []; private List _agents = new List(); private List _roleAgents = new List(); private List _userAgents = new List(); @@ -170,6 +172,36 @@ public partial class FileRepository : IBotSharpRepository } } + private IQueryable Dashboards + { + get + { + if (!_dashboards.IsNullOrEmpty()) + { + return _dashboards.AsQueryable(); + } + + var dir = Path.Combine(_dbSettings.FileRepository, USERS_FOLDER); + _dashboards = []; + if (Directory.Exists(dir)) + { + foreach (var d in Directory.GetDirectories(dir)) + { + var dashboardFile = Path.Combine(d, DASHBOARD_FILE); + if (!Directory.Exists(d) || !File.Exists(dashboardFile)) + continue; + + var json = File.ReadAllText(dashboardFile); + var dash = JsonSerializer.Deserialize(json, _options); + + if (dash == null) continue; + _dashboards.Add(dash); + } + } + return _dashboards.AsQueryable(); + } + } + private IQueryable Agents { get diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ContinueExecuteTaskRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ContinueExecuteTaskRoutingHandler.cs index 646dd481..2ee6f9d7 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ContinueExecuteTaskRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ContinueExecuteTaskRoutingHandler.cs @@ -4,7 +4,7 @@ using BotSharp.Abstraction.Repositories; using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Routing; using BotSharp.Abstraction.Routing.Settings; -using BotSharp.Core.Routing.Planning; +using BotSharp.Core.Routing.Reasoning; namespace BotSharp.Core.Routing.Handlers; @@ -27,7 +27,7 @@ public class ContinueExecuteTaskRoutingHandler : RoutingHandlerBase//, IRoutingH public List Planers => new List { - nameof(HFPlanner) + nameof(HFReasoner) }; public ContinueExecuteTaskRoutingHandler(IServiceProvider services, ILogger logger, RoutingSettings settings) diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/InterruptTaskExecutionRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/InterruptTaskExecutionRoutingHandler.cs index da58b98b..0d11f296 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/InterruptTaskExecutionRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/InterruptTaskExecutionRoutingHandler.cs @@ -1,7 +1,7 @@ using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Routing; using BotSharp.Abstraction.Routing.Settings; -using BotSharp.Core.Routing.Planning; +using BotSharp.Core.Routing.Reasoning; namespace BotSharp.Core.Routing.Handlers; @@ -19,7 +19,7 @@ public class InterruptTaskExecutionRoutingHandler : RoutingHandlerBase//, IRouti public List Planers => new List { - nameof(HFPlanner) + nameof(HFReasoner) }; public InterruptTaskExecutionRoutingHandler(IServiceProvider services, ILogger logger, RoutingSettings settings) diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs index 5b5b1025..038488eb 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs @@ -1,5 +1,5 @@ using BotSharp.Abstraction.Routing.Settings; -using BotSharp.Core.Routing.Planning; +using BotSharp.Core.Routing.Reasoning; namespace BotSharp.Core.Routing.Handlers; @@ -26,7 +26,7 @@ public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase//, IRoutin public List Planers => new List { - nameof(HFPlanner) + nameof(HFReasoner) }; public RetrieveDataFromAgentRoutingHandler(IServiceProvider services, ILogger logger, RoutingSettings settings) diff --git a/src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/FirstStagePlanParameter.cs b/src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/FirstStagePlanParameter.cs deleted file mode 100644 index 7bececc6..00000000 --- a/src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/FirstStagePlanParameter.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Text.Json.Serialization; - -public class FirstStagePlanParameter -{ - [JsonPropertyName("input_args")] - public JsonDocument[] Parameters { get; set; } = new JsonDocument[0]; - - [JsonPropertyName("output_results")] - public string[] Results { get; set; } = new string[0]; - - public override string ToString() - { - return $"INPUTS:\r\n{JsonSerializer.Serialize(Parameters)}\r\n\r\nOUTPUTS:\r\n{JsonSerializer.Serialize(Results)}"; - } -} \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/SecondStagePlan.cs b/src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/SecondStagePlan.cs deleted file mode 100644 index f180c043..00000000 --- a/src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/SecondStagePlan.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Text.Json.Serialization; - -namespace BotSharp.Core.Routing.Planning; - -public class SecondStagePlan -{ - [JsonPropertyName("related_tables")] - public string[] Tables { get; set; } = new string[0]; - - [JsonPropertyName("description")] - public string Description { get; set; } = ""; - - [JsonPropertyName("tool_name")] - public string Tool { get; set; } = ""; - - [JsonPropertyName("input_args")] - public JsonDocument[] Parameters { get; set; } = new JsonDocument[0]; - - [JsonPropertyName("output_results")] - public string[] Results { get; set; } = new string[0]; -} diff --git a/src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/SecondStagePlanParameter.cs b/src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/SecondStagePlanParameter.cs deleted file mode 100644 index 1d043740..00000000 --- a/src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/SecondStagePlanParameter.cs +++ /dev/null @@ -1,4 +0,0 @@ -public class SecondStagePlanParameter : FirstStagePlanParameter -{ - -} diff --git a/src/Infrastructure/BotSharp.Core/Routing/Planning/HFPlanner.cs b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/HFReasoner.cs similarity index 72% rename from src/Infrastructure/BotSharp.Core/Routing/Planning/HFPlanner.cs rename to src/Infrastructure/BotSharp.Core/Routing/Reasoning/HFReasoner.cs index 8984da51..0c825a05 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Planning/HFPlanner.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/HFReasoner.cs @@ -1,17 +1,33 @@ -using BotSharp.Abstraction.Routing.Planning; +/***************************************************************************** + Copyright 2024 Written by Haiping Chen. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +******************************************************************************/ + +using BotSharp.Abstraction.Routing.Reasoning; using BotSharp.Abstraction.Templating; -namespace BotSharp.Core.Routing.Planning; +namespace BotSharp.Core.Routing.Reasoning; /// -/// Human feedback based planner +/// Human feedback based reasoner /// -public class HFPlanner : IRoutingPlaner +public class HFReasoner : IRoutingReasoner { private readonly IServiceProvider _services; private readonly ILogger _logger; - public HFPlanner(IServiceProvider services, ILogger logger) + public HFReasoner(IServiceProvider services, ILogger logger) { _services = services; _logger = logger; @@ -37,7 +53,7 @@ public class HFPlanner : IRoutingPlaner { new RoleDialogModel(AgentRole.User, next) { - FunctionName = nameof(HFPlanner), + FunctionName = nameof(HFReasoner), MessageId = messageId } }; @@ -60,7 +76,7 @@ public class HFPlanner : IRoutingPlaner } // Fix LLM malformed response - PlannerHelper.FixMalformedResponse(_services, inst); + ReasonerHelper.FixMalformedResponse(_services, inst); return inst; } @@ -87,13 +103,13 @@ public class HFPlanner : IRoutingPlaner public async Task AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List dialogs) { var context = _services.GetRequiredService(); - context.Empty(reason: $"Agent queue is cleared by {nameof(HFPlanner)}"); + context.Empty(reason: $"Agent queue is cleared by {nameof(HFReasoner)}"); return true; } private string GetNextStepPrompt(Agent router) { - var template = router.Templates.First(x => x.Name == "planner_prompt.hf").Content; + var template = router.Templates.First(x => x.Name == "reasoner.hf").Content; var render = _services.GetRequiredService(); // update states var conv = _services.GetRequiredService(); diff --git a/src/Infrastructure/BotSharp.Core/Routing/Planning/InstructExecutor.cs b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/InstructExecutor.cs similarity index 91% rename from src/Infrastructure/BotSharp.Core/Routing/Planning/InstructExecutor.cs rename to src/Infrastructure/BotSharp.Core/Routing/Reasoning/InstructExecutor.cs index a1d413f0..6bb23e97 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Planning/InstructExecutor.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/InstructExecutor.cs @@ -1,6 +1,6 @@ -using BotSharp.Abstraction.Routing.Planning; +using BotSharp.Abstraction.Planning; -namespace BotSharp.Core.Routing.Planning; +namespace BotSharp.Core.Routing.Reasoning; public class InstructExecutor : IExecutor { diff --git a/src/Infrastructure/BotSharp.Core/Routing/Planning/NaivePlanner.cs b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/NaiveReasoner.cs similarity index 73% rename from src/Infrastructure/BotSharp.Core/Routing/Planning/NaivePlanner.cs rename to src/Infrastructure/BotSharp.Core/Routing/Reasoning/NaiveReasoner.cs index 6ebc5012..4f7bba6c 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Planning/NaivePlanner.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/NaiveReasoner.cs @@ -1,16 +1,35 @@ +/***************************************************************************** + Copyright 2024 Written by Haiping Chen. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +******************************************************************************/ + using BotSharp.Abstraction.Infrastructures.Enums; using BotSharp.Abstraction.Routing.Models; -using BotSharp.Abstraction.Routing.Planning; +using BotSharp.Abstraction.Routing.Reasoning; using BotSharp.Abstraction.Templating; -namespace BotSharp.Core.Routing.Planning; +namespace BotSharp.Core.Routing.Reasoning; -public class NaivePlanner : IRoutingPlaner +/// +/// simple or unsophisticated methods used to decide which specialized model or module in a system to engage for a given task. +/// +public class NaiveReasoner : IRoutingReasoner { private readonly IServiceProvider _services; private readonly ILogger _logger; - public NaivePlanner(IServiceProvider services, ILogger logger) + public NaiveReasoner(IServiceProvider services, ILogger logger) { _services = services; _logger = logger; @@ -46,7 +65,7 @@ public class NaivePlanner : IRoutingPlaner { new RoleDialogModel(AgentRole.User, next) { - FunctionName = nameof(NaivePlanner), + FunctionName = nameof(NaiveReasoner), MessageId = messageId } }; @@ -69,7 +88,7 @@ public class NaivePlanner : IRoutingPlaner } // Fix LLM malformed response - PlannerHelper.FixMalformedResponse(_services, inst); + ReasonerHelper.FixMalformedResponse(_services, inst); return inst; } @@ -99,7 +118,7 @@ public class NaivePlanner : IRoutingPlaner } else { - context.Empty(reason: $"Agent queue is cleared by {nameof(NaivePlanner)}"); + context.Empty(reason: $"Agent queue is cleared by {nameof(NaiveReasoner)}"); // context.Push(inst.OriginalAgent, "Push user goal agent"); } return true; @@ -107,7 +126,7 @@ public class NaivePlanner : IRoutingPlaner private string GetNextStepPrompt(Agent router) { - var template = router.Templates.First(x => x.Name == "planner_prompt.naive").Content; + var template = router.Templates.First(x => x.Name == "reasoner.naive").Content; var states = _services.GetRequiredService(); var render = _services.GetRequiredService(); diff --git a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/OneStepForwardReasoner.cs b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/OneStepForwardReasoner.cs new file mode 100644 index 00000000..97568751 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/OneStepForwardReasoner.cs @@ -0,0 +1,137 @@ +/***************************************************************************** + Copyright 2024 Written by Haiping Chen. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +******************************************************************************/ + +using BotSharp.Abstraction.Infrastructures.Enums; +using BotSharp.Abstraction.Routing.Models; +using BotSharp.Abstraction.Routing.Reasoning; +using BotSharp.Abstraction.Templating; + +namespace BotSharp.Core.Routing.Reasoning; + +/// +/// One-step forward reasoning is a straightforward reasoning approach where the model or agent evaluates its current state +/// and takes the next best logical step toward the solution without extensive lookahead or planning. +/// This type of reasoning involves making a decision based on the current situation and immediate context +/// rather than considering multiple future steps or possibilities. +/// +public class OneStepForwardReasoner : IRoutingReasoner +{ + public string Name => "one-step-forward"; + + private readonly IServiceProvider _services; + private readonly ILogger _logger; + + public OneStepForwardReasoner(IServiceProvider services, ILogger logger) + { + _services = services; + _logger = logger; + } + + public async Task GetNextInstruction(Agent router, string messageId, List dialogs) + { + var next = GetNextStepPrompt(router); + + var inst = new FunctionCallFromLlm(); + + // 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); + dialogs = new List + { + new RoleDialogModel(AgentRole.User, next) + { + FunctionName = Name, + 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++; + } + } + + // Fix LLM malformed response + ReasonerHelper.FixMalformedResponse(_services, inst); + + return inst; + } + + public async Task AgentExecuting(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List dialogs) + { + // 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, List dialogs) + { + var context = _services.GetRequiredService(); + if (inst.UnmatchedAgent) + { + var unmatchedAgentId = context.GetCurrentAgentId(); + + // Exclude the wrong routed agent + var agents = router.TemplateDict["routing_agents"] as RoutableAgent[]; + router.TemplateDict["routing_agents"] = agents.Where(x => x.AgentId != unmatchedAgentId).ToArray(); + + // Handover to Router; + context.Pop(); + } + else + { + context.Empty(reason: $"Agent queue is cleared by {nameof(NaiveReasoner)}"); + // context.Push(inst.OriginalAgent, "Push user goal agent"); + } + return true; + } + + private string GetNextStepPrompt(Agent router) + { + var template = router.Templates.First(x => x.Name == "reasoner.one-step-forward").Content; + + var states = _services.GetRequiredService(); + var render = _services.GetRequiredService(); + return render.Render(template, new Dictionary + { + { StateConst.EXPECTED_ACTION_AGENT, states.GetState(StateConst.EXPECTED_ACTION_AGENT) }, + { StateConst.EXPECTED_GOAL_AGENT, states.GetState(StateConst.EXPECTED_GOAL_AGENT) } + }); + } +} diff --git a/src/Infrastructure/BotSharp.Core/Routing/Planning/PlannerHelper.cs b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/ReasonerHelper.cs similarity index 96% rename from src/Infrastructure/BotSharp.Core/Routing/Planning/PlannerHelper.cs rename to src/Infrastructure/BotSharp.Core/Routing/Reasoning/ReasonerHelper.cs index 4500ca1d..7c8debf6 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Planning/PlannerHelper.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/ReasonerHelper.cs @@ -1,6 +1,6 @@ -namespace BotSharp.Core.Routing.Planning; +namespace BotSharp.Core.Routing.Reasoning; -public static class PlannerHelper +public static class ReasonerHelper { /// /// Sometimes LLM hallucinates and fails to set function names correctly. diff --git a/src/Infrastructure/BotSharp.Core/Routing/Planning/SequentialPlanner.cs b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/SequentialReasoner.cs similarity index 81% rename from src/Infrastructure/BotSharp.Core/Routing/Planning/SequentialPlanner.cs rename to src/Infrastructure/BotSharp.Core/Routing/Reasoning/SequentialReasoner.cs index f6b05375..959747db 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Planning/SequentialPlanner.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/SequentialReasoner.cs @@ -1,11 +1,30 @@ +/***************************************************************************** + Copyright 2024 Written by Haiping Chen. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +******************************************************************************/ + using BotSharp.Abstraction.MLTasks; using BotSharp.Abstraction.Routing.Models; -using BotSharp.Abstraction.Routing.Planning; +using BotSharp.Abstraction.Routing.Reasoning; using BotSharp.Abstraction.Templating; -namespace BotSharp.Core.Routing.Planning; +namespace BotSharp.Core.Routing.Reasoning; -public class SequentialPlanner : IRoutingPlaner +/// +/// Sequential tasks focused reasoning approach +/// +public class SequentialReasoner : IRoutingReasoner { private readonly IServiceProvider _services; private readonly ILogger _logger; @@ -14,7 +33,7 @@ public class SequentialPlanner : IRoutingPlaner public int MaxLoopCount => 100; private FunctionCallFromLlm _lastInst; - public SequentialPlanner(IServiceProvider services, ILogger logger) + public SequentialReasoner(IServiceProvider services, ILogger logger) { _services = services; _logger = logger; @@ -72,7 +91,7 @@ public class SequentialPlanner : IRoutingPlaner { new RoleDialogModel(AgentRole.User, next) { - FunctionName = nameof(SequentialPlanner), + FunctionName = nameof(SequentialReasoner), MessageId = messageId } }; @@ -139,7 +158,7 @@ public class SequentialPlanner : IRoutingPlaner if (message.StopCompletion) { - context.Empty(reason: $"Agent queue is cleared by {nameof(SequentialPlanner)}"); + context.Empty(reason: $"Agent queue is cleared by {nameof(SequentialReasoner)}"); return false; } @@ -154,7 +173,7 @@ public class SequentialPlanner : IRoutingPlaner private string GetNextStepPrompt(Agent router) { - var template = router.Templates.First(x => x.Name == "planner_prompt.sequential").Content; + var template = router.Templates.First(x => x.Name == "reasoner.sequential").Content; var render = _services.GetRequiredService(); return render.Render(template, new Dictionary @@ -169,11 +188,11 @@ public class SequentialPlanner : IRoutingPlaner var inst = new DecomposedStep(); var llmProviderService = _services.GetRequiredService(); - var model = llmProviderService.GetProviderModel("azure-openai", "gpt-4"); + var model = llmProviderService.GetProviderModel("openai", "gpt-4o"); // chat completion var completion = CompletionProvider.GetChatCompletion(_services, - provider: "azure-openai", + provider: "openai", model: model.Name); int retryCount = 0; @@ -185,7 +204,7 @@ public class SequentialPlanner : IRoutingPlaner var response = await completion.GetChatCompletions(new Agent { Id = router.Id, - Name = nameof(SequentialPlanner), + Name = nameof(SequentialReasoner), Instruction = systemPrompt }, dialogs); @@ -208,16 +227,11 @@ public class SequentialPlanner : IRoutingPlaner private string GetDecomposeTaskPrompt(Agent router) { - var template = router.Templates.First(x => x.Name == "planner_prompt.sequential.get_remaining_task").Content; + var template = router.Templates.First(x => x.Name == "reasoner.sequential.get_remaining_task").Content; var render = _services.GetRequiredService(); return render.Render(template, new Dictionary { }); } - - public Task GetNextInstruction(Agent router, string messageId) - { - throw new NotImplementedException(); - } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs index 5c1f1903..b0527605 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs @@ -41,7 +41,8 @@ public class RoutingContext : IRoutingContext var agentService = _services.GetRequiredService(); _routerAgentIds = agentService.GetAgents(new AgentFilter { - Type = AgentType.Routing + Type = AgentType.Routing, + Pager = new Pagination { Size = 100 } }).Result.Items.Select(x => x.Id).ToArray(); } @@ -49,6 +50,17 @@ public class RoutingContext : IRoutingContext } } + /// + /// Entry agent + /// + public string EntryAgentId + { + get + { + return _stack.LastOrDefault() ?? string.Empty; + } + } + public bool IsEmpty => !_stack.Any(); public string GetCurrentAgentId() @@ -231,12 +243,16 @@ public class RoutingContext : IRoutingContext public Stack GetAgentStack() { - return new Stack(_stack); + var copy = _stack.ToList(); + copy.Reverse(); + return new Stack(copy); } public void SetAgentStack(Stack stack) { - _stack = new Stack(stack); + var copy = stack.ToList(); + copy.Reverse(); + _stack = new Stack(copy); } public void ResetAgentStack() diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingPlugin.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingPlugin.cs index 7a751057..f5c2a9d6 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingPlugin.cs @@ -1,10 +1,8 @@ -using BotSharp.Abstraction.Routing; -using BotSharp.Abstraction.Routing.Models; -using BotSharp.Abstraction.Routing.Planning; +using BotSharp.Abstraction.Routing.Reasoning; using BotSharp.Abstraction.Routing.Settings; using BotSharp.Abstraction.Settings; using BotSharp.Core.Routing.Hooks; -using BotSharp.Core.Routing.Planning; +using BotSharp.Core.Routing.Reasoning; using Microsoft.Extensions.Configuration; namespace BotSharp.Core.Routing; @@ -35,8 +33,10 @@ public class RoutingPlugin : IBotSharpPlugin services.AddScoped(); services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + services.AddScoped(); } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetPlanner.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetPlanner.cs deleted file mode 100644 index 0fcaa5a1..00000000 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetPlanner.cs +++ /dev/null @@ -1,24 +0,0 @@ -using BotSharp.Abstraction.Routing.Enums; -using BotSharp.Abstraction.Routing.Planning; -using BotSharp.Core.Routing.Planning; - -namespace BotSharp.Core.Routing; - -public partial class RoutingService -{ - public IRoutingPlaner GetPlanner(Agent router) - { - var rule = router.RoutingRules.FirstOrDefault(x => x.Type == RuleType.Planner); - - var planner = _services.GetServices(). - FirstOrDefault(x => x.GetType().Name.EndsWith(rule.Field)); - - if (planner == null) - { - _logger.LogError($"Can't find specific planner named {rule.Field}"); - return _services.GetRequiredService(); - } - - return planner; - } -} diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.HasMissingRequiredField.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.HasMissingRequiredField.cs index 3b1f0566..d88b65ba 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.HasMissingRequiredField.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.HasMissingRequiredField.cs @@ -1,6 +1,5 @@ using BotSharp.Abstraction.Conversations.Enums; using BotSharp.Abstraction.Routing.Models; -using System.Drawing; namespace BotSharp.Core.Routing; diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InstructLoop.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InstructLoop.cs new file mode 100644 index 00000000..5b84122c --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InstructLoop.cs @@ -0,0 +1,112 @@ +using BotSharp.Abstraction.Infrastructures.Enums; +using BotSharp.Abstraction.Planning; +using BotSharp.Abstraction.Routing.Enums; +using BotSharp.Abstraction.Routing.Reasoning; +using BotSharp.Core.Routing.Reasoning; + +namespace BotSharp.Core.Routing; + +public partial class RoutingService +{ + public async Task InstructLoop(RoleDialogModel message, List dialogs) + { + RoleDialogModel response = default; + + var agentService = _services.GetRequiredService(); + var convService = _services.GetRequiredService(); + var storage = _services.GetRequiredService(); + + _router = await agentService.LoadAgent(message.CurrentAgentId); + + var states = _services.GetRequiredService(); + var executor = _services.GetRequiredService(); + + var planner = GetReasoner(_router); + + _context.Push(_router.Id); + + // Handle multi-language for input + var agentSettings = _services.GetRequiredService(); + if (agentSettings.EnableTranslator) + { + var translator = _services.GetRequiredService(); + + var language = states.GetState(StateConst.LANGUAGE, LanguageType.ENGLISH); + if (language != LanguageType.ENGLISH) + { + message.SecondaryContent = message.Content; + message.Content = await translator.Translate(_router, message.MessageId, message.Content, + language: LanguageType.ENGLISH, + clone: false); + } + } + + dialogs.Add(message); + storage.Append(convService.ConversationId, message); + + // Get first instruction + _router.TemplateDict["conversation"] = await GetConversationContent(dialogs); + var inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs); + + int loopCount = 1; + while (true) + { + await HookEmitter.Emit(_services, async hook => + await hook.OnRoutingInstructionReceived(inst, message) + ); + + // Save states + states.SaveStateByArgs(inst.Arguments); + +#if DEBUG + Console.WriteLine($"*** Next Instruction *** {inst}"); +#else + _logger.LogInformation($"*** Next Instruction *** {inst}"); +#endif + await planner.AgentExecuting(_router, inst, message, dialogs); + + // Handover to Task Agent + if (inst.HandleDialogsByPlanner) + { + var dialogWithoutContext = planner.BeforeHandleContext(inst, message, dialogs); + response = await executor.Execute(this, inst, message, dialogWithoutContext); + planner.AfterHandleContext(dialogs, dialogWithoutContext); + } + else + { + response = await executor.Execute(this, inst, message, dialogs); + } + + await planner.AgentExecuted(_router, inst, response, dialogs); + + if (loopCount >= planner.MaxLoopCount || _context.IsEmpty) + { + break; + } + + // Get next instruction from Planner + _router.TemplateDict["conversation"] = await GetConversationContent(dialogs); + inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs); + loopCount++; + } + + return response; + } + + public IRoutingReasoner GetReasoner(Agent router) + { + var rule = router.RoutingRules.FirstOrDefault(x => x.Type == RuleType.Reasoner); + + var reasoner = _services.GetServices(). + FirstOrDefault(x => x.GetType().Name.EndsWith(rule.Field)); + + if (reasoner == null) + { + _logger.LogError($"Can't find specific planner named {rule.Field}"); + // Default use NaiveReasoner + return _services.GetRequiredService(); + } + + return reasoner; + } +} diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs index 25ab3552..39c8ed23 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs @@ -4,13 +4,11 @@ namespace BotSharp.Core.Routing; public partial class RoutingService { - //private int _currentRecursionDepth = 0; public async Task InvokeAgent(string agentId, List dialogs) { var agentService = _services.GetRequiredService(); var agent = await agentService.LoadAgent(agentId); - //_currentRecursionDepth++; Context.IncreaseRecursiveCounter(); if (Context.CurrentRecursionDepth > agent.LlmConfig.MaxRecursionDepth) { diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index 770acaf9..d3eb2b1c 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -1,6 +1,4 @@ -using BotSharp.Abstraction.Infrastructures.Enums; using BotSharp.Abstraction.Routing.Models; -using BotSharp.Abstraction.Routing.Planning; using BotSharp.Abstraction.Routing.Settings; namespace BotSharp.Core.Routing; @@ -16,21 +14,6 @@ public partial class RoutingService : IRoutingService public IRoutingContext Context => _context; public Agent Router => _router; - //public int GetRecursiveCounter() - //{ - // return _currentRecursionDepth; - //} - - //public void SetRecursiveCounter(int counter) - //{ - // _currentRecursionDepth = counter; - //} - - //public void ResetRecursiveCounter() - //{ - // _currentRecursionDepth = 0; - //} - public RoutingService( IServiceProvider services, RoutingSettings settings, @@ -75,97 +58,12 @@ public partial class RoutingService : IRoutingService return response; } - public async Task InstructLoop(RoleDialogModel message, List dialogs) - { - RoleDialogModel response = default; - - var agentService = _services.GetRequiredService(); - var convService = _services.GetRequiredService(); - var storage = _services.GetRequiredService(); - - _router = await agentService.LoadAgent(message.CurrentAgentId); - - var states = _services.GetRequiredService(); - var executor = _services.GetRequiredService(); - - var planner = GetPlanner(_router); - - _context.Push(_router.Id); - - // Handle multi-language for input - var agentSettings = _services.GetRequiredService(); - if (agentSettings.EnableTranslator) - { - var translator = _services.GetRequiredService(); - - var language = states.GetState(StateConst.LANGUAGE, LanguageType.ENGLISH); - if (language != LanguageType.ENGLISH) - { - message.SecondaryContent = message.Content; - message.Content = await translator.Translate(_router, message.MessageId, message.Content, - language: LanguageType.ENGLISH, - clone: false); - } - } - - dialogs.Add(message); - storage.Append(convService.ConversationId, message); - - // Get first instruction - _router.TemplateDict["conversation"] = await GetConversationContent(dialogs); - var inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs); - - int loopCount = 1; - while (true) - { - await HookEmitter.Emit(_services, async hook => - await hook.OnRoutingInstructionReceived(inst, message) - ); - - // Save states - states.SaveStateByArgs(inst.Arguments); - -#if DEBUG - Console.WriteLine($"*** Next Instruction *** {inst}"); -#else - _logger.LogInformation($"*** Next Instruction *** {inst}"); -#endif - await planner.AgentExecuting(_router, inst, message, dialogs); - - // Handover to Task Agent - if (inst.HandleDialogsByPlanner) - { - var dialogWithoutContext = planner.BeforeHandleContext(inst, message, dialogs); - response = await executor.Execute(this, inst, message, dialogWithoutContext); - planner.AfterHandleContext(dialogs, dialogWithoutContext); - } - else - { - response = await executor.Execute(this, inst, message, dialogs); - } - - await planner.AgentExecuted(_router, inst, response, dialogs); - - if (loopCount >= planner.MaxLoopCount || _context.IsEmpty) - { - break; - } - - // Get next instruction from Planner - _router.TemplateDict["conversation"] = await GetConversationContent(dialogs); - inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs); - loopCount++; - } - - return response; - } - public List GetHandlers(Agent router) { - var planer = GetPlanner(router); + var reasoner = GetReasoner(router); return _services.GetServices() - .Where(x => x.Planers == null || x.Planers.Contains(planer.GetType().Name)) + .Where(x => x.Planers == null || x.Planers.Contains(reasoner.GetType().Name)) .Where(x => !string.IsNullOrEmpty(x.Description)) .Select((x, i) => new RoutingHandlerDef { diff --git a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs index 33b27177..c16a8c4b 100644 --- a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs +++ b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs @@ -1,10 +1,10 @@ -using BotSharp.Abstraction.Agents.Models; -using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Models; using BotSharp.Abstraction.Routing.Models; using BotSharp.Abstraction.Templating; using BotSharp.Abstraction.Translation.Models; using Fluid; +using System.Collections; +using System.Reflection; namespace BotSharp.Core.Templating; @@ -48,4 +48,47 @@ public class TemplateRender : ITemplateRender return template; } } + + + public void Register(Type type) + { + if (type == null || IsStringType(type)) return; + + if (IsListType(type)) + { + if (type.IsGenericType) + { + var genericType = type.GetGenericArguments()[0]; + Register(genericType); + } + } + else if (IsTrackToNextLevel(type)) + { + _options.MemberAccessStrategy.Register(type); + var props = type.GetProperties(); + foreach (var prop in props) + { + Register(prop.PropertyType); + } + } + } + + + #region Private methods + private static bool IsStringType(Type type) + { + return type == typeof(string); + } + + private static bool IsListType(Type type) + { + var interfaces = type.GetTypeInfo().ImplementedInterfaces; + return type.IsArray || interfaces.Any(x => x.Name == typeof(IEnumerable).Name); + } + + private static bool IsTrackToNextLevel(Type type) + { + return type.IsClass || type.IsInterface || type.IsAbstract; + } + #endregion } diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index 1938302f..0cddc5b7 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -736,4 +736,42 @@ public class UserService : IUserService } return true; } + + public async Task AddDashboardConversation(string userId, string conversationId) + { + var db = _services.GetRequiredService(); + db.AddDashboardConversation(userId, conversationId); + + await Task.CompletedTask; + return true; + } + + public async Task RemoveDashboardConversation(string userId, string conversationId) + { + var db = _services.GetRequiredService(); + db.RemoveDashboardConversation(userId, conversationId); + + await Task.CompletedTask; + return true; + } + + public async Task UpdateDashboardConversation(string userId, DashboardConversation newDashConv) + { + var db = _services.GetRequiredService(); + var dashConv = db.GetDashboard(userId)?.ConversationList.FirstOrDefault(x => string.Equals(x.ConversationId, newDashConv.ConversationId)); + if (dashConv == null) return; + dashConv.Name = newDashConv.Name ?? dashConv.Name; + dashConv.Instruction = newDashConv.Instruction ?? dashConv.Instruction; + db.UpdateDashboardConversation(userId, dashConv); + await Task.CompletedTask; + return; + } + + public async Task GetDashboard(string userId) + { + var db = _services.GetRequiredService(); + var dash = db.GetDashboard(); + await Task.CompletedTask; + return dash; + } } 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 3159bb08..992e04f3 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 @@ -11,8 +11,8 @@ "profiles": [ "tool" ], "routingRules": [ { - "type": "planner", - "field": "HFPlanner" + "type": "reasoner", + "field": "HFReasoner" } ] } \ No newline at end of file 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/reasoner.hf.liquid similarity index 100% rename from src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.hf.liquid rename to src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/reasoner.hf.liquid 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/reasoner.naive.liquid similarity index 100% rename from src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.naive.liquid rename to src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/reasoner.naive.liquid diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/reasoner.one-step-forward.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/reasoner.one-step-forward.liquid new file mode 100644 index 00000000..dd6fe616 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/reasoner.one-step-forward.liquid @@ -0,0 +1,2 @@ +Analyze the user's problem. Which prerequisite task needs to be completed? Output the next step of routing instructions. +Check the job responsibilities of the routable Agent and do not transfer to an Agent that exceeds the scope of responsibility. \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.sequential.get_remaining_task.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/reasoner.sequential.get_remaining_task.liquid similarity index 100% rename from src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.sequential.get_remaining_task.liquid rename to src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/reasoner.sequential.get_remaining_task.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/reasoner.sequential.liquid similarity index 100% rename from src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.sequential.liquid rename to src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/reasoner.sequential.liquid diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs index 5ce68c2a..d8b701c9 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs @@ -1,4 +1,4 @@ -using BotSharp.Abstraction.Users.Enums; +using BotSharp.Abstraction.Agents.Models; namespace BotSharp.OpenAPI.Controllers; @@ -59,11 +59,7 @@ public class AgentController : ControllerBase var userService = _services.GetRequiredService(); var auth = await userService.GetUserAuthorizations(new List { targetAgent.Id }); - - targetAgent.Editable = auth.IsAgentActionAllowed(targetAgent.Id, UserAction.Edit); - targetAgent.Chatable = auth.IsAgentActionAllowed(targetAgent.Id, UserAction.Chat); - targetAgent.Trainable = auth.IsAgentActionAllowed(targetAgent.Id, UserAction.Train); - targetAgent.Evaluable = auth.IsAgentActionAllowed(targetAgent.Id, UserAction.Evaluate); + targetAgent.Actions = auth.GetAllowedAgentActions(targetAgent.Id); return targetAgent; } @@ -90,10 +86,7 @@ public class AgentController : ControllerBase agents = pagedAgents?.Items?.Select(x => { var model = AgentViewModel.FromAgent(x); - model.Editable = auth.IsAgentActionAllowed(x.Id, UserAction.Edit); - model.Chatable = auth.IsAgentActionAllowed(x.Id, UserAction.Chat); - model.Trainable = auth.IsAgentActionAllowed(x.Id, UserAction.Train); - model.Evaluable = auth.IsAgentActionAllowed(x.Id, UserAction.Evaluate); + model.Actions = auth.GetAllowedAgentActions(x.Id); return model; })?.ToList() ?? []; @@ -153,9 +146,9 @@ public class AgentController : ControllerBase return await _agentService.DeleteAgent(agentId); } - [HttpGet("/agent/utilities")] - public IEnumerable GetAgentUtilities() + [HttpGet("/agent/utility/options")] + public IEnumerable GetAgentUtilityOptions() { - return _agentService.GetAgentUtilities(); + return _agentService.GetAgentUtilityOptions(); } } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index 2da3251b..389bf96f 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -511,6 +511,28 @@ public class ConversationController : ControllerBase } #endregion + #region miscellaneous + [HttpPut("/agent/{agentId}/conversation/{conversationId}/dashboard")] + public async Task PinConversationToDashboard([FromRoute] string agentId, [FromRoute] string conversationId) + { + var userService = _services.GetRequiredService(); + + var user = await userService.GetUser(_user.Id); + var pinned = await userService.AddDashboardConversation(user.Id, conversationId); + return pinned; + } + + [HttpDelete("/agent/{agentId}/conversation/{conversationId}/dashboard")] + public async Task UnpinConversationFromDashboard([FromRoute] string agentId, [FromRoute] string conversationId) + { + var userService = _services.GetRequiredService(); + + var user = await userService.GetUser(_user.Id); + var unpinned = await userService.RemoveDashboardConversation(user.Id, conversationId); + return unpinned; + } + #endregion + #region Private methods private void SetStates(IConversationService conv, NewMessageModel input) { diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/DashboardController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/DashboardController.cs new file mode 100644 index 00000000..f75467fe --- /dev/null +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/DashboardController.cs @@ -0,0 +1,73 @@ +using BotSharp.Abstraction.Options; +using BotSharp.Abstraction.Users.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BotSharp.OpenAPI.Controllers; + +[Authorize] +[ApiController] +public class DashboardController : ControllerBase +{ + private readonly IServiceProvider _services; + private readonly IUserIdentity _user; + + public DashboardController(IServiceProvider services, + IUserIdentity user, + BotSharpOptions options) + { + _services = services; + _user = user; + + } + #region User Components + [HttpGet("/dashboard/components")] + public async Task GetComponents(string userId) + { + var userService = _services.GetRequiredService(); + var dashboardProfile = await userService.GetDashboard(userId); + if (dashboardProfile == null) return new UserDashboardModel(); + var result = new UserDashboardModel + { + ConversationList = dashboardProfile.ConversationList.Select( + x => new UserDashboardConversationModel + { + Name = x.Name, + ConversationId = x.ConversationId, + Instruction = x.Instruction + } + ).ToList() + }; + return result; + } + + [HttpPost("/dashboard/component/conversation")] + public async Task UpdateDashboardConversationInstruction(string userId, UserDashboardConversationModel dashConv) + { + if (string.IsNullOrEmpty(dashConv.Name) && string.IsNullOrEmpty(dashConv.Instruction)) + { + return; + } + var newDashConv = new DashboardConversation + { + Id = Guid.Empty.ToString(), + ConversationId = dashConv.ConversationId + }; + if (!string.IsNullOrEmpty(dashConv.Name)) + { + newDashConv.Name = dashConv.Name; + } + if (!string.IsNullOrEmpty(dashConv.Instruction)) + { + newDashConv.Instruction = dashConv.Instruction; + } + + var userService = _services.GetRequiredService(); + await userService.UpdateDashboardConversation(userId, newDashConv); + return; + } + #endregion +} diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs index 78f75833..32b3fdce 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs @@ -48,7 +48,10 @@ public class AgentCreationModel /// Combine different Agents together to form a Profile. /// public List Profiles { get; set; } = new(); - public List Utilities { get; set; } = new(); + + public bool MergeUtility { get; set; } + + public List Utilities { get; set; } = new(); public List RoutingRules { get; set; } = new(); public AgentLlmConfig? LlmConfig { get; set; } @@ -68,6 +71,7 @@ public class AgentCreationModel IsPublic = IsPublic, Type = Type, Disabled = Disabled, + MergeUtility = MergeUtility, Profiles = Profiles, RoutingRules = RoutingRules?.Select(x => RoutingRuleUpdateModel.ToDomainElement(x))?.ToList() ?? new List(), LlmConfig = LlmConfig diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs index d64530c6..30308c9f 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs @@ -31,10 +31,13 @@ public class AgentUpdateModel /// public List? Samples { get; set; } + [JsonPropertyName("merge_utility")] + public bool MergeUtility { get; set; } + /// /// Utilities /// - public List? Utilities { get; set; } + public List? Utilities { get; set; } /// /// Functions @@ -73,6 +76,7 @@ public class AgentUpdateModel Description = Description ?? string.Empty, IsPublic = IsPublic, Disabled = Disabled, + MergeUtility = MergeUtility, Type = Type, Profiles = Profiles ?? new List(), RoutingRules = RoutingRules?.Select(x => RoutingRuleUpdateModel.ToDomainElement(x))?.ToList() ?? new List(), @@ -81,7 +85,7 @@ public class AgentUpdateModel Templates = Templates ?? new List(), Functions = Functions ?? new List(), Responses = Responses ?? new List(), - Utilities = Utilities ?? new List(), + Utilities = Utilities ?? new List(), LlmConfig = LlmConfig }; diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs index ca814177..34b1bcc1 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs @@ -20,7 +20,10 @@ public class AgentViewModel public List Functions { get; set; } public List Responses { get; set; } public List Samples { get; set; } - public List Utilities { get; set; } + + [JsonPropertyName("merge_utility")] + public bool MergeUtility { get; set; } + public List Utilities { get; set; } [JsonPropertyName("is_public")] public bool IsPublic { get; set; } @@ -33,8 +36,7 @@ public class AgentViewModel [JsonPropertyName("icon_url")] public string IconUrl { get; set; } - public List Profiles { get; set; } - = new List(); + public List Profiles { get; set; } = new(); [JsonPropertyName("routing_rules")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] @@ -46,10 +48,7 @@ public class AgentViewModel public PluginDef Plugin { get; set; } - public bool Editable { get; set; } - public bool Chatable { get; set; } - public bool Trainable { get; set; } - public bool Evaluable { get; set; } + public IEnumerable? Actions { get; set; } [JsonPropertyName("created_datetime")] public DateTime CreatedDateTime { get; set; } @@ -74,6 +73,7 @@ public class AgentViewModel Utilities = agent.Utilities, IsPublic= agent.IsPublic, Disabled = agent.Disabled, + MergeUtility = agent.MergeUtility, IconUrl = agent.IconUrl, Profiles = agent.Profiles ?? new List(), RoutingRules = agent.RoutingRules, diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationViewModel.cs index 05f8cb89..90b4ff67 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationViewModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationViewModel.cs @@ -12,6 +12,7 @@ public class ConversationViewModel [JsonPropertyName("agent_name")] public string AgentName { get; set; } + [JsonPropertyName("title")] public string Title { get; set; } = string.Empty; public UserViewModel User { get; set; } = new UserViewModel(); diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserDashboardConversationModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserDashboardConversationModel.cs new file mode 100644 index 00000000..3a5f3491 --- /dev/null +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserDashboardConversationModel.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace BotSharp.OpenAPI.ViewModels.Users; +public class UserDashboardModel +{ + + [JsonPropertyName("conversation_list")] + public IList ConversationList { get; set; } = []; +} + +public class UserDashboardConversationModel +{ + [JsonPropertyName("name")] + public string? Name { get; set; } + + [JsonPropertyName("conversation_id")] + public string? ConversationId { get; set; } + + [JsonPropertyName("instruction")] + public string? Instruction { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.AudioHandler/AudioHandlerPlugin.cs b/src/Plugins/BotSharp.Plugin.AudioHandler/AudioHandlerPlugin.cs index 2c289907..d79ca6c2 100644 --- a/src/Plugins/BotSharp.Plugin.AudioHandler/AudioHandlerPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.AudioHandler/AudioHandlerPlugin.cs @@ -17,7 +17,6 @@ public class AudioHandlerPlugin : IBotSharpPlugin }); services.AddScoped(); - services.AddScoped(); services.AddScoped(); } } diff --git a/src/Plugins/BotSharp.Plugin.AudioHandler/Hooks/AudioHandlerHook.cs b/src/Plugins/BotSharp.Plugin.AudioHandler/Hooks/AudioHandlerHook.cs deleted file mode 100644 index 80acb149..00000000 --- a/src/Plugins/BotSharp.Plugin.AudioHandler/Hooks/AudioHandlerHook.cs +++ /dev/null @@ -1,60 +0,0 @@ -using BotSharp.Abstraction.Agents.Settings; -using BotSharp.Abstraction.Functions.Models; - -namespace BotSharp.Plugin.AudioHandler.Hooks; - -public class AudioHandlerHook : AgentHookBase, IAgentHook -{ - private const string HANDLER_AUDIO = "handle_audio_request"; - - public override string SelfId => string.Empty; - - public AudioHandlerHook(IServiceProvider services, AgentSettings settings) : base(services, settings) - { - } - - public override void OnAgentLoaded(Agent agent) - { - var conv = _services.GetRequiredService(); - var isConvMode = conv.IsConversationMode(); - var isEnabled = !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(UtilityName.AudioHandler); - - if (isEnabled && isConvMode) - { - AddUtility(agent, HANDLER_AUDIO); - } - - base.OnAgentLoaded(agent); - } - - private void AddUtility(Agent agent, string functionName) - { - var (prompt, fn) = GetPromptAndFunction(functionName); - - if (fn != null) - { - if (!string.IsNullOrWhiteSpace(prompt)) - { - agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n"; - } - - if (agent.Functions == null) - { - agent.Functions = new List { fn }; - } - else - { - agent.Functions.Add(fn); - } - } - } - - private (string, FunctionDef?) GetPromptAndFunction(string functionName) - { - var db = _services.GetRequiredService(); - var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant); - var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{functionName}.fn"))?.Content ?? string.Empty; - var loadAttachmentFn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(functionName)); - return (prompt, loadAttachmentFn); - } -} diff --git a/src/Plugins/BotSharp.Plugin.AudioHandler/Hooks/AudioHandlerUtilityHook.cs b/src/Plugins/BotSharp.Plugin.AudioHandler/Hooks/AudioHandlerUtilityHook.cs index ac3f0ed7..3d1f3992 100644 --- a/src/Plugins/BotSharp.Plugin.AudioHandler/Hooks/AudioHandlerUtilityHook.cs +++ b/src/Plugins/BotSharp.Plugin.AudioHandler/Hooks/AudioHandlerUtilityHook.cs @@ -2,8 +2,17 @@ namespace BotSharp.Plugin.AudioHandler.Hooks; public class AudioHandlerUtilityHook : IAgentUtilityHook { - public void AddUtilities(List utilities) + private const string HANDLER_AUDIO = "handle_audio_request"; + + public void AddUtilities(List utilities) { - utilities.Add(UtilityName.AudioHandler); + var utility = new AgentUtility + { + Name = UtilityName.AudioHandler, + Functions = [new(HANDLER_AUDIO)], + Templates = [new($"{HANDLER_AUDIO}.fn")] + }; + + utilities.Add(utility); } } \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.CodeDriver/data/agents/c0ded7d9-3f9d-4ef6-b7ce-56a892dcef62/agent.json b/src/Plugins/BotSharp.Plugin.CodeDriver/data/agents/c0ded7d9-3f9d-4ef6-b7ce-56a892dcef62/agent.json index 4ed8765c..038c969e 100644 --- a/src/Plugins/BotSharp.Plugin.CodeDriver/data/agents/c0ded7d9-3f9d-4ef6-b7ce-56a892dcef62/agent.json +++ b/src/Plugins/BotSharp.Plugin.CodeDriver/data/agents/c0ded7d9-3f9d-4ef6-b7ce-56a892dcef62/agent.json @@ -8,7 +8,7 @@ "updatedDateTime": "2024-11-23T00:00:00Z", "disabled": false, "isPublic": true, - "profiles": [ "database" ], + "profiles": [ "coding" ], "llmConfig": { "provider": "openai", "model": "gpt-4o", diff --git a/src/Plugins/BotSharp.Plugin.EmailHandler/EmailHandlerPlugin.cs b/src/Plugins/BotSharp.Plugin.EmailHandler/EmailHandlerPlugin.cs index 5d7ff586..fffc8c31 100644 --- a/src/Plugins/BotSharp.Plugin.EmailHandler/EmailHandlerPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.EmailHandler/EmailHandlerPlugin.cs @@ -20,8 +20,6 @@ namespace BotSharp.Plugin.EmailHandler return settingService.Bind("EmailSender"); }); - services.AddScoped(); - services.AddScoped(); services.AddScoped(); var emailReaderSettings = new EmailReaderSettings(); diff --git a/src/Plugins/BotSharp.Plugin.EmailHandler/Hooks/EmailHandlerUtilityHook.cs b/src/Plugins/BotSharp.Plugin.EmailHandler/Hooks/EmailHandlerUtilityHook.cs index 1d801e66..a909932e 100644 --- a/src/Plugins/BotSharp.Plugin.EmailHandler/Hooks/EmailHandlerUtilityHook.cs +++ b/src/Plugins/BotSharp.Plugin.EmailHandler/Hooks/EmailHandlerUtilityHook.cs @@ -1,18 +1,22 @@ using BotSharp.Abstraction.Agents; using BotSharp.Plugin.EmailHandler.Enums; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace BotSharp.Plugin.EmailHandler.Hooks +namespace BotSharp.Plugin.EmailHandler.Hooks; + +public class EmailHandlerUtilityHook : IAgentUtilityHook { - public class EmailHandlerUtilityHook : IAgentUtilityHook + private static string EMAIL_READER_FN = "handle_email_reader"; + private static string EMAIL_SENDER_FN = "handle_email_sender"; + + public void AddUtilities(List utilities) { - public void AddUtilities(List utilities) + var utility = new AgentUtility { - utilities.Add(UtilityName.EmailHandler); - } + Name = UtilityName.EmailHandler, + Functions = [new(EMAIL_READER_FN), new(EMAIL_SENDER_FN)], + Templates = [new($"{EMAIL_READER_FN}.fn"), new($"{EMAIL_SENDER_FN}.fn")] + }; + + utilities.Add(utility); } } diff --git a/src/Plugins/BotSharp.Plugin.EmailHandler/Hooks/EmailReaderHook.cs b/src/Plugins/BotSharp.Plugin.EmailHandler/Hooks/EmailReaderHook.cs deleted file mode 100644 index beece9e2..00000000 --- a/src/Plugins/BotSharp.Plugin.EmailHandler/Hooks/EmailReaderHook.cs +++ /dev/null @@ -1,56 +0,0 @@ -using BotSharp.Abstraction.Agents; -using BotSharp.Abstraction.Agents.Settings; -using BotSharp.Abstraction.Functions.Models; -using BotSharp.Plugin.EmailHandler.Enums; - -namespace BotSharp.Plugin.EmailHandler.Hooks; - -public class EmailReaderHook : AgentHookBase -{ - private static string FUNCTION_NAME = "handle_email_reader"; - - public override string SelfId => string.Empty; - - public EmailReaderHook(IServiceProvider services, AgentSettings settings) - : base(services, settings) - { - } - public override void OnAgentLoaded(Agent agent) - { - var conv = _services.GetRequiredService(); - var isConvMode = conv.IsConversationMode(); - var isEnabled = !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(UtilityName.EmailHandler); - - if (isConvMode && isEnabled) - { - var (prompt, fn) = GetPromptAndFunction(); - if (fn != null) - { - if (!string.IsNullOrWhiteSpace(prompt)) - { - agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n"; - } - - if (agent.Functions == null) - { - agent.Functions = new List { fn }; - } - else - { - agent.Functions.Add(fn); - } - } - } - - base.OnAgentLoaded(agent); - } - - private (string, FunctionDef?) GetPromptAndFunction() - { - var db = _services.GetRequiredService(); - var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant); - var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{FUNCTION_NAME}.fn"))?.Content ?? string.Empty; - var loadAttachmentFn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(FUNCTION_NAME)); - return (prompt, loadAttachmentFn); - } -} diff --git a/src/Plugins/BotSharp.Plugin.EmailHandler/Hooks/EmailSenderHook.cs b/src/Plugins/BotSharp.Plugin.EmailHandler/Hooks/EmailSenderHook.cs deleted file mode 100644 index 27491015..00000000 --- a/src/Plugins/BotSharp.Plugin.EmailHandler/Hooks/EmailSenderHook.cs +++ /dev/null @@ -1,56 +0,0 @@ -using BotSharp.Abstraction.Agents; -using BotSharp.Abstraction.Agents.Settings; -using BotSharp.Abstraction.Functions.Models; -using BotSharp.Plugin.EmailHandler.Enums; - -namespace BotSharp.Plugin.EmailHandler.Hooks; - -public class EmailSenderHook : AgentHookBase -{ - private static string FUNCTION_NAME = "handle_email_sender"; - - public override string SelfId => string.Empty; - - public EmailSenderHook(IServiceProvider services, AgentSettings settings) - : base(services, settings) - { - } - public override void OnAgentLoaded(Agent agent) - { - var conv = _services.GetRequiredService(); - var isConvMode = conv.IsConversationMode(); - var isEnabled = !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(UtilityName.EmailHandler); - - if (isConvMode && isEnabled) - { - var (prompt, fn) = GetPromptAndFunction(); - if (fn != null) - { - if (!string.IsNullOrWhiteSpace(prompt)) - { - agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n"; - } - - if (agent.Functions == null) - { - agent.Functions = new List { fn }; - } - else - { - agent.Functions.Add(fn); - } - } - } - - base.OnAgentLoaded(agent); - } - - private (string, FunctionDef?) GetPromptAndFunction() - { - var db = _services.GetRequiredService(); - var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant); - var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{FUNCTION_NAME}.fn"))?.Content ?? string.Empty; - var loadAttachmentFn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(FUNCTION_NAME)); - return (prompt, loadAttachmentFn); - } -} diff --git a/src/Plugins/BotSharp.Plugin.ExcelHandler/ExcelHandlerPlugin.cs b/src/Plugins/BotSharp.Plugin.ExcelHandler/ExcelHandlerPlugin.cs index 0111ea99..08d652f0 100644 --- a/src/Plugins/BotSharp.Plugin.ExcelHandler/ExcelHandlerPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.ExcelHandler/ExcelHandlerPlugin.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using BotSharp.Abstraction.Plugins; using BotSharp.Abstraction.Settings; using BotSharp.Plugin.ExcelHandler.Helpers.MySql; @@ -30,7 +25,6 @@ public class ExcelHandlerPlugin : IBotSharpPlugin }); services.AddScoped(); - services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/src/Plugins/BotSharp.Plugin.ExcelHandler/Helpers/MySql/MySqlDbHelpers.cs b/src/Plugins/BotSharp.Plugin.ExcelHandler/Helpers/MySql/MySqlDbHelpers.cs index 4b86f89f..8b4c0940 100644 --- a/src/Plugins/BotSharp.Plugin.ExcelHandler/Helpers/MySql/MySqlDbHelpers.cs +++ b/src/Plugins/BotSharp.Plugin.ExcelHandler/Helpers/MySql/MySqlDbHelpers.cs @@ -1,12 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; -using BotSharp.Plugin.SqlHero.Settings; -using Microsoft.Data.Sqlite; +using BotSharp.Plugin.SqlDriver.Settings; using MySql.Data.MySqlClient; namespace BotSharp.Plugin.ExcelHandler.Helpers.MySql diff --git a/src/Plugins/BotSharp.Plugin.ExcelHandler/Helpers/Sqlite/SqliteDbHelpers.cs b/src/Plugins/BotSharp.Plugin.ExcelHandler/Helpers/Sqlite/SqliteDbHelpers.cs index c55e1642..464fa467 100644 --- a/src/Plugins/BotSharp.Plugin.ExcelHandler/Helpers/Sqlite/SqliteDbHelpers.cs +++ b/src/Plugins/BotSharp.Plugin.ExcelHandler/Helpers/Sqlite/SqliteDbHelpers.cs @@ -1,11 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using BotSharp.Plugin.SqlDriver.Settings; using Microsoft.Data.Sqlite; -using BotSharp.Plugin.SqlDriver.Models; -using BotSharp.Plugin.SqlHero.Settings; namespace BotSharp.Plugin.ExcelHandler.Helpers.Sqlite; diff --git a/src/Plugins/BotSharp.Plugin.ExcelHandler/Hooks/ExcelHandlerHook.cs b/src/Plugins/BotSharp.Plugin.ExcelHandler/Hooks/ExcelHandlerHook.cs deleted file mode 100644 index 8716e4b1..00000000 --- a/src/Plugins/BotSharp.Plugin.ExcelHandler/Hooks/ExcelHandlerHook.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace BotSharp.Plugin.ExcelHandler.Hooks; - -public class ExcelHandlerHook : AgentHookBase, IAgentHook -{ - private const string HANDLER_EXCEL = "handle_excel_request"; - - public override string SelfId => string.Empty; - - public ExcelHandlerHook(IServiceProvider services, AgentSettings settings) : base(services, settings) - { - } - - public override void OnAgentLoaded(Agent agent) - { - var conv = _services.GetRequiredService(); - var isConvMode = conv.IsConversationMode(); - var isEnabled = !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(UtilityName.ExcelHandler); - - if (isEnabled && isConvMode) - { - AddUtility(agent, HANDLER_EXCEL); - } - - base.OnAgentLoaded(agent); - } - - private void AddUtility(Agent agent, string functionName) - { - var (prompt, fn) = GetPromptAndFunction(functionName); - - if (fn != null) - { - if (!string.IsNullOrWhiteSpace(prompt)) - { - agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n"; - } - - if (agent.Functions == null) - { - agent.Functions = new List { fn }; - } - else - { - agent.Functions.Add(fn); - } - } - } - - private (string, FunctionDef?) GetPromptAndFunction(string functionName) - { - var db = _services.GetRequiredService(); - var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant); - var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{functionName}.fn"))?.Content ?? string.Empty; - var loadAttachmentFn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(functionName)); - return (prompt, loadAttachmentFn); - } -} - diff --git a/src/Plugins/BotSharp.Plugin.ExcelHandler/Hooks/ExcelHandlerUtilityHook.cs b/src/Plugins/BotSharp.Plugin.ExcelHandler/Hooks/ExcelHandlerUtilityHook.cs index fde7533f..15c41642 100644 --- a/src/Plugins/BotSharp.Plugin.ExcelHandler/Hooks/ExcelHandlerUtilityHook.cs +++ b/src/Plugins/BotSharp.Plugin.ExcelHandler/Hooks/ExcelHandlerUtilityHook.cs @@ -2,8 +2,17 @@ namespace BotSharp.Plugin.ExcelHandler.Hooks; public class ExcelHandlerUtilityHook : IAgentUtilityHook { - public void AddUtilities(List utilities) + private const string HANDLER_EXCEL = "handle_excel_request"; + + public void AddUtilities(List utilities) { - utilities.Add(UtilityName.ExcelHandler); + var utility = new AgentUtility + { + Name = UtilityName.ExcelHandler, + Functions = [new(HANDLER_EXCEL)], + Templates = [new($"{HANDLER_EXCEL}.fn")] + }; + + utilities.Add(utility); } } \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.FileHandler/FileHandlerPlugin.cs b/src/Plugins/BotSharp.Plugin.FileHandler/FileHandlerPlugin.cs index caef8304..a8ecac6d 100644 --- a/src/Plugins/BotSharp.Plugin.FileHandler/FileHandlerPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.FileHandler/FileHandlerPlugin.cs @@ -19,7 +19,6 @@ public class FileHandlerPlugin : IBotSharpPlugin return settingService.Bind("FileHandler"); }); - services.AddScoped(); services.AddScoped(); } diff --git a/src/Plugins/BotSharp.Plugin.FileHandler/Hooks/FileHandlerHook.cs b/src/Plugins/BotSharp.Plugin.FileHandler/Hooks/FileHandlerHook.cs deleted file mode 100644 index e9375244..00000000 --- a/src/Plugins/BotSharp.Plugin.FileHandler/Hooks/FileHandlerHook.cs +++ /dev/null @@ -1,69 +0,0 @@ -namespace BotSharp.Plugin.FileHandler.Hooks; - -public class FileHandlerHook : AgentHookBase, IAgentHook -{ - private const string READ_IMAGE_FN = "read_image"; - private const string READ_PDF_FN = "read_pdf"; - private const string GENERATE_IMAGE_FN = "generate_image"; - private const string EDIT_IMAGE_FN = "edit_image"; - - public override string SelfId => string.Empty; - - public FileHandlerHook(IServiceProvider services, AgentSettings settings) : base(services, settings) - { - } - - public override void OnAgentLoaded(Agent agent) - { - var conv = _services.GetRequiredService(); - var isConvMode = conv.IsConversationMode(); - - if (isConvMode) - { - AddUtility(agent, UtilityName.ImageGenerator, GENERATE_IMAGE_FN); - AddUtility(agent, UtilityName.ImageReader, READ_IMAGE_FN); - AddUtility(agent, UtilityName.ImageEditor, EDIT_IMAGE_FN); - AddUtility(agent, UtilityName.PdfReader, READ_PDF_FN); - - } - - base.OnAgentLoaded(agent); - } - - private void AddUtility(Agent agent, string utility, string functionName) - { - if (!IsEnableUtility(agent, utility)) return; - - var (prompt, fn) = GetPromptAndFunction(functionName); - if (fn != null) - { - if (!string.IsNullOrWhiteSpace(prompt)) - { - agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n"; - } - - if (agent.Functions == null) - { - agent.Functions = new List { fn }; - } - else - { - agent.Functions.Add(fn); - } - } - } - - private bool IsEnableUtility(Agent agent, string utility) - { - return !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(utility); - } - - private (string, FunctionDef?) GetPromptAndFunction(string functionName) - { - var db = _services.GetRequiredService(); - var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant); - var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{functionName}.fn"))?.Content ?? string.Empty; - var loadAttachmentFn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(functionName)); - return (prompt, loadAttachmentFn); - } -} diff --git a/src/Plugins/BotSharp.Plugin.FileHandler/Hooks/FileHandlerUtilityHook.cs b/src/Plugins/BotSharp.Plugin.FileHandler/Hooks/FileHandlerUtilityHook.cs index ce28634f..ab295063 100644 --- a/src/Plugins/BotSharp.Plugin.FileHandler/Hooks/FileHandlerUtilityHook.cs +++ b/src/Plugins/BotSharp.Plugin.FileHandler/Hooks/FileHandlerUtilityHook.cs @@ -2,11 +2,41 @@ namespace BotSharp.Plugin.FileHandler.Hooks; public class FileHandlerUtilityHook : IAgentUtilityHook { - public void AddUtilities(List utilities) + private const string READ_IMAGE_FN = "read_image"; + private const string READ_PDF_FN = "read_pdf"; + private const string GENERATE_IMAGE_FN = "generate_image"; + private const string EDIT_IMAGE_FN = "edit_image"; + + public void AddUtilities(List utilities) { - utilities.Add(UtilityName.ImageGenerator); - utilities.Add(UtilityName.ImageReader); - utilities.Add(UtilityName.ImageEditor); - utilities.Add(UtilityName.PdfReader); + var items = new List + { + new AgentUtility + { + Name = UtilityName.ImageGenerator, + Functions = [new(GENERATE_IMAGE_FN)], + Templates = [new($"{GENERATE_IMAGE_FN}.fn")] + }, + new AgentUtility + { + Name = UtilityName.ImageReader, + Functions = [new(READ_IMAGE_FN)], + Templates = [new($"{READ_IMAGE_FN}.fn")] + }, + new AgentUtility + { + Name = UtilityName.ImageEditor, + Functions = [new(EDIT_IMAGE_FN)], + Templates = [new($"{EDIT_IMAGE_FN}.fn")] + }, + new AgentUtility + { + Name = UtilityName.PdfReader, + Functions = [new(READ_PDF_FN)], + Templates = [new($"{READ_PDF_FN}.fn")] + } + }; + + utilities.AddRange(items); } } diff --git a/src/Plugins/BotSharp.Plugin.HttpHandler/Hooks/HttpHandlerHook.cs b/src/Plugins/BotSharp.Plugin.HttpHandler/Hooks/HttpHandlerHook.cs deleted file mode 100644 index 78ee2c09..00000000 --- a/src/Plugins/BotSharp.Plugin.HttpHandler/Hooks/HttpHandlerHook.cs +++ /dev/null @@ -1,58 +0,0 @@ -using BotSharp.Abstraction.Agents; -using BotSharp.Abstraction.Agents.Enums; -using BotSharp.Abstraction.Agents.Settings; -using BotSharp.Abstraction.Functions.Models; -using BotSharp.Abstraction.Repositories; - -namespace BotSharp.Plugin.HttpHandler.Hooks; - -public class HttpHandlerHook : AgentHookBase -{ - private static string FUNCTION_NAME = "handle_http_request"; - - public override string SelfId => string.Empty; - - public HttpHandlerHook(IServiceProvider services, AgentSettings settings) - : base(services, settings) - { - } - - public override void OnAgentLoaded(Agent agent) - { - var conv = _services.GetRequiredService(); - var isConvMode = conv.IsConversationMode(); - var isEnabled = !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(UtilityName.HttpHandler); - - if (isConvMode && isEnabled) - { - var (prompt, fn) = GetPromptAndFunction(FUNCTION_NAME); - if (fn != null) - { - if (!string.IsNullOrWhiteSpace(prompt)) - { - agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n"; - } - - if (agent.Functions == null) - { - agent.Functions = new List { fn }; - } - else - { - agent.Functions.Add(fn); - } - } - } - - base.OnAgentLoaded(agent); - } - - private (string, FunctionDef?) GetPromptAndFunction(string functionName) - { - var db = _services.GetRequiredService(); - var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant); - var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{functionName}.fn"))?.Content ?? string.Empty; - var loadAttachmentFn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(functionName)); - return (prompt, loadAttachmentFn); - } -} diff --git a/src/Plugins/BotSharp.Plugin.HttpHandler/Hooks/HttpHandlerUtilityHook.cs b/src/Plugins/BotSharp.Plugin.HttpHandler/Hooks/HttpHandlerUtilityHook.cs index 17a38df5..265558ac 100644 --- a/src/Plugins/BotSharp.Plugin.HttpHandler/Hooks/HttpHandlerUtilityHook.cs +++ b/src/Plugins/BotSharp.Plugin.HttpHandler/Hooks/HttpHandlerUtilityHook.cs @@ -4,8 +4,17 @@ namespace BotSharp.Plugin.HttpHandler.Hooks; public class HttpHandlerUtilityHook : IAgentUtilityHook { - public void AddUtilities(List utilities) + private static string HTTP_HANDLER_FN = "handle_http_request"; + + public void AddUtilities(List utilities) { - utilities.Add(UtilityName.HttpHandler); + var utility = new AgentUtility + { + Name = UtilityName.HttpHandler, + Functions = [new(HTTP_HANDLER_FN)], + Templates = [new($"{HTTP_HANDLER_FN}.fn")] + }; + + utilities.Add(utility); } } diff --git a/src/Plugins/BotSharp.Plugin.HttpHandler/HttpHandlerPlugin.cs b/src/Plugins/BotSharp.Plugin.HttpHandler/HttpHandlerPlugin.cs index 48d71ee8..9c8cab55 100644 --- a/src/Plugins/BotSharp.Plugin.HttpHandler/HttpHandlerPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.HttpHandler/HttpHandlerPlugin.cs @@ -20,7 +20,6 @@ public class HttpHandlerPlugin : IBotSharpPlugin return settingService.Bind("HttpHandler"); }); - services.AddScoped(); services.AddScoped(); } } diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Hooks/KnowledgeBaseAgentHook.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Hooks/KnowledgeBaseAgentHook.cs deleted file mode 100644 index f8296520..00000000 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Hooks/KnowledgeBaseAgentHook.cs +++ /dev/null @@ -1,60 +0,0 @@ -namespace BotSharp.Plugin.KnowledgeBase.Hooks; - -public class KnowledgeBaseAgentHook : AgentHookBase, IAgentHook -{ - public override string SelfId => string.Empty; - public KnowledgeBaseAgentHook(IServiceProvider services, AgentSettings settings) : base(services, settings) - { - - } - - public override void OnAgentLoaded(Agent agent) - { - var conv = _services.GetRequiredService(); - var isConvMode = conv.IsConversationMode(); - - if (isConvMode) - { - AddUtility(agent, UtilityName.KnowledgeRetrieval, "knowledge_retrieval"); - } - - base.OnAgentLoaded(agent); - } - - private void AddUtility(Agent agent, string utility, string functionName) - { - if (!IsEnableUtility(agent, utility)) return; - - var (prompt, fn) = GetPromptAndFunction(functionName); - if (fn != null) - { - if (!string.IsNullOrWhiteSpace(prompt)) - { - agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n"; - } - - if (agent.Functions == null) - { - agent.Functions = new List { fn }; - } - else - { - agent.Functions.Add(fn); - } - } - } - - private bool IsEnableUtility(Agent agent, string utility) - { - return !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(utility); - } - - private (string, FunctionDef?) GetPromptAndFunction(string functionName) - { - var db = _services.GetRequiredService(); - var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant); - var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{functionName}.fn"))?.Content ?? string.Empty; - var fn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(functionName)); - return (prompt, fn); - } -} diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Hooks/KnowledgeBaseUtilityHook.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Hooks/KnowledgeBaseUtilityHook.cs index cd428136..464fd5b2 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Hooks/KnowledgeBaseUtilityHook.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Hooks/KnowledgeBaseUtilityHook.cs @@ -2,8 +2,17 @@ namespace BotSharp.Plugin.KnowledgeBase.Hooks; public class KnowledgeBaseUtilityHook : IAgentUtilityHook { - public void AddUtilities(List utilities) + private const string KNOWLEDGE_RETRIEVAL_FN = "knowledge_retrieval"; + + public void AddUtilities(List utilities) { - utilities.Add(UtilityName.KnowledgeRetrieval); + var utility = new AgentUtility + { + Name = UtilityName.KnowledgeRetrieval, + Functions = [new(KNOWLEDGE_RETRIEVAL_FN)], + Templates = [new($"{KNOWLEDGE_RETRIEVAL_FN}.fn")] + }; + + utilities.Add(utility); } } diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs index ebb5aa63..8d89daf6 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs @@ -25,7 +25,6 @@ public class KnowledgeBasePlugin : IBotSharpPlugin services.AddSingleton(); services.AddScoped(); - services.AddScoped(); services.AddScoped(); } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs index baa0729f..76fee1b3 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs @@ -8,14 +8,15 @@ public class AgentDocument : MongoBase public string? InheritAgentId { get; set; } public string? IconUrl { get; set; } public string Instruction { get; set; } + public bool IsPublic { get; set; } + public bool Disabled { get; set; } + public bool MergeUtility { get; set; } public List ChannelInstructions { get; set; } public List Templates { get; set; } public List Functions { get; set; } public List Responses { get; set; } public List Samples { get; set; } - public List Utilities { get; set; } - public bool IsPublic { get; set; } - public bool Disabled { get; set; } + public List Utilities { get; set; } public List Profiles { get; set; } public List RoutingRules { get; set; } public AgentLlmConfigMongoElement? LlmConfig { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs index ce4227e7..4c716959 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs @@ -26,6 +26,8 @@ public class UserDocument : MongoBase public DateTime CreatedTime { get; set; } public DateTime UpdatedTime { get; set; } + public Dashboard? Dashboard { get; set; } + public User ToUser() { return new User diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentUtilityMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentUtilityMongoElement.cs new file mode 100644 index 00000000..c05f07ef --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentUtilityMongoElement.cs @@ -0,0 +1,63 @@ +using BotSharp.Abstraction.Agents.Models; + +namespace BotSharp.Plugin.MongoStorage.Models; + +public class AgentUtilityMongoElement +{ + public string Name { get; set; } + public bool Disabled { get; set; } + public List Functions { get; set; } = []; + public List Templates { get; set; } = []; + + public static AgentUtilityMongoElement ToMongoElement(AgentUtility utility) + { + return new AgentUtilityMongoElement + { + Name = utility.Name, + Disabled = utility.Disabled, + Functions = utility.Functions?.Select(x => new UtilityFunctionMongoElement(x.Name))?.ToList() ?? [], + Templates = utility.Templates?.Select(x => new UtilityTemplateMongoElement(x.Name))?.ToList() ?? [] + }; + } + + public static AgentUtility ToDomainElement(AgentUtilityMongoElement utility) + { + return new AgentUtility + { + Name = utility.Name, + Disabled = utility.Disabled, + Functions = utility.Functions?.Select(x => new UtilityFunction(x.Name))?.ToList() ?? [], + Templates = utility.Templates?.Select(x => new UtilityTemplate(x.Name))?.ToList() ?? [] + }; + } +} + +public class UtilityFunctionMongoElement +{ + public string Name { get; set; } + + public UtilityFunctionMongoElement() + { + + } + + public UtilityFunctionMongoElement(string name) + { + Name = name; + } +} + +public class UtilityTemplateMongoElement +{ + public string Name { get; set; } + + public UtilityTemplateMongoElement() + { + + } + + public UtilityTemplateMongoElement(string name) + { + Name = name; + } +} \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs index d17f7f2f..7f216b6d 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs @@ -2,7 +2,6 @@ using BotSharp.Abstraction.Agents.Models; using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Routing.Models; -using MongoDB.Driver; namespace BotSharp.Plugin.MongoStorage.Repository; @@ -57,7 +56,7 @@ public partial class MongoRepository UpdateAgentLlmConfig(agent.Id, agent.LlmConfig); break; case AgentField.Utility: - UpdateAgentUtilities(agent.Id, agent.Utilities); + UpdateAgentUtilities(agent.Id, agent.MergeUtility, agent.Utilities); break; case AgentField.All: UpdateAgentAllFields(agent); @@ -224,13 +223,16 @@ public partial class MongoRepository _dc.Agents.UpdateOne(filter, update); } - private void UpdateAgentUtilities(string agentId, List utilities) + private void UpdateAgentUtilities(string agentId, bool mergeUtility, List utilities) { if (utilities == null) return; + var elements = utilities?.Select(x => AgentUtilityMongoElement.ToMongoElement(x))?.ToList() ?? []; + var filter = Builders.Filter.Eq(x => x.Id, agentId); var update = Builders.Update - .Set(x => x.Utilities, utilities) + .Set(x => x.MergeUtility, mergeUtility) + .Set(x => x.Utilities, elements) .Set(x => x.UpdatedTime, DateTime.UtcNow); _dc.Agents.UpdateOne(filter, update); @@ -254,6 +256,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.MergeUtility, agent.MergeUtility) .Set(x => x.Type, agent.Type) .Set(x => x.Profiles, agent.Profiles) .Set(x => x.RoutingRules, agent.RoutingRules.Select(r => RoutingRuleMongoElement.ToMongoElement(r)).ToList()) @@ -263,7 +266,7 @@ public partial class MongoRepository .Set(x => x.Functions, agent.Functions.Select(f => FunctionDefMongoElement.ToMongoElement(f)).ToList()) .Set(x => x.Responses, agent.Responses.Select(r => AgentResponseMongoElement.ToMongoElement(r)).ToList()) .Set(x => x.Samples, agent.Samples) - .Set(x => x.Utilities, agent.Utilities) + .Set(x => x.Utilities, agent.Utilities.Select(u => AgentUtilityMongoElement.ToMongoElement(u)).ToList()) .Set(x => x.LlmConfig, AgentLlmConfigMongoElement.ToMongoElement(agent.LlmConfig)) .Set(x => x.IsPublic, agent.IsPublic) .Set(x => x.UpdatedTime, DateTime.UtcNow); @@ -406,28 +409,19 @@ public partial class MongoRepository IconUrl = x.IconUrl, Description = x.Description, Instruction = x.Instruction, - ChannelInstructions = x.ChannelInstructions? - .Select(i => ChannelInstructionMongoElement.ToMongoElement(i))? - .ToList() ?? new List(), - Templates = x.Templates? - .Select(t => AgentTemplateMongoElement.ToMongoElement(t))? - .ToList() ?? new List(), - Functions = x.Functions? - .Select(f => FunctionDefMongoElement.ToMongoElement(f))? - .ToList() ?? new List(), - Responses = x.Responses? - .Select(r => AgentResponseMongoElement.ToMongoElement(r))? - .ToList() ?? new List(), + ChannelInstructions = x.ChannelInstructions?.Select(i => ChannelInstructionMongoElement.ToMongoElement(i))?.ToList() ?? [], + Templates = x.Templates?.Select(t => AgentTemplateMongoElement.ToMongoElement(t))?.ToList() ?? [], + Functions = x.Functions?.Select(f => FunctionDefMongoElement.ToMongoElement(f))?.ToList() ?? [], + Responses = x.Responses?.Select(r => AgentResponseMongoElement.ToMongoElement(r))?.ToList() ?? [], Samples = x.Samples ?? new List(), - Utilities = x.Utilities ?? new List(), + Utilities = x.Utilities?.Select(u => AgentUtilityMongoElement.ToMongoElement(u))?.ToList() ?? [], IsPublic = x.IsPublic, Type = x.Type, InheritAgentId = x.InheritAgentId, Disabled = x.Disabled, + MergeUtility = x.MergeUtility, Profiles = x.Profiles, - RoutingRules = x.RoutingRules? - .Select(r => RoutingRuleMongoElement.ToMongoElement(r))? - .ToList() ?? new List(), + RoutingRules = x.RoutingRules?.Select(r => RoutingRuleMongoElement.ToMongoElement(r))?.ToList() ?? [], LlmConfig = AgentLlmConfigMongoElement.ToMongoElement(x.LlmConfig), CreatedTime = x.CreatedDateTime, UpdatedTime = x.UpdatedDateTime @@ -505,26 +499,17 @@ public partial class MongoRepository IconUrl = agentDoc.IconUrl, Description = agentDoc.Description, Instruction = agentDoc.Instruction, - ChannelInstructions = !agentDoc.ChannelInstructions.IsNullOrEmpty() ? agentDoc.ChannelInstructions - .Select(i => ChannelInstructionMongoElement.ToDomainElement(i)) - .ToList() : new List(), - Templates = !agentDoc.Templates.IsNullOrEmpty() ? agentDoc.Templates - .Select(t => AgentTemplateMongoElement.ToDomainElement(t)) - .ToList() : new List(), - Functions = !agentDoc.Functions.IsNullOrEmpty() ? agentDoc.Functions - .Select(f => FunctionDefMongoElement.ToDomainElement(f)) - .ToList() : new List(), - Responses = !agentDoc.Responses.IsNullOrEmpty() ? agentDoc.Responses - .Select(r => AgentResponseMongoElement.ToDomainElement(r)) - .ToList() : new List(), - RoutingRules = !agentDoc.RoutingRules.IsNullOrEmpty() ? agentDoc.RoutingRules - .Select(r => RoutingRuleMongoElement.ToDomainElement(agentDoc.Id, agentDoc.Name, r)) - .ToList() : new List(), + ChannelInstructions = agentDoc.ChannelInstructions?.Select(i => ChannelInstructionMongoElement.ToDomainElement(i))?.ToList() ?? [], + Templates = agentDoc.Templates?.Select(t => AgentTemplateMongoElement.ToDomainElement(t))?.ToList() ?? [], + Functions = agentDoc.Functions?.Select(f => FunctionDefMongoElement.ToDomainElement(f)).ToList() ?? [], + Responses = agentDoc.Responses?.Select(r => AgentResponseMongoElement.ToDomainElement(r))?.ToList() ?? [], + RoutingRules = agentDoc.RoutingRules?.Select(r => RoutingRuleMongoElement.ToDomainElement(agentDoc.Id, agentDoc.Name, r))?.ToList() ?? [], LlmConfig = AgentLlmConfigMongoElement.ToDomainElement(agentDoc.LlmConfig), - Samples = agentDoc.Samples ?? new List(), - Utilities = agentDoc.Utilities ?? new List(), + Samples = agentDoc.Samples ?? [], + Utilities = agentDoc.Utilities?.Select(u => AgentUtilityMongoElement.ToDomainElement(u))?.ToList() ?? [], IsPublic = agentDoc.IsPublic, Disabled = agentDoc.Disabled, + MergeUtility = agentDoc.MergeUtility, Type = agentDoc.Type, InheritAgentId = agentDoc.InheritAgentId, Profiles = agentDoc.Profiles, diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs index c2792b23..3c76e21d 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs @@ -324,4 +324,51 @@ public partial class MongoRepository return true; } + + public void AddDashboardConversation(string userId, string conversationId) + { + var user = _dc.Users.AsQueryable() + .FirstOrDefault(x => x.Id == userId || (x.ExternalId != null && x.ExternalId == userId)); + if (user == null) return; + var curDash = user.Dashboard ?? new Dashboard(); + curDash.ConversationList.Add(new DashboardConversation + { + Id = Guid.NewGuid().ToString(), + ConversationId = conversationId + }); + + var filter = Builders.Filter.Eq(x => x.Id, userId); + var update = Builders.Update.Set(x => x.Dashboard, curDash) + .Set(x => x.UpdatedTime, DateTime.UtcNow); + } + + public void RemoveDashboardConversation(string userId, string conversationId) + { + var user = _dc.Users.AsQueryable() + .FirstOrDefault(x => x.Id == userId || (x.ExternalId != null && x.ExternalId == userId)); + if (user == null || user.Dashboard == null || user.Dashboard.ConversationList.IsNullOrEmpty()) return; + var curDash = user.Dashboard; + var unpinConv = user.Dashboard.ConversationList.FirstOrDefault( + x => string.Equals(x.ConversationId, conversationId, StringComparison.OrdinalIgnoreCase)); + if (unpinConv == null) return; + curDash.ConversationList.Remove(unpinConv); + + var filter = Builders.Filter.Eq(x => x.Id, userId); + var update = Builders.Update.Set(x => x.Dashboard, curDash) + .Set(x => x.UpdatedTime, DateTime.UtcNow); + } + + public void UpdateDashboardConversation(string userId, DashboardConversation dashConv) + { + var user = _dc.Users.AsQueryable() + .FirstOrDefault(x => x.Id == userId || (x.ExternalId != null && x.ExternalId == userId)); + if (user == null || user.Dashboard == null || user.Dashboard.ConversationList.IsNullOrEmpty()) return; + var curIdx = user.Dashboard.ConversationList.ToList().FindIndex( + x => string.Equals(x.ConversationId, dashConv.ConversationId, StringComparison.OrdinalIgnoreCase)); + if (curIdx < 0) return; + + var filter = Builders.Filter.Eq(x => x.Id, userId); + var update = Builders.Update.Set(x => x.Dashboard.ConversationList[curIdx], dashConv) + .Set(x => x.UpdatedTime, DateTime.UtcNow); + } } diff --git a/src/Plugins/BotSharp.Plugin.Planner/Hooks/PlannerAgentHook.cs b/src/Plugins/BotSharp.Plugin.Planner/Hooks/PlannerAgentHook.cs index 503de3d5..d4bd1594 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/Hooks/PlannerAgentHook.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/Hooks/PlannerAgentHook.cs @@ -27,79 +27,4 @@ public class PlannerAgentHook : AgentHookBase return true; } - - public override void OnAgentLoaded(Agent agent) - { - var conv = _services.GetRequiredService(); - var isConvMode = conv.IsConversationMode(); - var isEnabled = !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(UtilityName.TwoStagePlanner); - - if (isConvMode && isEnabled) - { - var (prompt, fn) = GetPromptAndFunction("plan_primary_stage"); - if (fn != null) - { - if (!string.IsNullOrWhiteSpace(prompt)) - { - agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n"; - } - - if (agent.Functions == null) - { - agent.Functions = new List { fn }; - } - else - { - agent.Functions.Add(fn); - } - } - - (prompt, fn) = GetPromptAndFunction("plan_secondary_stage"); - if (fn != null) - { - if (!string.IsNullOrWhiteSpace(prompt)) - { - agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n"; - } - - if (agent.Functions == null) - { - agent.Functions = new List { fn }; - } - else - { - agent.Functions.Add(fn); - } - } - - (prompt, fn) = GetPromptAndFunction("plan_summary"); - if (fn != null) - { - if (!string.IsNullOrWhiteSpace(prompt)) - { - agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n"; - } - - if (agent.Functions == null) - { - agent.Functions = new List { fn }; - } - else - { - agent.Functions.Add(fn); - } - } - } - - base.OnAgentLoaded(agent); - } - - private (string, FunctionDef?) GetPromptAndFunction(string functionName) - { - var db = _services.GetRequiredService(); - var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant); - var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{functionName}.fn"))?.Content ?? string.Empty; - var loadAttachmentFn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(functionName)); - return (prompt, loadAttachmentFn); - } } diff --git a/src/Plugins/BotSharp.Plugin.Planner/Hooks/PlannerUtilityHook.cs b/src/Plugins/BotSharp.Plugin.Planner/Hooks/PlannerUtilityHook.cs index 08d3f8f5..b10d02ac 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/Hooks/PlannerUtilityHook.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/Hooks/PlannerUtilityHook.cs @@ -2,8 +2,27 @@ namespace BotSharp.Plugin.Planner.Hooks; public class PlannerUtilityHook : IAgentUtilityHook { - public void AddUtilities(List utilities) + private const string PRIMARY_STAGE_FN = "plan_primary_stage"; + private const string SECONDARY_STAGE_FN = "plan_secondary_stage"; + private const string SUMMARY_FN = "plan_summary"; + + public void AddUtilities(List utilities) { - utilities.Add(UtilityName.TwoStagePlanner); + var utility = new AgentUtility + { + Name = UtilityName.TwoStagePlanner, + Functions = [ + new(PRIMARY_STAGE_FN), + new(SECONDARY_STAGE_FN), + new(SUMMARY_FN) + ], + Templates = [ + new($"{PRIMARY_STAGE_FN}.fn"), + new($"{SECONDARY_STAGE_FN}.fn"), + new($"{SUMMARY_FN}.fn") + ] + }; + + utilities.Add(utility); } } diff --git a/src/Plugins/BotSharp.Plugin.Planner/PlannerPlugin.cs b/src/Plugins/BotSharp.Plugin.Planner/PlannerPlugin.cs index aba02745..17d84c1c 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/PlannerPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/PlannerPlugin.cs @@ -1,4 +1,4 @@ -using BotSharp.Abstraction.Routing.Planning; +using BotSharp.Abstraction.Planning; using BotSharp.Plugin.Planner.TwoStaging; namespace BotSharp.Plugin.Planner; @@ -17,7 +17,7 @@ public class PlannerPlugin : IBotSharpPlugin public void RegisterDI(IServiceCollection services, IConfiguration config) { - services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); } diff --git a/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs b/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs index 565115a5..47b9c03a 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs @@ -1,10 +1,10 @@ using BotSharp.Abstraction.Infrastructures.Enums; -using BotSharp.Abstraction.Routing.Planning; -using BotSharp.Core.Routing.Planning; +using BotSharp.Abstraction.Planning; +using BotSharp.Core.Routing.Reasoning; namespace BotSharp.Plugin.Planner.TwoStaging; -public partial class TwoStageTaskPlanner : IRoutingPlaner +public partial class TwoStageTaskPlanner : ITaskPlanner { private readonly IServiceProvider _services; private readonly ILogger _logger; @@ -40,7 +40,7 @@ public partial class TwoStageTaskPlanner : IRoutingPlaner inst = response.Content.JsonContent(); // Fix LLM malformed response - PlannerHelper.FixMalformedResponse(_services, inst); + ReasonerHelper.FixMalformedResponse(_services, inst); return inst; } diff --git a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/agent.json b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/agent.json index a90ef2ed..d1b5eb52 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/agent.json +++ b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/agent.json @@ -9,7 +9,8 @@ "disabled": false, "isPublic": true, "profiles": [ "planning" ], - "utilities": [ "two-stage-planner", "sql-dictionary-lookup", "excel-handler" ], + "mergeUtility": true, + "utilities": [], "llmConfig": { "provider": "openai", "model": "gpt-4o", diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterAgentHook.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterAgentHook.cs deleted file mode 100644 index 64e0ec27..00000000 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterAgentHook.cs +++ /dev/null @@ -1,51 +0,0 @@ -namespace BotSharp.Plugin.PythonInterpreter.Hooks; - -public class InterpreterAgentHook : AgentHookBase -{ - private static string FUNCTION_NAME = "python_interpreter"; - - public override string SelfId => string.Empty; - - public InterpreterAgentHook(IServiceProvider services, AgentSettings settings) - : base(services, settings) - { - } - public override void OnAgentLoaded(Agent agent) - { - var conv = _services.GetRequiredService(); - var isConvMode = conv.IsConversationMode(); - var isEnabled = !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(UtilityName.PythonInterpreter); - - if (isConvMode && isEnabled) - { - var (prompt, fn) = GetPromptAndFunction(); - if (fn != null) - { - if (!string.IsNullOrWhiteSpace(prompt)) - { - agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n"; - } - - if (agent.Functions == null) - { - agent.Functions = new List { fn }; - } - else - { - agent.Functions.Add(fn); - } - } - } - - base.OnAgentLoaded(agent); - } - - private (string, FunctionDef?) GetPromptAndFunction() - { - var db = _services.GetRequiredService(); - var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant); - var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{FUNCTION_NAME}.fn"))?.Content ?? string.Empty; - var loadAttachmentFn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(FUNCTION_NAME)); - return (prompt, loadAttachmentFn); - } -} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterUtilityHook.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterUtilityHook.cs index be37bfa1..540870be 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterUtilityHook.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterUtilityHook.cs @@ -2,8 +2,17 @@ namespace BotSharp.Plugin.PythonInterpreter.Hooks; public class InterpreterUtilityHook : IAgentUtilityHook { - public void AddUtilities(List utilities) + private static string FUNCTION_NAME = "python_interpreter"; + + public void AddUtilities(List utilities) { - utilities.Add(UtilityName.PythonInterpreter); + var utility = new AgentUtility() + { + Name = UtilityName.PythonInterpreter, + Functions = [new(FUNCTION_NAME)], + Templates = [new($"{FUNCTION_NAME}.fn")] + }; + + utilities.Add(utility); } } diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/InterpreterPlugin.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/InterpreterPlugin.cs index be2e7c06..7d195286 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/InterpreterPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/InterpreterPlugin.cs @@ -15,7 +15,6 @@ public class InterpreterPlugin : IBotSharpAppPlugin public void RegisterDI(IServiceCollection services, IConfiguration config) { - services.AddScoped(); services.AddScoped(); } diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj b/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj index 6433aae1..3a090f0c 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj @@ -1,4 +1,4 @@ - + $(TargetFramework) diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Enum/Utility.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Enum/UtilityName.cs similarity index 90% rename from src/Plugins/BotSharp.Plugin.SqlDriver/Enum/Utility.cs rename to src/Plugins/BotSharp.Plugin.SqlDriver/Enum/UtilityName.cs index 4d9142ca..159d5e7a 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Enum/Utility.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Enum/UtilityName.cs @@ -1,6 +1,6 @@ namespace BotSharp.Plugin.SqlDriver.Enum; -public class Utility +public class UtilityName { public const string SqlExecutor = "sql-executor"; public const string SqlDictionaryLookup = "sql-dictionary-lookup"; diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Helpers/SqlDriverHelper.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Helpers/SqlDriverHelper.cs new file mode 100644 index 00000000..3049b754 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Helpers/SqlDriverHelper.cs @@ -0,0 +1,20 @@ +namespace BotSharp.Plugin.SqlDriver.Helpers; + +internal static class SqlDriverHelper +{ + internal static string GetDatabaseType(IServiceProvider services) + { + var settings = services.GetRequiredService(); + var dbType = "MySQL"; + + if (!string.IsNullOrWhiteSpace(settings?.SqlServerConnectionString)) + { + dbType = "SQL Server"; + } + else if (!string.IsNullOrWhiteSpace(settings?.SqlLiteConnectionString)) + { + dbType = "SQL Lite"; + } + return dbType; + } +} diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/GetTableDefinitionHook.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/GetTableDefinitionHook.cs deleted file mode 100644 index bcfd2093..00000000 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/GetTableDefinitionHook.cs +++ /dev/null @@ -1,84 +0,0 @@ -using BotSharp.Abstraction.Agents.Enums; -using BotSharp.Abstraction.Agents.Settings; -using BotSharp.Abstraction.Functions.Models; -using BotSharp.Abstraction.Repositories; - -namespace BotSharp.Plugin.SqlDriver.Hooks; - -public class GetTableDefinitionHook : AgentHookBase, IAgentHook -{ - private const string SQL_EXECUTOR_TEMPLATE = "sql_table_definition.fn"; - private IEnumerable _targetSqlExecutorFunctions = new List - { - "sql_table_definition", - }; - - public override string SelfId => BuiltInAgentId.Planner; - - public GetTableDefinitionHook(IServiceProvider services, AgentSettings settings) : base(services, settings) - { - } - - public override void OnAgentLoaded(Agent agent) - { - var conv = _services.GetRequiredService(); - var isConvMode = conv.IsConversationMode(); - var isEnabled = !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(Utility.SqlTableDefinition); - - if (isConvMode && isEnabled) - { - var (prompt, fns) = GetPromptAndFunctions(); - if (!fns.IsNullOrEmpty()) - { - if (!string.IsNullOrWhiteSpace(prompt)) - { - agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n"; - } - - if (agent.Functions == null) - { - agent.Functions = fns; - } - else - { - agent.Functions.AddRange(fns); - } - } - } - - base.OnAgentLoaded(agent); - } - - private (string, List?) GetPromptAndFunctions() - { - var db = _services.GetRequiredService(); - var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant); - var fns = agent?.Functions?.Where(x => _targetSqlExecutorFunctions.Contains(x.Name))?.ToList(); - - var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo(SQL_EXECUTOR_TEMPLATE))?.Content ?? string.Empty; - var dbType = GetDatabaseType(); - var render = _services.GetRequiredService(); - prompt = render.Render(prompt, new Dictionary - { - { "db_type", dbType } - }); - - return (prompt, fns); - } - - private string GetDatabaseType() - { - var settings = _services.GetRequiredService(); - var dbType = "MySQL"; - - if (!string.IsNullOrWhiteSpace(settings?.SqlServerConnectionString)) - { - dbType = "SQL Server"; - } - else if (!string.IsNullOrWhiteSpace(settings?.SqlLiteConnectionString)) - { - dbType = "SQL Lite"; - } - return dbType; - } -} diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlDictionaryLookupHook.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlDictionaryLookupHook.cs deleted file mode 100644 index 534b0bf7..00000000 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlDictionaryLookupHook.cs +++ /dev/null @@ -1,84 +0,0 @@ -using BotSharp.Abstraction.Agents.Enums; -using BotSharp.Abstraction.Agents.Settings; -using BotSharp.Abstraction.Functions.Models; -using BotSharp.Abstraction.Repositories; - -namespace BotSharp.Plugin.SqlDriver.Hooks; - -public class SqlDictionaryLookupHook : AgentHookBase, IAgentHook -{ - private const string SQL_EXECUTOR_TEMPLATE = "verify_dictionary_term.fn"; - private IEnumerable _targetSqlExecutorFunctions = new List - { - "verify_dictionary_term", - }; - - public override string SelfId => BuiltInAgentId.Planner; - - public SqlDictionaryLookupHook(IServiceProvider services, AgentSettings settings) : base(services, settings) - { - } - - public override void OnAgentLoaded(Agent agent) - { - var conv = _services.GetRequiredService(); - var isConvMode = conv.IsConversationMode(); - var isEnabled = !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(Utility.SqlDictionaryLookup); - - if (isConvMode && isEnabled) - { - var (prompt, fns) = GetPromptAndFunctions(); - if (!fns.IsNullOrEmpty()) - { - if (!string.IsNullOrWhiteSpace(prompt)) - { - agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n"; - } - - if (agent.Functions == null) - { - agent.Functions = fns; - } - else - { - agent.Functions.AddRange(fns); - } - } - } - - base.OnAgentLoaded(agent); - } - - private (string, List?) GetPromptAndFunctions() - { - var db = _services.GetRequiredService(); - var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant); - var fns = agent?.Functions?.Where(x => _targetSqlExecutorFunctions.Contains(x.Name))?.ToList(); - - var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo(SQL_EXECUTOR_TEMPLATE))?.Content ?? string.Empty; - var dbType = GetDatabaseType(); - var render = _services.GetRequiredService(); - prompt = render.Render(prompt, new Dictionary - { - { "db_type", dbType } - }); - - return (prompt, fns); - } - - private string GetDatabaseType() - { - var settings = _services.GetRequiredService(); - var dbType = "MySQL"; - - if (!string.IsNullOrWhiteSpace(settings?.SqlServerConnectionString)) - { - dbType = "SQL Server"; - } - else if (!string.IsNullOrWhiteSpace(settings?.SqlLiteConnectionString)) - { - dbType = "SQL Lite"; - } - return dbType; - } -} diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlDriverAgentHook.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlDriverAgentHook.cs new file mode 100644 index 00000000..53b7866f --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlDriverAgentHook.cs @@ -0,0 +1,19 @@ +using BotSharp.Abstraction.Agents.Settings; + +namespace BotSharp.Plugin.SqlDriver.Hooks; + +public class SqlDriverAgentHook : AgentHookBase, IAgentHook +{ + public override string SelfId => BuiltInAgentId.Planner; + + public SqlDriverAgentHook(IServiceProvider services, AgentSettings settings) + : base(services, settings) + { + } + + public override void OnAgentLoaded(Agent agent) + { + var dbType = SqlDriverHelper.GetDatabaseType(_services); + agent.TemplateDict["db_type"] = dbType; + } +} diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlExecutorHook.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlExecutorHook.cs deleted file mode 100644 index 56e932c7..00000000 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlExecutorHook.cs +++ /dev/null @@ -1,85 +0,0 @@ -using BotSharp.Abstraction.Agents.Enums; -using BotSharp.Abstraction.Agents.Settings; -using BotSharp.Abstraction.Functions.Models; -using BotSharp.Abstraction.Repositories; - -namespace BotSharp.Plugin.SqlDriver.Hooks; - -public class SqlExecutorHook : AgentHookBase, IAgentHook -{ - private const string SQL_EXECUTOR_TEMPLATE = "sql_executor.fn"; - private IEnumerable _targetSqlExecutorFunctions = new List - { - "sql_select", - "sql_table_definition", - }; - - public override string SelfId => string.Empty; - - public SqlExecutorHook(IServiceProvider services, AgentSettings settings) : base(services, settings) - { - } - - public override void OnAgentLoaded(Agent agent) - { - var conv = _services.GetRequiredService(); - var isConvMode = conv.IsConversationMode(); - var isEnabled = !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(Utility.SqlExecutor); - - if (isConvMode && isEnabled) - { - var (prompt, fns) = GetPromptAndFunctions(); - if (!fns.IsNullOrEmpty()) - { - if (!string.IsNullOrWhiteSpace(prompt)) - { - agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n"; - } - - if (agent.Functions == null) - { - agent.Functions = fns; - } - else - { - agent.Functions.AddRange(fns); - } - } - } - - base.OnAgentLoaded(agent); - } - - private (string, List?) GetPromptAndFunctions() - { - var db = _services.GetRequiredService(); - var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant); - var fns = agent?.Functions?.Where(x => _targetSqlExecutorFunctions.Contains(x.Name))?.ToList(); - - var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo(SQL_EXECUTOR_TEMPLATE))?.Content ?? string.Empty; - var dbType = GetDatabaseType(); //need change-> using hook? - var render = _services.GetRequiredService(); - prompt = render.Render(prompt, new Dictionary - { - { "db_type", dbType } - }); - - return (prompt, fns); - } - - private string GetDatabaseType() - { - var settings = _services.GetRequiredService(); - var dbType = "MySQL"; - - if (!string.IsNullOrWhiteSpace(settings?.SqlServerConnectionString)) - { - dbType = "SQL Server"; - } - else if (!string.IsNullOrWhiteSpace(settings?.SqlLiteConnectionString)) - { - dbType = "SQL Lite"; - } - return dbType; - } -} diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlUtilityHook.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlUtilityHook.cs index daac8cba..1984ee98 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlUtilityHook.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlUtilityHook.cs @@ -2,10 +2,34 @@ namespace BotSharp.Plugin.SqlDriver.Hooks; public class SqlUtilityHook : IAgentUtilityHook { - public void AddUtilities(List utilities) + private const string SQL_TABLE_DEFINITION_FN = "sql_table_definition"; + private const string VERIFY_DICTIONARY_TERM_FN = "verify_dictionary_term"; + private const string SQL_SELECT_FN = "sql_select"; + + public void AddUtilities(List utilities) { - utilities.Add(Utility.SqlExecutor); - utilities.Add(Utility.SqlDictionaryLookup); - utilities.Add(Utility.SqlTableDefinition); + var items = new List + { + new AgentUtility + { + Name = UtilityName.SqlTableDefinition, + Functions = [new(SQL_TABLE_DEFINITION_FN)], + Templates = [new($"{SQL_TABLE_DEFINITION_FN}.fn")] + }, + new AgentUtility + { + Name = UtilityName.SqlDictionaryLookup, + Functions = [new(VERIFY_DICTIONARY_TERM_FN)], + Templates = [new($"{VERIFY_DICTIONARY_TERM_FN}.fn")] + }, + new AgentUtility + { + Name = UtilityName.SqlExecutor, + Functions = [new(SQL_SELECT_FN), new(SQL_TABLE_DEFINITION_FN)], + Templates = [new($"sql_executor.fn")] + } + }; + + utilities.AddRange(items); } } diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Settings/SqlDriverSetting.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Settings/SqlDriverSetting.cs index 24f12420..bff2c48c 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Settings/SqlDriverSetting.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Settings/SqlDriverSetting.cs @@ -1,4 +1,4 @@ -namespace BotSharp.Plugin.SqlHero.Settings; +namespace BotSharp.Plugin.SqlDriver.Settings; public class SqlDriverSetting { diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/SqlDriverPlugin.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/SqlDriverPlugin.cs index 47a7883f..cf4579d5 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/SqlDriverPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/SqlDriverPlugin.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Agents.Enums; using BotSharp.Abstraction.Planning; namespace BotSharp.Plugin.SqlDriver; @@ -25,12 +24,10 @@ public class SqlDriverPlugin : IBotSharpPlugin services.AddScoped(); services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddScoped(); + services.AddScoped(); } } diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Using.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Using.cs index 9f63a841..b6b5ea06 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Using.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Using.cs @@ -6,10 +6,8 @@ global using System.Text.RegularExpressions; global using System.Threading.Tasks; global using System.Linq; global using System.Text.Json; - global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.Logging; - global using BotSharp.Abstraction.Conversations; global using BotSharp.Abstraction.Plugins; global using BotSharp.Abstraction.Conversations.Models; @@ -26,9 +24,10 @@ global using BotSharp.Abstraction.Settings; global using BotSharp.Plugin.SqlDriver.Hooks; global using BotSharp.Plugin.SqlDriver.Services; global using BotSharp.Plugin.SqlDriver.Enum; -global using BotSharp.Plugin.SqlHero.Settings; global using BotSharp.Abstraction.Agents.Enums; global using BotSharp.Abstraction.Instructs; global using BotSharp.Abstraction.Instructs.Models; global using BotSharp.Abstraction.Routing; global using BotSharp.Plugin.SqlDriver.Interfaces; +global using BotSharp.Plugin.SqlDriver.Helpers; +global using BotSharp.Plugin.SqlDriver.Settings; diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/agent.json b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/agent.json index 1d5aca1f..1ec4bfce 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/agent.json +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/agent.json @@ -1,7 +1,7 @@ { "id": "beda4c12-e1ec-4b4b-b328-3df4a6687c4f", "name": "SQL Driver", - "description": "Execute the sql query in database from the latest dialog.", + "description": "Transfer to this Agent is allowed only when executable SQL statements are provided in the context.", "iconUrl": "https://cdn-icons-png.flaticon.com/512/3161/3161158.png", "type": "task", "createdDateTime": "2023-11-15T13:49:00Z", @@ -18,7 +18,7 @@ "field": "sql_statement", "required": true, "field_type": "string", - "description": "SQL statement" + "description": "SQL statement provided in the context" } ] } \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Hooks/OutboundPhoneCallHandlerHook.cs b/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Hooks/OutboundPhoneCallHandlerHook.cs deleted file mode 100644 index 7d8d33fb..00000000 --- a/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Hooks/OutboundPhoneCallHandlerHook.cs +++ /dev/null @@ -1,58 +0,0 @@ -using BotSharp.Abstraction.Agents.Models; -using BotSharp.Abstraction.Agents.Settings; -using BotSharp.Abstraction.Repositories; -using BotSharp.Abstraction.Utilities; -using BotSharp.Plugin.Twilio.OutboundPhoneCallHandler.Enums; - -namespace BotSharp.Plugin.Twilio.OutboundPhoneCallHandler.Hooks -{ - internal class OutboundPhoneCallHandlerHook : AgentHookBase - { - private static string FUNCTION_NAME = "twilio_outbound_phone_call"; - - public override string SelfId => string.Empty; - - public OutboundPhoneCallHandlerHook(IServiceProvider services, AgentSettings settings) : base(services, settings) - { - } - - public override void OnAgentLoaded(Agent agent) - { - var conv = _services.GetRequiredService(); - var isConvMode = conv.IsConversationMode(); - var isEnabled = !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(UtilityName.OutboundPhoneCall); - - if (isConvMode && isEnabled) - { - var (prompt, fn) = GetPromptAndFunction(); - if (fn != null) - { - if (!string.IsNullOrWhiteSpace(prompt)) - { - agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n"; - } - - if (agent.Functions == null) - { - agent.Functions = new List { fn }; - } - else - { - agent.Functions.Add(fn); - } - } - } - - base.OnAgentLoaded(agent); - } - - private (string, FunctionDef) GetPromptAndFunction() - { - var db = _services.GetRequiredService(); - var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant); - var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{FUNCTION_NAME}.fn"))?.Content ?? string.Empty; - var loadAttachmentFn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(FUNCTION_NAME)); - return (prompt, loadAttachmentFn); - } - } -} diff --git a/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Hooks/OutboundPhoneCallHandlerUtilityHook.cs b/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Hooks/OutboundPhoneCallHandlerUtilityHook.cs index 982fe9b1..09e8a9e4 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Hooks/OutboundPhoneCallHandlerUtilityHook.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Hooks/OutboundPhoneCallHandlerUtilityHook.cs @@ -1,12 +1,21 @@ +using BotSharp.Abstraction.Agents.Models; using BotSharp.Plugin.Twilio.OutboundPhoneCallHandler.Enums; -namespace BotSharp.Plugin.Twilio.OutboundPhoneCallHandler.Hooks +namespace BotSharp.Plugin.Twilio.OutboundPhoneCallHandler.Hooks; + +public class OutboundPhoneCallHandlerUtilityHook : IAgentUtilityHook { - public class OutboundPhoneCallHandlerUtilityHook : IAgentUtilityHook + private static string OUTBOUND_PHONE_CALL_FN = "twilio_outbound_phone_call"; + + public void AddUtilities(List utilities) { - public void AddUtilities(List utilities) + var utility = new AgentUtility { - utilities.Add(UtilityName.OutboundPhoneCall); - } + Name = UtilityName.OutboundPhoneCall, + Functions = [new(OUTBOUND_PHONE_CALL_FN)], + Templates = [new($"{OUTBOUND_PHONE_CALL_FN}.fn")] + }; + + utilities.Add(utility); } } diff --git a/src/Plugins/BotSharp.Plugin.Twilio/TwilioPlugin.cs b/src/Plugins/BotSharp.Plugin.Twilio/TwilioPlugin.cs index 484a5eaf..d78489ad 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/TwilioPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/TwilioPlugin.cs @@ -31,7 +31,6 @@ public class TwilioPlugin : IBotSharpPlugin services.AddSingleton(); services.AddHostedService(); services.AddTwilioRequestValidation(); - services.AddScoped(); services.AddScoped(); } }