diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructHook.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructHook.cs new file mode 100644 index 00000000..8d5dea5e --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructHook.cs @@ -0,0 +1,9 @@ +using BotSharp.Abstraction.Instructs.Models; + +namespace BotSharp.Abstraction.Instructs; + +public interface IInstructHook +{ + Task BeforeCompletion(RoleDialogModel message); + Task AfterCompletion(InstructResult result); +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs index 0adb2be8..76346165 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs @@ -1,9 +1,11 @@ +using BotSharp.Abstraction.Instructs.Models; + namespace BotSharp.Abstraction.Instructs; public interface IInstructService { - Task ExecuteInstructionRecursively(Agent agent, - List wholeDialogs, + Task ExecuteInstruction(Agent agent, + RoleDialogModel message, Func onMessageReceived, Func onFunctionExecuting, Func onFunctionExecuted); diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/InstructHookBase.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/InstructHookBase.cs new file mode 100644 index 00000000..fc1f1dd5 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/InstructHookBase.cs @@ -0,0 +1,16 @@ +using BotSharp.Abstraction.Instructs.Models; + +namespace BotSharp.Abstraction.Instructs; + +public class InstructHookBase : IInstructHook +{ + public virtual async Task AfterCompletion(InstructResult result) + { + return; + } + + public virtual async Task BeforeCompletion(RoleDialogModel message) + { + return; + } +} diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs index 7ea90864..5420d6a0 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs @@ -5,6 +5,9 @@ namespace BotSharp.Core.Agents.Services; public partial class AgentService { +#if !DEBUG + [MemoryCache(10 * 60)] +#endif public async Task LoadAgent(string id) { var hooks = _services.GetServices(); diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index 6392a491..b9af74c5 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -73,6 +73,7 @@ + diff --git a/src/Infrastructure/BotSharp.Core/Instructs/InstructService.cs b/src/Infrastructure/BotSharp.Core/Instructs/InstructService.cs index c3ad6346..2d0f5a5d 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/InstructService.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/InstructService.cs @@ -1,8 +1,10 @@ using BotSharp.Abstraction.Agents.Enums; using BotSharp.Abstraction.Agents.Models; using BotSharp.Abstraction.Instructs; +using BotSharp.Abstraction.Instructs.Models; using BotSharp.Abstraction.MLTasks; using BotSharp.Abstraction.Templating; +using System.IO; namespace BotSharp.Core.Instructs; @@ -17,7 +19,53 @@ public partial class InstructService : IInstructService _logger = logger; } - public async Task ExecuteInstructionRecursively(Agent agent, + public async Task ExecuteInstruction(Agent agent, + RoleDialogModel message, + Func onMessageReceived, + Func onFunctionExecuting, + Func onFunctionExecuted) + { + var response = new InstructResult(); + + var wholeDialogs = new List + { + new RoleDialogModel("user", message.Content) + }; + + // Trigger before completion hooks + var hooks = _services.GetServices(); + foreach (var hook in hooks) + { + await hook.BeforeCompletion(message); + } + + await ExecuteInstructionRecursively(agent, + wholeDialogs, + async msg => + { + response.Text = msg.Content; + await onMessageReceived(msg); + }, + async fn => + { + response.Function = fn.FunctionName; + await onFunctionExecuting(fn); + }, + async fn => + { + response.Data = fn.ExecutionData; + await onFunctionExecuted(fn); + }); + + foreach (var hook in hooks) + { + await hook.AfterCompletion(response); + } + + return response; + } + + private async Task ExecuteInstructionRecursively(Agent agent, List wholeDialogs, Func onMessageReceived, Func onFunctionExecuting, @@ -28,6 +76,8 @@ public partial class InstructService : IInstructService var result = await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs, async msg => { await onMessageReceived(msg); + + wholeDialogs.Add(msg); }, async fn => { var preAgentId = agent.Id; diff --git a/src/Infrastructure/BotSharp.Core/Routing/Router.cs b/src/Infrastructure/BotSharp.Core/Routing/Router.cs index c1f025c4..8dd9515c 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Router.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Router.cs @@ -1,3 +1,4 @@ +using Aspects.Cache; using BotSharp.Abstraction.Agents.Models; using BotSharp.Abstraction.Routing.Models; using BotSharp.Abstraction.Routing.Settings; @@ -28,6 +29,7 @@ public class Router : IAgentRouting return await agentService.LoadAgent(AgentId); } + [MemoryCache(10 * 60)] public RoutingRecord[] GetRoutingRecords() { var agentSettings = _services.GetRequiredService(); diff --git a/src/Infrastructure/BotSharp.Core/Using.cs b/src/Infrastructure/BotSharp.Core/Using.cs index 986529dd..898faa86 100644 --- a/src/Infrastructure/BotSharp.Core/Using.cs +++ b/src/Infrastructure/BotSharp.Core/Using.cs @@ -22,4 +22,5 @@ global using BotSharp.Core.Repository.DbTables; global using BotSharp.Core.Agents.Services; global using BotSharp.Core.Conversations.Services; global using BotSharp.Core.Infrastructures; -global using BotSharp.Core.Users.Services; \ No newline at end of file +global using BotSharp.Core.Users.Services; +global using Aspects.Cache; \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs index bfb50d98..3eeac672 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs @@ -1,9 +1,9 @@ +using BotSharp.Abstraction.Agents.Enums; using BotSharp.Abstraction.Agents.Models; using BotSharp.Abstraction.ApiAdapters; using BotSharp.Abstraction.Conversations.Models; using BotSharp.Abstraction.Instructs; using BotSharp.Abstraction.Instructs.Models; -using BotSharp.OpenAPI.ViewModels.Conversations; using BotSharp.OpenAPI.ViewModels.Instructs; namespace BotSharp.OpenAPI.Controllers; @@ -13,43 +13,32 @@ namespace BotSharp.OpenAPI.Controllers; public class InstructModeController : ControllerBase, IApiAdapter { private readonly IServiceProvider _services; - private readonly IUserIdentity _user; - public InstructModeController(IServiceProvider services, - IUserIdentity user) + public InstructModeController(IServiceProvider services) { _services = services; - _user = user; } [HttpPost("/instruct/{agentId}")] public async Task NewConversation([FromRoute] string agentId, [FromBody] InstructMessageModel input) { - var response = new InstructResult(); var instructor = _services.GetRequiredService(); var agentService = _services.GetRequiredService(); Agent agent = await agentService.LoadAgent(agentId); - await instructor.ExecuteInstructionRecursively(agent, - new List - { - new RoleDialogModel("user", input.Text) - }, - async msg => - { - response.Text = msg.Content; - }, - async fnExecuting => - { + // switch to different instruction template + if (!string.IsNullOrEmpty(input.TemplateName)) + { + var agentSettings = _services.GetRequiredService(); + var filePath = Path.Combine(agentService.GetAgentDataDir(agentId), $"{input.TemplateName}.{agentSettings.TemplateFormat}"); + agent.Instruction = System.IO.File.ReadAllText(filePath); + } - }, - async fnExecuted => - { - response.Function = fnExecuted.FunctionName; - response.Data = fnExecuted.ExecutionData; - }); - - return response; + return await instructor.ExecuteInstruction(agent, + new RoleDialogModel(AgentRole.User, input.Text), + fn => Task.CompletedTask, + fn => Task.CompletedTask, + fn => Task.CompletedTask); } }