From 975b5720c7f2f116735b45ae7cd50446a7da848b Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 25 Oct 2023 13:02:50 -0500 Subject: [PATCH 01/14] add impact in agent functions --- .../BotSharp.Abstraction/Functions/Models/FunctionDef.cs | 1 + .../Models/FunctionDefMongoElement.cs | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionDef.cs b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionDef.cs index 153fb473..d89620a9 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionDef.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionDef.cs @@ -4,6 +4,7 @@ public class FunctionDef { public string Name { get; set; } public string Description { get; set; } + public string? Impact { get; set; } public FunctionParametersDef Parameters { get; set; } = new FunctionParametersDef(); public override string ToString() diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/FunctionDefMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/FunctionDefMongoElement.cs index ec9699c0..08caa2a4 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/FunctionDefMongoElement.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/FunctionDefMongoElement.cs @@ -7,6 +7,7 @@ public class FunctionDefMongoElement { public string Name { get; set; } public string Description { get; set; } + public string? Impact { get; set; } public FunctionParametersDefMongoElement Parameters { get; set; } = new FunctionParametersDefMongoElement(); public FunctionDefMongoElement() @@ -20,6 +21,7 @@ public class FunctionDefMongoElement { Name = function.Name, Description = function.Description, + Impact = function.Impact, Parameters = new FunctionParametersDefMongoElement { Type = function.Parameters.Type, @@ -35,6 +37,7 @@ public class FunctionDefMongoElement { Name = mongoFunction.Name, Description = mongoFunction.Description, + Impact = mongoFunction.Impact, Parameters = new FunctionParametersDef { Type = mongoFunction.Parameters.Type, From 26b738ef6468f60fb249c3fe5d37e4b99f0792ff Mon Sep 17 00:00:00 2001 From: hchen Date: Wed, 25 Oct 2023 14:44:27 -0500 Subject: [PATCH 02/14] Populate states for ResponseTemplateService --- .../BotSharp.Core/Templating/ResponseTemplateService.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Infrastructure/BotSharp.Core/Templating/ResponseTemplateService.cs b/src/Infrastructure/BotSharp.Core/Templating/ResponseTemplateService.cs index cb536e0e..6bde9ce6 100644 --- a/src/Infrastructure/BotSharp.Core/Templating/ResponseTemplateService.cs +++ b/src/Infrastructure/BotSharp.Core/Templating/ResponseTemplateService.cs @@ -35,6 +35,10 @@ public class ResponseTemplateService : IResponseTemplateService // Convert args and execute data to dictionary var dict = new Dictionary(); + // Populate states + var state = _services.GetRequiredService(); + state.GetStates().Select(x => dict[x.Key] = x.Value).ToList(); + if (message.FunctionArgs != null) { ExtractArgs(JsonSerializer.Deserialize(message.FunctionArgs), dict); From 6f7f143e7d658313ed1e56ea4a9a188d1f44383f Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 25 Oct 2023 15:09:21 -0500 Subject: [PATCH 03/14] add impact --- .../BotSharp.Core/Repository/FileRepository.cs | 9 +++++++-- .../Models/AgentResponseMongoElement.cs | 1 + .../Models/AgentTemplateMongoElement.cs | 1 + .../Models/FunctionDefMongoElement.cs | 1 + .../Models/RoutingRuleMongoElement.cs | 1 + src/Plugins/BotSharp.Plugin.MongoStorage/Using.cs | 3 ++- 6 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs index 4d862e7a..f873a827 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs @@ -5,7 +5,6 @@ using BotSharp.Abstraction.Users.Models; using BotSharp.Abstraction.Agents.Models; using MongoDB.Driver; using BotSharp.Abstraction.Routing.Models; -using Amazon.Util; namespace BotSharp.Core.Repository; @@ -380,7 +379,13 @@ public class FileRepository : IBotSharpRepository } var functionText = JsonSerializer.Serialize(functions, _options); - File.WriteAllText(functionFile, functionText); + + using (var sw = File.CreateText(functionFile)) + { + sw.Write(functionText); + } + + //File.WriteAllText(functionFile, functionText); } private void UpdateAgentTemplates(string agentId, List templates) diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentResponseMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentResponseMongoElement.cs index ee556549..26ddebbc 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentResponseMongoElement.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentResponseMongoElement.cs @@ -2,6 +2,7 @@ using BotSharp.Abstraction.Agents.Models; namespace BotSharp.Plugin.MongoStorage.Models; +[BsonIgnoreExtraElements] public class AgentResponseMongoElement { public string Prefix { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateMongoElement.cs index 719a11d9..847ec5c9 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateMongoElement.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateMongoElement.cs @@ -2,6 +2,7 @@ using BotSharp.Abstraction.Agents.Models; namespace BotSharp.Plugin.MongoStorage.Models; +[BsonIgnoreExtraElements] public class AgentTemplateMongoElement { public string Name { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/FunctionDefMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/FunctionDefMongoElement.cs index 08caa2a4..5910661f 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/FunctionDefMongoElement.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/FunctionDefMongoElement.cs @@ -3,6 +3,7 @@ using System.Text.Json; namespace BotSharp.Plugin.MongoStorage.Models; +[BsonIgnoreExtraElements] public class FunctionDefMongoElement { public string Name { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/RoutingRuleMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/RoutingRuleMongoElement.cs index 9f644c68..1a40b0f7 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/RoutingRuleMongoElement.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/RoutingRuleMongoElement.cs @@ -2,6 +2,7 @@ using BotSharp.Abstraction.Routing.Models; namespace BotSharp.Plugin.MongoStorage.Models; +[BsonIgnoreExtraElements] public class RoutingRuleMongoElement { public string Field { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Using.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Using.cs index 5debd75f..ca13f204 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Using.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Using.cs @@ -10,4 +10,5 @@ global using BotSharp.Abstraction.Plugins; global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; global using MongoDB.Bson; -global using MongoDB.Driver; \ No newline at end of file +global using MongoDB.Driver; +global using MongoDB.Bson.Serialization.Attributes; \ No newline at end of file From 2e3fbc98a97f6b6e146451bd20462b3513d09004 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 25 Oct 2023 15:11:15 -0500 Subject: [PATCH 04/14] remove test code --- .../BotSharp.Core/Repository/FileRepository.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs index f873a827..db801cd5 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs @@ -379,13 +379,7 @@ public class FileRepository : IBotSharpRepository } var functionText = JsonSerializer.Serialize(functions, _options); - - using (var sw = File.CreateText(functionFile)) - { - sw.Write(functionText); - } - - //File.WriteAllText(functionFile, functionText); + File.WriteAllText(functionFile, functionText); } private void UpdateAgentTemplates(string agentId, List templates) From 8cca7361b2cd89507f477afa86c27dc915d2f8aa Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 25 Oct 2023 15:56:50 -0500 Subject: [PATCH 05/14] refine agent update functions --- .../BotSharp.Core/Repository/FileRepository.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs index db801cd5..a1779c34 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs @@ -372,13 +372,7 @@ public class FileRepository : IBotSharpRepository var functionFile = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, "functions.json"); - var functions = new List(); - foreach (var function in inputFunctions) - { - functions.Add(JsonSerializer.Serialize(function, _options)); - } - - var functionText = JsonSerializer.Serialize(functions, _options); + var functionText = JsonSerializer.Serialize(inputFunctions, _options); File.WriteAllText(functionFile, functionText); } From 4041ca8de7239dabbc4e809a8a53970edd654361 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 25 Oct 2023 16:40:36 -0500 Subject: [PATCH 06/14] remove code --- .../BotSharp.Core/Conversations/Services/ConversationService.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs index 8f3a865e..efcb9cf9 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs @@ -54,8 +54,6 @@ public partial class ConversationService : IConversationService public async Task NewConversation(Conversation sess) { var db = _services.GetRequiredService(); - var dbSettings = _services.GetRequiredService(); - var conversationSettings = _services.GetRequiredService(); var user = db.GetUserByExternalId(_user.Id); var foundUserId = user?.Id ?? string.Empty; From d7ef99d034c055f16f1a6fa84cc2c0768b3895e8 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Wed, 25 Oct 2023 19:30:23 -0500 Subject: [PATCH 07/14] Render assistant role as specific agent name. --- .../Routing/RoutingService.GetNextInstruction.cs | 11 ++++++++++- .../instruction.liquid | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetNextInstruction.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetNextInstruction.cs index c08230ad..9cf2726a 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetNextInstruction.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetNextInstruction.cs @@ -56,15 +56,24 @@ public partial class RoutingService model: _settings.Model); int retryCount = 0; + var agentService = _services.GetRequiredService(); while (retryCount < 3) { try { var conversation = ""; + foreach (var dialog in _dialogs.TakeLast(20)) { - conversation += $"{dialog.Role}: {dialog.Content}\r\n"; + var role = dialog.Role; + if (role != AgentRole.User) + { + var agent = await agentService.GetAgent(dialog.CurrentAgentId); + role = agent.Name; + } + + conversation += $"{role}: {dialog.Content}\r\n"; } content = $"{conversation}\r\n###\r\n{content}"; diff --git a/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid b/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid index 1543518d..a6d15308 100644 --- a/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid +++ b/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid @@ -3,7 +3,7 @@ You're {{router.name}} ({{router.description}}). Follow these steps to handle us 2. Select a appropriate function from [FUNCTIONS]. 3. Determine which agent is suitable to handle this conversation. 4. Re-think about the selected function or agent is the best choice. -5. For agent required arguments, leave it blank if user doesn't provide it. +5. For agent required arguments, leave it as blank object if user doesn't provide it. [FUNCTIONS] {% for handler in routing_handlers %} From 2a52e30ba81ed7b985e85711f2a7b6714ce25c54 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Wed, 25 Oct 2023 22:05:12 -0500 Subject: [PATCH 08/14] change description of human_intervention_needed. --- .../Routing/Handlers/HumanInterventionNeededHandler.cs | 4 ++-- .../templates/next_step_prompt.liquid | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs index 3df04f69..62247445 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs @@ -9,13 +9,13 @@ public class HumanInterventionNeededHandler : RoutingHandlerBase, IRoutingHandle { public string Name => "human_intervention_needed"; - public string Description => "Reach out to a real human or customer representative."; + public string Description => "Reach out to human being, customer service or customer representative."; private readonly RoutingSettings _settings; public List Parameters => new List { - new NameDesc("reason", "why need customer service representative (human being)"), + new NameDesc("reason", "why need customer service"), new NameDesc("response", "response content to user") }; diff --git a/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/next_step_prompt.liquid b/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/next_step_prompt.liquid index 24132c96..b61e4b34 100644 --- a/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/next_step_prompt.liquid +++ b/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/next_step_prompt.liquid @@ -1 +1 @@ -What is the next step based on the CONVERSATION? Response must be in appropriate JSON format. \ No newline at end of file +What is the next step based on the CONVERSATION? Response must be in appropriate JSON format. Route to the latest agent as much as possible. \ No newline at end of file From 012158470f8bcba457aefa5cb6b55a4570c598f5 Mon Sep 17 00:00:00 2001 From: Haiping Chen <101423@smsassist.com> Date: Thu, 26 Oct 2023 09:18:14 -0500 Subject: [PATCH 09/14] PerInstanceCache = true --- .../Agents/Services/AgentService.GetAgents.cs | 8 ++------ .../BotSharp.Core/Repository/FileRepository.cs | 3 --- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs index 6b749b3b..5912b1e0 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs @@ -4,18 +4,14 @@ namespace BotSharp.Core.Agents.Services; public partial class AgentService { -#if !DEBUG - [MemoryCache(10 * 60)] -#endif + [MemoryCache(10 * 60, PerInstanceCache = true)] public async Task> GetAgents(bool? allowRouting = null) { var agents = _db.GetAgents(allowRouting: allowRouting); return await Task.FromResult(agents); } -#if !DEBUG - [MemoryCache(10 * 60)] -#endif + [MemoryCache(10 * 60, PerInstanceCache = true)] public async Task GetAgent(string id) { var profile = _db.GetAgent(id); diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs index a1779c34..8aed5989 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs @@ -486,9 +486,6 @@ public class FileRepository : IBotSharpRepository return responses; } -#if !DEBUG - [MemoryCache(10 * 60)] -#endif public Agent? GetAgent(string agentId) { var agentDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir); From 2cd3dfcd2978517146df86c2620c7ca6dd6bbb48 Mon Sep 17 00:00:00 2001 From: hchen Date: Thu, 26 Oct 2023 13:52:44 -0500 Subject: [PATCH 10/14] JsonContent --- .../Utilities/StringExtensions.cs | 13 +++++++++++++ .../Routing/RoutingService.GetNextInstruction.cs | 4 +--- .../templates/next_step_prompt.liquid | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Utilities/StringExtensions.cs b/src/Infrastructure/BotSharp.Abstraction/Utilities/StringExtensions.cs index d62b9ec0..17232920 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Utilities/StringExtensions.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Utilities/StringExtensions.cs @@ -1,3 +1,4 @@ +using System.Text.Json; using System.Text.RegularExpressions; namespace BotSharp.Abstraction.Utilities; @@ -27,4 +28,16 @@ public static class StringExtensions { return str1.Equals(str2, option); } + + public static string JsonContent(this string text) + { + var m = Regex.Match(text, @"\{(?:[^{}]|(?\{)|(?<-open>\}))+(?(open)(?!))\}"); + return m.Success ? m.Value : "{}"; + } + + public static T? JsonContent(this string text) + { + text = JsonContent(text); + return JsonSerializer.Deserialize(text); + } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetNextInstruction.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetNextInstruction.cs index 9cf2726a..10541e4c 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetNextInstruction.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetNextInstruction.cs @@ -82,9 +82,7 @@ public partial class RoutingService new RoleDialogModel(AgentRole.User, content) }); - var pattern = @"\{(?:[^{}]|(?\{)|(?<-open>\}))+(?(open)(?!))\}"; - response.Content = Regex.Match(response.Content, pattern).Value; - args = JsonSerializer.Deserialize(response.Content); + args = response.Content.JsonContent(); break; } catch (Exception ex) diff --git a/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/next_step_prompt.liquid b/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/next_step_prompt.liquid index b61e4b34..24132c96 100644 --- a/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/next_step_prompt.liquid +++ b/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/next_step_prompt.liquid @@ -1 +1 @@ -What is the next step based on the CONVERSATION? Response must be in appropriate JSON format. Route to the latest agent as much as possible. \ No newline at end of file +What is the next step based on the CONVERSATION? Response must be in appropriate JSON format. \ No newline at end of file From 7ab383aa53eb51a363deb2d1bd2537f41c692fa1 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Thu, 26 Oct 2023 19:33:34 -0500 Subject: [PATCH 11/14] Update router prompt. --- .../01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid | 2 +- .../templates/next_step_prompt.liquid | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid b/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid index a6d15308..d9c0515b 100644 --- a/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid +++ b/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid @@ -2,7 +2,7 @@ You're {{router.name}} ({{router.description}}). Follow these steps to handle us 1. Read the [CONVERSATION] content. 2. Select a appropriate function from [FUNCTIONS]. 3. Determine which agent is suitable to handle this conversation. -4. Re-think about the selected function or agent is the best choice. +4. If user wants to talk with human being, you will transfer to customer representative. 5. For agent required arguments, leave it as blank object if user doesn't provide it. [FUNCTIONS] diff --git a/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/next_step_prompt.liquid b/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/next_step_prompt.liquid index 24132c96..41bcd63d 100644 --- a/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/next_step_prompt.liquid +++ b/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/next_step_prompt.liquid @@ -1 +1 @@ -What is the next step based on the CONVERSATION? Response must be in appropriate JSON format. \ No newline at end of file +What is the next step based on the CONVERSATION? Response must be in appropriate JSON format. Route to the Agent that last handled the conversation if necessary. \ No newline at end of file From cee91acaaad512c268db6c8b59d71f6e481d3b80 Mon Sep 17 00:00:00 2001 From: Haiping Chen <101423@smsassist.com> Date: Thu, 26 Oct 2023 20:23:06 -0500 Subject: [PATCH 12/14] Fix OriginAgentId --- .../BotSharp.Abstraction/Routing/Models/RoutingContext.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingContext.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingContext.cs index 49b38274..b24c7941 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingContext.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingContext.cs @@ -18,8 +18,11 @@ public class RoutingContext /// public string IntentName { get; set; } + /// + /// Agent that can handl user original goal. + /// public string OriginAgentId - => _stack.Last(); + => _stack.Where(x => x != _setting.RouterId).Last(); public string GetCurrentAgentId() { From 492a1d70eedc0eea547ef7217fc17bac28d57721 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Fri, 27 Oct 2023 10:18:48 -0500 Subject: [PATCH 13/14] ITrackableMessage --- docs/channels/components.md | 4 +-- .../Models/IncomingMessageModel.cs | 35 +++---------------- .../Conversations/Models/RoleDialogModel.cs | 11 +++++- .../Instructs/Models/InstructResult.cs | 5 ++- .../Models/ITrackableMessage.cs | 9 +++++ .../Models/MessageConfig.cs | 32 +++++++++++++++++ .../Routing/Models/RoutingArgs.cs | 5 ++- .../BotSharp.Abstraction/Using.cs | 3 +- .../Services/ConversationService.cs | 4 +-- .../Services/ConversationStorage.cs | 20 +++-------- .../Instructs/InstructService.cs | 2 ++ .../ContinueExecuteTaskRoutingHandler.cs | 1 + .../Handlers/ConversationEndRoutingHandler.cs | 1 + .../HumanInterventionNeededHandler.cs | 1 + .../InterruptTaskExecutionRoutingHandler.cs | 1 + .../Handlers/ResponseToUserRoutingHandler.cs | 1 + .../RetrieveDataFromAgentRoutingHandler.cs | 1 + .../Handlers/RouteToAgentRoutingHandler.cs | 2 ++ .../Routing/Handlers/TaskEndRoutingHandler.cs | 1 + .../BotSharp.Core/Routing/RoutingService.cs | 6 ++-- .../Controllers/ConversationController.cs | 17 +++++---- .../Conversations/MessageResponseModel.cs | 5 ++- 22 files changed, 103 insertions(+), 64 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Models/ITrackableMessage.cs create mode 100644 src/Infrastructure/BotSharp.Abstraction/Models/MessageConfig.cs diff --git a/docs/channels/components.md b/docs/channels/components.md index 3754a1c3..89ab8cb4 100644 --- a/docs/channels/components.md +++ b/docs/channels/components.md @@ -1,6 +1,6 @@ # Messaging Components -Conversations are a lot more than simple text messages when you are building a AI chatbot. In addition to text, the `BotSharp`` allows you to send rich-media, like audio, video, and images, and provides a set of structured messaging options in the form of message templates, quick replies, buttons and more. The UI rendering program can render components according to the returned data format. +Conversations are a lot more than simple text messages when you are building a AI chatbot. In addition to text, the `BotSharp` allows you to send rich-media, like audio, video, and images, and provides a set of structured messaging options in the form of message templates, quick replies, buttons and more. The UI rendering program can render components according to the returned data format. ## Text Messages @@ -75,7 +75,7 @@ Message templates are structured message formats used for various purposes to pr ... } ] - } + } } } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/IncomingMessageModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/IncomingMessageModel.cs index 98381559..903e5d0b 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/IncomingMessageModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/IncomingMessageModel.cs @@ -1,36 +1,9 @@ +using BotSharp.Abstraction.Models; + namespace BotSharp.Abstraction.Conversations.Models; -public class IncomingMessageModel +public class IncomingMessageModel : MessageConfig { public string Text { get; set; } = string.Empty; - - public virtual string Channel { get; set; } = string.Empty; - - /// - /// Completion Provider - /// - [JsonPropertyName("provider")] - public virtual string? Provider { get; set; } = null; - - /// - /// Model name - /// - [JsonPropertyName("model")] - public virtual string? Model { get; set; } = null; - - /// - /// The sampling temperature to use that controls the apparent creativity of generated completions. - /// - public float Temperature { get; set; } = 0.5f; - - /// - /// An alternative value to Temperature, called nucleus sampling, that causes - /// the model to consider the results of the tokens with probability mass. - /// - public float SamplingFactor { get; set; } = 0.5f; - - /// - /// Conversation states from input - /// - public List States { get; set; } = new List(); + public virtual string Channel { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs index ae798160..6b8624f9 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs @@ -1,7 +1,11 @@ +using BotSharp.Abstraction.Models; + namespace BotSharp.Abstraction.Conversations.Models; -public class RoleDialogModel +public class RoleDialogModel : ITrackableMessage { + public string MessageId { get; set; } + /// /// user, system, assistant, function /// @@ -34,10 +38,15 @@ public class RoleDialogModel [JsonIgnore(Condition = JsonIgnoreCondition.Always)] public bool StopCompletion { get; set; } + private RoleDialogModel() + { + } + public RoleDialogModel(string role, string text) { Role = role; Content = text; + MessageId = Guid.NewGuid().ToString(); } public override string ToString() diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/InstructResult.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/InstructResult.cs index 10635152..67bd2039 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/InstructResult.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/InstructResult.cs @@ -1,7 +1,10 @@ +using BotSharp.Abstraction.Models; + namespace BotSharp.Abstraction.Instructs.Models; -public class InstructResult +public class InstructResult : ITrackableMessage { + public string MessageId { get; set; } public string Text { get; set; } public object Data { get; set; } public ConversationState States { get; set; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Models/ITrackableMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Models/ITrackableMessage.cs new file mode 100644 index 00000000..5756cfd6 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Models/ITrackableMessage.cs @@ -0,0 +1,9 @@ +namespace BotSharp.Abstraction.Models; + +/// +/// Define a message ID to extend message-level applications, such as model fees, token usage, and data collection +/// +public interface ITrackableMessage +{ + string MessageId { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Models/MessageConfig.cs b/src/Infrastructure/BotSharp.Abstraction/Models/MessageConfig.cs new file mode 100644 index 00000000..95d72492 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Models/MessageConfig.cs @@ -0,0 +1,32 @@ +namespace BotSharp.Abstraction.Models; + +public class MessageConfig +{ + /// + /// Completion Provider + /// + [JsonPropertyName("provider")] + public virtual string? Provider { get; set; } = null; + + /// + /// Model name + /// + [JsonPropertyName("model")] + public virtual string? Model { get; set; } = null; + + /// + /// The sampling temperature to use that controls the apparent creativity of generated completions. + /// + public float Temperature { get; set; } = 0.5f; + + /// + /// An alternative value to Temperature, called nucleus sampling, that causes + /// the model to consider the results of the tokens with probability mass. + /// + public float SamplingFactor { get; set; } = 0.5f; + + /// + /// Conversation states from input + /// + public List States { get; set; } = new List(); +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs index bca8018e..96e133d9 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs @@ -1,7 +1,10 @@ namespace BotSharp.Abstraction.Routing.Models; -public class RoutingArgs +public class RoutingArgs : ITrackableMessage { + [JsonPropertyName("message_id")] + public string MessageId { get; set; } + [JsonPropertyName("function")] public string Function { get; set; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Using.cs b/src/Infrastructure/BotSharp.Abstraction/Using.cs index 87d42791..39ececfe 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Using.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Using.cs @@ -7,4 +7,5 @@ global using System.ComponentModel.DataAnnotations; global using System.Text.Json.Serialization; global using BotSharp.Abstraction.Agents.Models; global using BotSharp.Abstraction.Conversations.Models; -global using BotSharp.Abstraction.Agents.Enums; \ No newline at end of file +global using BotSharp.Abstraction.Agents.Enums; +global using BotSharp.Abstraction.Models; \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs index efcb9cf9..db6fc90a 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs @@ -78,11 +78,11 @@ public partial class ConversationService : IConversationService throw new NotImplementedException(); } - public List GetDialogHistory(int lastCount = 20) + public List GetDialogHistory(int lastCount = 50) { var dialogs = _storage.GetDialogs(_conversationId); return dialogs - .Where(x => x.CreatedAt > DateTime.UtcNow.AddHours(-8)) + .Where(x => x.CreatedAt > DateTime.UtcNow.AddHours(-24)) .TakeLast(lastCount) .ToList(); } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs index 2d8af0ad..3d8b3085 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs @@ -6,19 +6,13 @@ namespace BotSharp.Core.Conversations.Services; public class ConversationStorage : IConversationStorage { private readonly BotSharpDatabaseSettings _dbSettings; - private readonly AgentSettings _agentSettings; private readonly IServiceProvider _services; - private readonly IUserIdentity _user; public ConversationStorage( BotSharpDatabaseSettings dbSettings, - AgentSettings agentSettings, - IServiceProvider services, - IUserIdentity user) + IServiceProvider services) { _dbSettings = dbSettings; - _agentSettings = agentSettings; _services = services; - _user = user; } public void Append(string conversationId, RoleDialogModel dialog) @@ -32,7 +26,7 @@ public class ConversationStorage : IConversationStorage { var args = dialog.FunctionArgs.Replace("\r", " ").Replace("\n", " ").Trim(); - sb.AppendLine($"{dialog.CreatedAt}|{dialog.Role}|{agentId}|{dialog.FunctionName}|{args}"); + sb.AppendLine($"{dialog.CreatedAt}|{dialog.Role}|{agentId}|{dialog.MessageId}"); var content = dialog.Content; content = content.Replace("\r", " ").Replace("\n", " ").Trim(); @@ -44,9 +38,7 @@ public class ConversationStorage : IConversationStorage } else { - var agentName = db.GetAgent(agentId)?.Name; - - sb.AppendLine($"{dialog.CreatedAt}|{dialog.Role}|{agentId}|{agentName}|"); + sb.AppendLine($"{dialog.CreatedAt}|{dialog.Role}|{agentId}|{dialog.MessageId}"); var content = dialog.Content.Replace("\r", " ").Replace("\n", " ").Trim(); if (string.IsNullOrEmpty(content)) { @@ -73,15 +65,13 @@ public class ConversationStorage : IConversationStorage var createdAt = DateTime.Parse(meta.Split('|')[0]); var role = meta.Split('|')[1]; var currentAgentId = meta.Split('|')[2]; - var funcName = meta.Split('|')[3]; - var funcArgs= meta.Split('|')[4]; + var messageId = meta.Split('|')[3]; var text = dialog.Substring(4); results.Add(new RoleDialogModel(role, text) { CurrentAgentId = currentAgentId, - FunctionName = funcName, - FunctionArgs = funcArgs, + MessageId = messageId, Content = text, CreatedAt = createdAt }); diff --git a/src/Infrastructure/BotSharp.Core/Instructs/InstructService.cs b/src/Infrastructure/BotSharp.Core/Instructs/InstructService.cs index bb328753..8ee49eac 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/InstructService.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/InstructService.cs @@ -33,6 +33,7 @@ public partial class InstructService : IInstructService { return new InstructResult { + MessageId = message.MessageId, Text = message.Content }; } @@ -42,6 +43,7 @@ public partial class InstructService : IInstructService var result = await completer.GetCompletion(agent.Instruction); var response = new InstructResult { + MessageId = message.MessageId, Text = result }; diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ContinueExecuteTaskRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ContinueExecuteTaskRoutingHandler.cs index 984651f0..8e167855 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ContinueExecuteTaskRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ContinueExecuteTaskRoutingHandler.cs @@ -33,6 +33,7 @@ public class ContinueExecuteTaskRoutingHandler : RoutingHandlerBase, IRoutingHan var result = new RoleDialogModel(AgentRole.Function, inst.Question) { + MessageId = inst.MessageId, FunctionName = inst.Function, FunctionArgs = JsonSerializer.Serialize(inst.Arguments), CurrentAgentId = record.Id diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ConversationEndRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ConversationEndRoutingHandler.cs index 44563f42..69a5c9f7 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ConversationEndRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ConversationEndRoutingHandler.cs @@ -28,6 +28,7 @@ public class ConversationEndRoutingHandler : RoutingHandlerBase, IRoutingHandler { var result = new RoleDialogModel(AgentRole.Assistant, inst.Response) { + MessageId = inst.MessageId, CurrentAgentId = _settings.RouterId, FunctionName = inst.Function, Data = inst diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs index 62247445..1ebf9c43 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs @@ -29,6 +29,7 @@ public class HumanInterventionNeededHandler : RoutingHandlerBase, IRoutingHandle { var result = new RoleDialogModel(AgentRole.Assistant, inst.Response) { + MessageId = inst.MessageId, CurrentAgentId = _settings.RouterId, FunctionName = inst.Function, Data = inst diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/InterruptTaskExecutionRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/InterruptTaskExecutionRoutingHandler.cs index 096f9d61..c62102e2 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/InterruptTaskExecutionRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/InterruptTaskExecutionRoutingHandler.cs @@ -28,6 +28,7 @@ public class InterruptTaskExecutionRoutingHandler : RoutingHandlerBase, IRouting { var result = new RoleDialogModel(AgentRole.User, inst.Reason) { + MessageId = inst.MessageId, FunctionName = inst.Function, StopCompletion = true }; diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs index ec1c5bcf..9da370b8 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs @@ -28,6 +28,7 @@ public class ResponseToUserRoutingHandler : RoutingHandlerBase, IRoutingHandler { var result = new RoleDialogModel(AgentRole.Assistant, inst.Response) { + MessageId = inst.MessageId, CurrentAgentId = _settings.RouterId, FunctionName = inst.Function, Data = inst, diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs index d4492d2b..04d78bdb 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs @@ -45,6 +45,7 @@ public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase, IRoutingH /*_dialogs.Add(new RoleDialogModel(AgentRole.Function, inst.Parameters.Answer) { + MessageId = inst.MessageId, FunctionName = inst.Function, FunctionArgs = JsonSerializer.Serialize(inst.Parameters.Arguments), ExecutionResult = inst.Parameters.Answer, diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs index d4fba5ff..52766548 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs @@ -35,6 +35,7 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler var function = _services.GetServices().FirstOrDefault(x => x.Name == inst.Function); var message = new RoleDialogModel(AgentRole.Function, inst.Question) { + MessageId = inst.MessageId, FunctionName = inst.Function, FunctionArgs = JsonSerializer.Serialize(inst), CurrentAgentId = context.GetCurrentAgentId(), @@ -46,6 +47,7 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler // Keep last message data for debug result.Data = result.Data ?? message.Data; result.FunctionName = result.FunctionName ?? message.FunctionName; + return result; } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/TaskEndRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/TaskEndRoutingHandler.cs index 9190bba5..ba7f2e2c 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/TaskEndRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/TaskEndRoutingHandler.cs @@ -27,6 +27,7 @@ public class TaskEndRoutingHandler : RoutingHandlerBase, IRoutingHandler { var result = new RoleDialogModel(AgentRole.Assistant, inst.Response) { + MessageId = inst.MessageId, CurrentAgentId = _settings.RouterId, FunctionName = inst.Function, Data = inst diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index 9159434d..ba4f8f1b 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -76,7 +76,7 @@ public partial class RoutingService : IRoutingService CurrentAgentId = router.Id }; - var message = Dialogs.Last().Content; + var inputMsg = Dialogs.Last(); var handlers = _services.GetServices(); @@ -87,7 +87,8 @@ public partial class RoutingService : IRoutingService loopCount++; var inst = await GetNextInstruction(); - inst.Question = inst.Question ?? message; + inst.MessageId = inputMsg.MessageId; + inst.Question = inst.Question ?? inputMsg.Content; var handler = handlers.FirstOrDefault(x => x.Name == inst.Function); if (handler == null) @@ -99,6 +100,7 @@ public partial class RoutingService : IRoutingService handler.SetDialogs(Dialogs); result = await handler.Handle(this, inst); + result.MessageId = inputMsg.MessageId; stop = !_settings.EnableReasoning; } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index 3ca6ca9c..4fd4be21 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -1,5 +1,6 @@ using BotSharp.Abstraction.ApiAdapters; using BotSharp.Abstraction.Conversations.Models; +using BotSharp.Abstraction.Models; using BotSharp.OpenAPI.ViewModels.Conversations; namespace BotSharp.OpenAPI.Controllers; @@ -19,15 +20,17 @@ public class ConversationController : ControllerBase, IApiAdapter } [HttpPost("/conversation/{agentId}")] - public async Task NewConversation([FromRoute] string agentId) + public async Task NewConversation([FromRoute] string agentId, [FromBody] MessageConfig config) { var service = _services.GetRequiredService(); - var sess = new Conversation + var conv = new Conversation { AgentId = agentId }; - sess = await service.NewConversation(sess); - return ConversationViewModel.FromSession(sess); + conv = await service.NewConversation(conv); + config.States.ForEach(x => conv.States[x.Split('=')[0]] = x.Split('=')[1]); + + return ConversationViewModel.FromSession(conv); } [HttpDelete("/conversation/{agentId}/{conversationId}")] @@ -51,9 +54,8 @@ public class ConversationController : ControllerBase, IApiAdapter var response = new MessageResponseModel(); var stackMsg = new List(); - - await conv.SendMessage(agentId, - new RoleDialogModel("user", input.Text), + var inputMsg = new RoleDialogModel("user", input.Text); + await conv.SendMessage(agentId, inputMsg, async msg => { stackMsg.Add(msg); @@ -71,6 +73,7 @@ public class ConversationController : ControllerBase, IApiAdapter response.Text = string.Join("\r\n", stackMsg.Select(x => x.Content)); response.Data = response.Data ?? stackMsg.Last().Data; response.Function = stackMsg.Last().FunctionName; + response.MessageId = inputMsg.MessageId; return response; } diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/MessageResponseModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/MessageResponseModel.cs index 5f7241c0..dc88faaf 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/MessageResponseModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/MessageResponseModel.cs @@ -1,7 +1,10 @@ +using BotSharp.Abstraction.Models; + namespace BotSharp.OpenAPI.ViewModels.Conversations; -public class MessageResponseModel +public class MessageResponseModel : ITrackableMessage { + public string MessageId { get; set; } public string Text { get; set; } public string Function { get; set; } public object Data { get; set; } From 933fc3febdd52143d6aba1bb8ee605fc60aa5e75 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Fri, 27 Oct 2023 15:36:26 -0500 Subject: [PATCH 14/14] Share one message instance through the conversation. --- .../Conversations/Models/RoleDialogModel.cs | 3 ++ .../Routing/IRoutingHandler.cs | 3 +- .../Routing/IRoutingService.cs | 6 ++-- .../Routing/Models/RoutingArgs.cs | 5 +-- .../ConversationService.SendMessage.cs | 30 ++++++++-------- .../Routing/Functions/RouteToAgentFn.cs | 3 -- .../ContinueExecuteTaskRoutingHandler.cs | 14 +++----- .../Handlers/ConversationEndRoutingHandler.cs | 16 ++++----- .../HumanInterventionNeededHandler.cs | 19 ++++------ .../InterruptTaskExecutionRoutingHandler.cs | 12 +++---- .../Handlers/ResponseToUserRoutingHandler.cs | 15 +++----- .../RetrieveDataFromAgentRoutingHandler.cs | 10 +++--- .../Handlers/RouteToAgentRoutingHandler.cs | 19 +++------- .../Routing/Handlers/TaskEndRoutingHandler.cs | 14 ++------ .../RoutingService.GetNextInstruction.cs | 6 ++-- .../Routing/RoutingService.InvokeAgent.cs | 36 +++++++++++-------- .../BotSharp.Core/Routing/RoutingService.cs | 36 +++++++++---------- .../Controllers/ConversationController.cs | 13 ++++--- .../Conversations/MessageResponseModel.cs | 3 ++ src/WebStarter/WebStarter.csproj | 1 + .../instruction.liquid | 2 +- .../templates/next_step_prompt.liquid | 5 ++- .../Functions/GetPizzaPricesFn.cs | 2 ++ 23 files changed, 116 insertions(+), 157 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs index 6b8624f9..f08e5a66 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Models; namespace BotSharp.Abstraction.Conversations.Models; @@ -38,6 +39,8 @@ public class RoleDialogModel : ITrackableMessage [JsonIgnore(Condition = JsonIgnoreCondition.Always)] public bool StopCompletion { get; set; } + public FunctionCallFromLlm Instruction { get; set; } + private RoleDialogModel() { } diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingHandler.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingHandler.cs index d86c6298..6c8ae94e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingHandler.cs @@ -1,5 +1,4 @@ using BotSharp.Abstraction.Functions.Models; -using BotSharp.Abstraction.Models; namespace BotSharp.Abstraction.Routing; @@ -19,5 +18,5 @@ public interface IRoutingHandler void SetDialogs(List dialogs) { } - Task Handle(IRoutingService routing, FunctionCallFromLlm inst); + Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs index 802f8f73..47744957 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs @@ -8,7 +8,7 @@ public interface IRoutingService void ResetRecursiveCounter(); void RefreshDialogs(); Task GetNextInstruction(); - Task InvokeAgent(string agentId); - Task InstructLoop(); - Task ExecuteOnce(Agent agent); + Task InvokeAgent(string agentId, RoleDialogModel message); + Task InstructLoop(RoleDialogModel message); + Task ExecuteOnce(Agent agent, RoleDialogModel message); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs index 96e133d9..bca8018e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs @@ -1,10 +1,7 @@ namespace BotSharp.Abstraction.Routing.Models; -public class RoutingArgs : ITrackableMessage +public class RoutingArgs { - [JsonPropertyName("message_id")] - public string MessageId { get; set; } - [JsonPropertyName("function")] public string Function { get; set; } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs index e80b5b1e..387a18e7 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -8,7 +8,7 @@ namespace BotSharp.Core.Conversations.Services; public partial class ConversationService { public async Task SendMessage(string agentId, - RoleDialogModel incoming, + RoleDialogModel message, Func onMessageReceived, Func onFunctionExecuting, Func onFunctionExecuted) @@ -18,16 +18,16 @@ public partial class ConversationService var agentService = _services.GetRequiredService(); Agent agent = await agentService.LoadAgent(agentId); - var message = $"Received [{agent.Name}] {incoming.Role}: {incoming.Content}"; + var content = $"Received [{agent.Name}] {message.Role}: {message.Content}"; #if DEBUG - Console.WriteLine(message, Color.OrangeRed); + Console.WriteLine(content, Color.OrangeRed); #else - _logger.LogInformation(message); + _logger.LogInformation(content); #endif - incoming.CurrentAgentId = agent.Id; + message.CurrentAgentId = agent.Id; - _storage.Append(_conversationId, incoming); + _storage.Append(_conversationId, message); var hooks = _services.GetServices().ToList(); @@ -37,13 +37,13 @@ public partial class ConversationService hook.SetAgent(agent) .SetConversation(conversation); - await hook.OnMessageReceived(incoming); + await hook.OnMessageReceived(message); // Interrupted by hook - if (incoming.StopCompletion) + if (message.StopCompletion) { - await onMessageReceived(incoming); - _storage.Append(_conversationId, incoming); + await onMessageReceived(message); + _storage.Append(_conversationId, message); return true; } } @@ -52,11 +52,11 @@ public partial class ConversationService var routing = _services.GetRequiredService(); var settings = _services.GetRequiredService(); - var response = agentId == settings.RouterId ? - await routing.InstructLoop() : - await routing.ExecuteOnce(agent); + var ret = agentId == settings.RouterId ? + await routing.InstructLoop(message) : + await routing.ExecuteOnce(agent, message); - await HandleAssistantMessage(response, onMessageReceived); + await HandleAssistantMessage(message, onMessageReceived); var statistics = _services.GetRequiredService(); statistics.PrintStatistics(); @@ -64,7 +64,7 @@ public partial class ConversationService routing.ResetRecursiveCounter(); routing.RefreshDialogs(); - return true; + return ret; } private async Task GetConversationRecord(string agentId) diff --git a/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs b/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs index dcfe47e4..fd916b12 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs @@ -66,14 +66,11 @@ public class RouteToAgentFn : IFunctionCallback else { message.CurrentAgentId = targetAgent.Id; - message.Content = $"Routing to {args.AgentName}"; } } _context.Push(message.CurrentAgentId); - // Set default execution data - message.Data = JsonSerializer.Deserialize(message.FunctionArgs); return true; } diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ContinueExecuteTaskRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ContinueExecuteTaskRoutingHandler.cs index 8e167855..1c809ef4 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ContinueExecuteTaskRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ContinueExecuteTaskRoutingHandler.cs @@ -26,19 +26,15 @@ public class ContinueExecuteTaskRoutingHandler : RoutingHandlerBase, IRoutingHan { } - public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst) + public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message) { var db = _services.GetRequiredService(); var record = db.GetAgents(inst.AgentName).FirstOrDefault(); - var result = new RoleDialogModel(AgentRole.Function, inst.Question) - { - MessageId = inst.MessageId, - FunctionName = inst.Function, - FunctionArgs = JsonSerializer.Serialize(inst.Arguments), - CurrentAgentId = record.Id - }; + message.FunctionName = inst.Function; + message.CurrentAgentId = record.Id; + message.FunctionArgs = JsonSerializer.Serialize(inst.Arguments); - return result; + return true; } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ConversationEndRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ConversationEndRoutingHandler.cs index 69a5c9f7..97eafa46 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ConversationEndRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ConversationEndRoutingHandler.cs @@ -24,15 +24,11 @@ public class ConversationEndRoutingHandler : RoutingHandlerBase, IRoutingHandler { } - public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst) + + public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message) { - var result = new RoleDialogModel(AgentRole.Assistant, inst.Response) - { - MessageId = inst.MessageId, - CurrentAgentId = _settings.RouterId, - FunctionName = inst.Function, - Data = inst - }; + message.Content = inst.Response; + message.FunctionName = inst.Function; var hooks = _services.GetServices() .OrderBy(x => x.Priority) @@ -40,9 +36,9 @@ public class ConversationEndRoutingHandler : RoutingHandlerBase, IRoutingHandler foreach (var hook in hooks) { - await hook.OnConversationEnding(result); + await hook.OnConversationEnding(message); } - return result; + return true; } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs index 1ebf9c43..671dc644 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs @@ -11,8 +11,6 @@ public class HumanInterventionNeededHandler : RoutingHandlerBase, IRoutingHandle public string Description => "Reach out to human being, customer service or customer representative."; - private readonly RoutingSettings _settings; - public List Parameters => new List { new NameDesc("reason", "why need customer service"), @@ -22,18 +20,13 @@ public class HumanInterventionNeededHandler : RoutingHandlerBase, IRoutingHandle public HumanInterventionNeededHandler(IServiceProvider services, ILogger logger, RoutingSettings settings) : base(services, logger, settings) { - _settings = settings; + } - public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst) + public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message) { - var result = new RoleDialogModel(AgentRole.Assistant, inst.Response) - { - MessageId = inst.MessageId, - CurrentAgentId = _settings.RouterId, - FunctionName = inst.Function, - Data = inst - }; + message.Role = AgentRole.Assistant; + message.Content = inst.Response; var hooks = _services.GetServices() .OrderBy(x => x.Priority) @@ -41,9 +34,9 @@ public class HumanInterventionNeededHandler : RoutingHandlerBase, IRoutingHandle foreach (var hook in hooks) { - await hook.OnHumanInterventionNeeded(result); + await hook.OnHumanInterventionNeeded(message); } - return result; + return true; } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/InterruptTaskExecutionRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/InterruptTaskExecutionRoutingHandler.cs index c62102e2..76d18be3 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/InterruptTaskExecutionRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/InterruptTaskExecutionRoutingHandler.cs @@ -24,15 +24,11 @@ public class InterruptTaskExecutionRoutingHandler : RoutingHandlerBase, IRouting { } - public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst) + public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message) { - var result = new RoleDialogModel(AgentRole.User, inst.Reason) - { - MessageId = inst.MessageId, - FunctionName = inst.Function, - StopCompletion = true - }; + message.FunctionName = inst.Function; + message.StopCompletion = true; - return result; + return true; } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs index 9da370b8..cb480e56 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs @@ -24,16 +24,11 @@ public class ResponseToUserRoutingHandler : RoutingHandlerBase, IRoutingHandler { } - public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst) + public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message) { - var result = new RoleDialogModel(AgentRole.Assistant, inst.Response) - { - MessageId = inst.MessageId, - CurrentAgentId = _settings.RouterId, - FunctionName = inst.Function, - Data = inst, - StopCompletion = true - }; - return result; + message.Content = inst.Response; + message.StopCompletion = true; + message.Role = AgentRole.Assistant; + return true; } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs index 04d78bdb..3b30c19d 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs @@ -27,14 +27,12 @@ public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase, IRoutingH { } - public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst) + public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message) { // Retrieve information from specific agent var db = _services.GetRequiredService(); var record = db.GetAgents(inst.AgentName).FirstOrDefault(); - var response = await routing.InvokeAgent(record.Id); - - inst.Response = response.Content; + var ret = await routing.InvokeAgent(record.Id, message); /*_dialogs.Add(new RoleDialogModel(AgentRole.Assistant, inst.Parameters.Question) { @@ -53,11 +51,11 @@ public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase, IRoutingH CurrentAgentId = record.Id });*/ - _router.Instruction += $"\r\n{AgentRole.Function}: {response.Content}"; + _router.Instruction += $"\r\n{AgentRole.Function}: {message.Content}"; // Got the response from agent, then send to reasoner again to make the decision // inst = await GetNextInstructionFromReasoner($"What's the next step based on user's original goal and function result?"); - return null; + return true; } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs index 52766548..daa4e734 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs @@ -28,26 +28,15 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler { } - public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst) + public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message) { var context = _services.GetRequiredService(); - var function = _services.GetServices().FirstOrDefault(x => x.Name == inst.Function); - var message = new RoleDialogModel(AgentRole.Function, inst.Question) - { - MessageId = inst.MessageId, - FunctionName = inst.Function, - FunctionArgs = JsonSerializer.Serialize(inst), - CurrentAgentId = context.GetCurrentAgentId(), - }; - + message.FunctionArgs = JsonSerializer.Serialize(inst); var ret = await function.Execute(message); - var result = await routing.InvokeAgent(context.GetCurrentAgentId()); - // Keep last message data for debug - result.Data = result.Data ?? message.Data; - result.FunctionName = result.FunctionName ?? message.FunctionName; + ret = await routing.InvokeAgent(context.GetCurrentAgentId(), message); - return result; + return true; } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/TaskEndRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/TaskEndRoutingHandler.cs index ba7f2e2c..edb2cfc9 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/TaskEndRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/TaskEndRoutingHandler.cs @@ -23,25 +23,17 @@ public class TaskEndRoutingHandler : RoutingHandlerBase, IRoutingHandler { } - public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst) + public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message) { - var result = new RoleDialogModel(AgentRole.Assistant, inst.Response) - { - MessageId = inst.MessageId, - CurrentAgentId = _settings.RouterId, - FunctionName = inst.Function, - Data = inst - }; - var hooks = _services.GetServices() .OrderBy(x => x.Priority) .ToList(); foreach (var hook in hooks) { - await hook.OnCurrentTaskEnding(result); + await hook.OnCurrentTaskEnding(message); } - return result; + return true; } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetNextInstruction.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetNextInstruction.cs index 10541e4c..e217dd89 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetNextInstruction.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetNextInstruction.cs @@ -57,6 +57,7 @@ public partial class RoutingService int retryCount = 0; var agentService = _services.GetRequiredService(); + var dialogs = Dialogs; while (retryCount < 3) { @@ -64,7 +65,7 @@ public partial class RoutingService { var conversation = ""; - foreach (var dialog in _dialogs.TakeLast(20)) + foreach (var dialog in dialogs.TakeLast(50)) { var role = dialog.Role; if (role != AgentRole.User) @@ -120,9 +121,6 @@ public partial class RoutingService return args; } -#if !DEBUG - [MemoryCache(10 * 60)] -#endif private string GetNextStepPrompt() { var template = _routerInstance.Router.Templates.First(x => x.Name == "next_step_prompt").Content; diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs index 8147f437..f19bee3f 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs @@ -8,13 +8,13 @@ public partial class RoutingService { const int MAXIMUM_RECURSION_DEPTH = 3; private int _currentRecursionDepth = 0; - public async Task InvokeAgent(string agentId) + public async Task InvokeAgent(string agentId, RoleDialogModel message) { _currentRecursionDepth++; if (_currentRecursionDepth > MAXIMUM_RECURSION_DEPTH) { _logger.LogWarning($"Current recursive call depth greater than {MAXIMUM_RECURSION_DEPTH}, which will cause unexpected result."); - return Dialogs.Last(); + return false; } var agentService = _services.GetRequiredService(); @@ -23,50 +23,56 @@ public partial class RoutingService var settings = _services.GetRequiredService(); var chatCompletion = CompletionProvider.GetChatCompletion(_services, provider: settings.Provider, model: settings.Model); RoleDialogModel response = chatCompletion.GetChatCompletions(agent, Dialogs); + message.Role = response.Role; if (response.Role == AgentRole.Function) { - return await InvokeFunction(agent, response); + message.FunctionName = response.FunctionName; + message.FunctionArgs = response.FunctionArgs; + + await InvokeFunction(agent, message); } else { - return response; + message.Content = response.Content; } + + return true; } - private async Task InvokeFunction(Agent agent, RoleDialogModel response) + private async Task InvokeFunction(Agent agent, RoleDialogModel message) { // execute function // Save states - SaveStateByArgs(JsonSerializer.Deserialize(response.FunctionArgs)); + SaveStateByArgs(JsonSerializer.Deserialize(message.FunctionArgs)); var conversationService = _services.GetRequiredService(); // Call functions - await conversationService.CallFunctions(response); + await conversationService.CallFunctions(message); - Dialogs.Add(response); + Dialogs.Add(message); // Pass execution result to LLM to get response - if (!response.StopCompletion) + if (!message.StopCompletion) { // Find response template var templateService = _services.GetRequiredService(); - var responseTemplate = await templateService.RenderFunctionResponse(agent.Id, response); + var responseTemplate = await templateService.RenderFunctionResponse(agent.Id, message); if (!string.IsNullOrEmpty(responseTemplate)) { - response.Role = AgentRole.Assistant; - response.Content = responseTemplate.Trim(); + message.Role = AgentRole.Assistant; + message.Content = responseTemplate.Trim(); } else { - response = await InvokeAgent(response.CurrentAgentId); + await InvokeAgent(message.CurrentAgentId, message); } } else { - response.Role = AgentRole.Assistant; + message.Role = AgentRole.Assistant; } - return response; + return message; } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index ba4f8f1b..b7073f04 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -46,38 +46,29 @@ public partial class RoutingService : IRoutingService _routerInstance = routerInstance; } - - public async Task ExecuteOnce(Agent agent) + public async Task ExecuteOnce(Agent agent, RoleDialogModel message) { - var message = Dialogs.Last().Content; - var handlers = _services.GetServices(); var handler = handlers.FirstOrDefault(x => x.Name == "route_to_agent"); handler.SetDialogs(Dialogs); + var result = await handler.Handle(this, new FunctionCallFromLlm { Function = "route_to_agent", - Question = message, - Reason = message, + Question = message.Content, + Reason = message.Content, AgentName = agent.Name - }); + }, message); return result; } - public async Task InstructLoop() + public async Task InstructLoop(RoleDialogModel message) { _routerInstance.Load(); var router = _routerInstance.Router; - var result = new RoleDialogModel(AgentRole.Assistant, "Can you repeat your request again?") - { - CurrentAgentId = router.Id - }; - - var inputMsg = Dialogs.Last(); - var handlers = _services.GetServices(); int loopCount = 0; @@ -87,8 +78,8 @@ public partial class RoutingService : IRoutingService loopCount++; var inst = await GetNextInstruction(); - inst.MessageId = inputMsg.MessageId; - inst.Question = inst.Question ?? inputMsg.Content; + message.Instruction = inst; + inst.Question = message.Content; var handler = handlers.FirstOrDefault(x => x.Name == inst.Function); if (handler == null) @@ -99,13 +90,18 @@ public partial class RoutingService : IRoutingService handler.SetRouter(router); handler.SetDialogs(Dialogs); - result = await handler.Handle(this, inst); - result.MessageId = inputMsg.MessageId; + message.FunctionName = inst.Function; + message.Role = AgentRole.Function; + message.FunctionArgs = inst.Arguments == null ? "{}" : JsonSerializer.Serialize(inst.Arguments); + + await handler.Handle(this, inst, message); + + inst.Response = message.Content; stop = !_settings.EnableReasoning; } - return result; + return true; } protected void SaveStateByArgs(JsonDocument args) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index 4fd4be21..5fb5cd7c 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -53,12 +53,11 @@ public class ConversationController : ControllerBase, IApiAdapter .SetState("sampling_factor", input.SamplingFactor); var response = new MessageResponseModel(); - var stackMsg = new List(); var inputMsg = new RoleDialogModel("user", input.Text); await conv.SendMessage(agentId, inputMsg, async msg => { - stackMsg.Add(msg); + }, async fnExecuting => { @@ -66,14 +65,14 @@ public class ConversationController : ControllerBase, IApiAdapter }, async fnExecuted => { - response.Function = fnExecuted.FunctionName; - response.Data = fnExecuted.Data; + }); - response.Text = string.Join("\r\n", stackMsg.Select(x => x.Content)); - response.Data = response.Data ?? stackMsg.Last().Data; - response.Function = stackMsg.Last().FunctionName; response.MessageId = inputMsg.MessageId; + response.Text = inputMsg.Content; + response.Data = inputMsg.Data; + response.Function = inputMsg.FunctionName; + response.Instruction = inputMsg.Instruction; return response; } diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/MessageResponseModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/MessageResponseModel.cs index dc88faaf..4dcf0c58 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/MessageResponseModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/MessageResponseModel.cs @@ -1,4 +1,6 @@ +using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Models; +using BotSharp.Abstraction.Routing.Models; namespace BotSharp.OpenAPI.ViewModels.Conversations; @@ -8,4 +10,5 @@ public class MessageResponseModel : ITrackableMessage public string Text { get; set; } public string Function { get; set; } public object Data { get; set; } + public FunctionCallFromLlm Instruction { get; set; } } diff --git a/src/WebStarter/WebStarter.csproj b/src/WebStarter/WebStarter.csproj index b05729a7..6723a4da 100644 --- a/src/WebStarter/WebStarter.csproj +++ b/src/WebStarter/WebStarter.csproj @@ -5,6 +5,7 @@ enable enable 4fb8c9df-7975-4926-ba73-46c8ca440691 + False diff --git a/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid b/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid index d9c0515b..67cd6d1a 100644 --- a/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid +++ b/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid @@ -2,7 +2,7 @@ You're {{router.name}} ({{router.description}}). Follow these steps to handle us 1. Read the [CONVERSATION] content. 2. Select a appropriate function from [FUNCTIONS]. 3. Determine which agent is suitable to handle this conversation. -4. If user wants to talk with human being, you will transfer to customer representative. +4. Re-think on whether the function you chose matches the reason. 5. For agent required arguments, leave it as blank object if user doesn't provide it. [FUNCTIONS] diff --git a/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/next_step_prompt.liquid b/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/next_step_prompt.liquid index 41bcd63d..12de3ffd 100644 --- a/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/next_step_prompt.liquid +++ b/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/next_step_prompt.liquid @@ -1 +1,4 @@ -What is the next step based on the CONVERSATION? Response must be in appropriate JSON format. Route to the Agent that last handled the conversation if necessary. \ No newline at end of file +What is the next step based on the CONVERSATION? +Response must be in appropriate JSON format. +Route to the Agent that last handled the conversation if necessary. +If user wants to speak to customer service, use function human_intervention_needed. \ No newline at end of file diff --git a/tests/BotSharp.Plugin.PizzaBot/Functions/GetPizzaPricesFn.cs b/tests/BotSharp.Plugin.PizzaBot/Functions/GetPizzaPricesFn.cs index 9e39a155..4fa39c7c 100644 --- a/tests/BotSharp.Plugin.PizzaBot/Functions/GetPizzaPricesFn.cs +++ b/tests/BotSharp.Plugin.PizzaBot/Functions/GetPizzaPricesFn.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Conversations.Models; +using System.Text.Json; namespace BotSharp.Plugin.PizzaBot.Functions; @@ -14,6 +15,7 @@ public class GetPizzaPricesFn : IFunctionCallback cheese_unit_price = 3.5, margherita_unit_price = 3.8, }; + message.Content = JsonSerializer.Serialize(message.Data); return true; } }