diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentRouting.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentRouting.cs index 2a1f18fa..8b0769a7 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentRouting.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentRouting.cs @@ -2,5 +2,6 @@ namespace BotSharp.Abstraction.Agents; public interface IAgentRouting { + Task LoadRouter(); Task LoadCurrentAgent(); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentRoutingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentRoutingArgs.cs new file mode 100644 index 00000000..4046cda4 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentRoutingArgs.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace BotSharp.Abstraction.Agents.Models; + +public class AgentRoutingArgs +{ + [JsonPropertyName("agent_id")] + public string AgentId { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs index 740547a3..6f56d00b 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs @@ -20,12 +20,14 @@ public interface IConversationService /// /// /// This delegate is useful when you want to report progress on UI + /// This delegate is useful when you want to report progress on UI /// Task SendMessage(string agentId, string conversationId, RoleDialogModel lastDalog, Func onMessageReceived, - Func onFunctionExecuting); + Func onFunctionExecuting, + Func onFunctionExecuted); List GetDialogHistory(string conversationId, int lastCount = 20); Task CleanHistory(string agentId); diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentRouter.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentRouter.cs index 1c28bad3..f14ed93c 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentRouter.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentRouter.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.Agents; using BotSharp.Abstraction.Agents.Models; namespace BotSharp.Core.Agents.Services; @@ -17,11 +18,18 @@ public class AgentRouter : IAgentRouting _settings = settings; } + public async Task LoadRouter() + { + var agentService = _services.GetRequiredService(); + var agent = await agentService.LoadAgent(_settings.RouterId); + return agent; + } + public async Task LoadCurrentAgent() { // Load current agent from state var state = _services.GetRequiredService(); - var currentAgentId = state.GetState("agentId"); + var currentAgentId = state.GetState("agent_id"); if (string.IsNullOrEmpty(currentAgentId)) { currentAgentId = _settings.RouterId; @@ -30,7 +38,7 @@ public class AgentRouter : IAgentRouting var agent = await agentService.LoadAgent(currentAgentId); // Set agent and trigger state changed - state.SetState("agentId", currentAgentId); + state.SetState("agent_id", currentAgentId); return agent; } diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index ceb5a348..6b871db4 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -73,7 +73,6 @@ - diff --git a/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs b/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs index 4607086a..ae315146 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs +++ b/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs @@ -38,6 +38,7 @@ public static class BotSharpServiceCollectionExtensions services.AddScoped(); services.AddScoped(); + services.AddScoped(); return services; } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.GetChatCompletionsAsyncRecursively.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.GetChatCompletionsAsyncRecursively.cs index 1180c7e6..c28ca63d 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.GetChatCompletionsAsyncRecursively.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.GetChatCompletionsAsyncRecursively.cs @@ -15,7 +15,8 @@ public partial class ConversationService Agent agent, List wholeDialogs, Func onMessageReceived, - Func onFunctionExecuting) + Func onFunctionExecuting, + Func onFunctionExecuted) { currentRecursiveDepth++; if (currentRecursiveDepth > maxRecursiveDepth) @@ -38,7 +39,7 @@ public partial class ConversationService { var preAgentId = agent.Id; - await HandleFunctionMessage(fn, onFunctionExecuting); + await HandleFunctionMessage(fn, onFunctionExecuting, onFunctionExecuted); // Function executed has exception if (fn.ExecutionResult == null) @@ -58,10 +59,6 @@ public partial class ConversationService var agentSettings = _services.GetRequiredService(); var agentService = _services.GetRequiredService(); agent = await agentService.LoadAgent(fn.CurrentAgentId); - - // Set state to make next conversation will go to this agent directly - // var state = _services.GetRequiredService(); - // state.SetState("agentId", fn.CurrentAgentId); } // Add to dialog history @@ -70,7 +67,13 @@ public partial class ConversationService // After function is executed, pass the result to LLM to get a natural response wholeDialogs.Add(fn); - await GetChatCompletionsAsyncRecursively(chatCompletion, conversationId, agent, wholeDialogs, onMessageReceived, onFunctionExecuting); + await GetChatCompletionsAsyncRecursively(chatCompletion, + conversationId, + agent, + wholeDialogs, + onMessageReceived, + onFunctionExecuting, + onFunctionExecuted); }); return result; @@ -89,7 +92,9 @@ public partial class ConversationService await onMessageReceived(msg); } - private async Task HandleFunctionMessage(RoleDialogModel msg, Func onFunctionExecuting) + private async Task HandleFunctionMessage(RoleDialogModel msg, + Func onFunctionExecuting, + Func onFunctionExecuted) { // Save states SaveStateByArgs(msg.FunctionArgs); @@ -97,5 +102,6 @@ public partial class ConversationService // Call functions await onFunctionExecuting(msg); await CallFunctions(msg); + await onFunctionExecuted(msg); } } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs index b4ab98f5..d4a44406 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -8,7 +8,8 @@ public partial class ConversationService public async Task SendMessage(string agentId, string conversationId, RoleDialogModel lastDialog, Func onMessageReceived, - Func onFunctionExecuting) + Func onFunctionExecuting, + Func onFunctionExecuted) { var converation = await GetConversation(conversationId); @@ -29,7 +30,7 @@ public partial class ConversationService stateService.Load(); var router = _services.GetRequiredService(); - var agent = await router.LoadCurrentAgent(); + var agent = await router.LoadRouter(); _logger.LogInformation($"[{agent.Name}] {lastDialog.Role}: {lastDialog.Content}"); @@ -67,7 +68,8 @@ public partial class ConversationService agent, wholeDialogs, onMessageReceived, - onFunctionExecuting); + onFunctionExecuting, + onFunctionExecuted); return result; } diff --git a/src/Infrastructure/BotSharp.Core/Functions/RouteToAgentFn.cs b/src/Infrastructure/BotSharp.Core/Functions/RouteToAgentFn.cs new file mode 100644 index 00000000..c8cd625d --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Functions/RouteToAgentFn.cs @@ -0,0 +1,36 @@ +using BotSharp.Abstraction.Agents.Models; +using BotSharp.Abstraction.Conversations.Models; +using BotSharp.Abstraction.Functions; +using BotSharp.Abstraction.Functions.Models; + +namespace BotSharp.Core.Functions; + +public class RouteToAgentFn : IFunctionCallback +{ + public string Name => "route_to_agent"; + private readonly IServiceProvider _services; + + public RouteToAgentFn(IServiceProvider services) + { + _services = services; + } + + public async Task Execute(RoleDialogModel message) + { + var args = JsonSerializer.Deserialize(message.FunctionArgs); + + if (string.IsNullOrEmpty(args.AgentId)) + { + var result = new FunctionExecutionValidationResult("false", "agent_id can't be parsed."); + message.ExecutionResult = JsonSerializer.Serialize(result); + } + else + { + var result = new FunctionExecutionValidationResult("true"); + message.ExecutionResult = JsonSerializer.Serialize(result); + message.CurrentAgentId = args.AgentId; + } + + return true; + } +} diff --git a/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs b/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs index 516b906f..60bc3d3d 100644 --- a/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs +++ b/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs @@ -3,7 +3,6 @@ using Microsoft.Extensions.Configuration; using System.Drawing; using System.IO; using System.Reflection; -using Console = Colorful.Console; namespace BotSharp.Core.Plugins; diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index dab95e50..e0d60c36 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -1,8 +1,6 @@ using BotSharp.Abstraction.ApiAdapters; using BotSharp.Abstraction.Conversations.Models; using BotSharp.OpenAPI.ViewModels.Conversations; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; namespace BotSharp.OpenAPI.Controllers; @@ -52,9 +50,17 @@ public class ConversationController : ControllerBase, IApiAdapter await conv.SendMessage(agentId, conversationId, new RoleDialogModel("user", input.Text), async msg => - stackMsg.Add(msg), - async fn - => await Task.CompletedTask); + { + stackMsg.Add(msg); + }, + async fnExecuting => + { + + }, + async fnExecuted => + { + response.Json = JsonSerializer.Deserialize(fnExecuted.ExecutionResult); + }); response.Text = string.Join("\r\n", stackMsg.Select(x => x.Content)); return response; diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/MessageResponseModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/MessageResponseModel.cs index b6a6e1e4..79345773 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/MessageResponseModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/MessageResponseModel.cs @@ -3,4 +3,5 @@ namespace BotSharp.OpenAPI.ViewModels.Conversations; public class MessageResponseModel { public string Text { get; set; } + public object Json { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.ChatbotUI/ChatbotUiController.cs b/src/Plugins/BotSharp.Plugin.ChatbotUI/ChatbotUiController.cs index 84c8e309..2f7fa97e 100644 --- a/src/Plugins/BotSharp.Plugin.ChatbotUI/ChatbotUiController.cs +++ b/src/Plugins/BotSharp.Plugin.ChatbotUI/ChatbotUiController.cs @@ -75,6 +75,8 @@ public class ChatbotUiController : ControllerBase, IApiAdapter async msg => await OnChunkReceived(outputStream, msg), async fn + => await Task.CompletedTask, + async fn => await Task.CompletedTask); await OnEventCompleted(outputStream); diff --git a/src/Plugins/BotSharp.Plugin.MetaMessenger/Controllers/WebhookController.cs b/src/Plugins/BotSharp.Plugin.MetaMessenger/Controllers/WebhookController.cs index 2e45f60b..138788b7 100644 --- a/src/Plugins/BotSharp.Plugin.MetaMessenger/Controllers/WebhookController.cs +++ b/src/Plugins/BotSharp.Plugin.MetaMessenger/Controllers/WebhookController.cs @@ -105,7 +105,7 @@ public class WebhookController : ControllerBase } content = msg.Content; - }, async fn => + }, async functionExecuting => { /*await messenger.SendMessage(setting.ApiVersion, setting.PageId, new SendingMessageRequest { @@ -113,13 +113,15 @@ public class WebhookController : ControllerBase Recipient = JsonSerializer.Serialize(new { Id = sessionId }, jsonOpt), Message = JsonSerializer.Serialize(new { Text = "I'm pulling the relevent information, please wait a second ..." }, jsonOpt) });*/ - - await messenger.SendMessage(setting.ApiVersion, setting.PageId, new SendingMessageRequest + }, async functionExecuted => + { + // Render structured data + /*await messenger.SendMessage(setting.ApiVersion, setting.PageId, new SendingMessageRequest { AccessToken = setting.PageAccessToken, Recipient = JsonSerializer.Serialize(new { Id = senderId }, jsonOpt), SenderAction = SenderActionEnum.TypingOn - }); + });*/ }); // Response to user diff --git a/src/Plugins/BotSharp.Plugin.WeChat/WeChatBackgroundService.cs b/src/Plugins/BotSharp.Plugin.WeChat/WeChatBackgroundService.cs index 5823e813..ac0dab42 100644 --- a/src/Plugins/BotSharp.Plugin.WeChat/WeChatBackgroundService.cs +++ b/src/Plugins/BotSharp.Plugin.WeChat/WeChatBackgroundService.cs @@ -59,7 +59,10 @@ namespace BotSharp.Plugin.WeChat var result = await conversationService.SendMessage(AgentId, latestConversationId, new RoleDialogModel("user", message), async msg => { await ReplyTextMessageAsync(openid, msg.Content); - }, async fn => + }, async functionExecuting => + { + + }, async functionExecuted => { });