diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs index 530f425d..c99a819b 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs @@ -34,23 +34,28 @@ public class DialogElement { public DialogMetaData MetaData { get; set; } public string Content { get; set; } + public string? SecondaryContent { get; set; } public string? RichContent { get; set; } + public string? SecondaryRichContent { get; set; } public DialogElement() { } - public DialogElement(DialogMetaData meta, string content, string? richContent = null) + public DialogElement(DialogMetaData meta, string content, string? richContent = null, + string? secondaryContent = null, string? secondaryRichContent = null) { MetaData = meta; Content = content; RichContent = richContent; + SecondaryContent = secondaryContent; + SecondaryRichContent = secondaryRichContent; } public override string ToString() { - return $"{MetaData.Role}: {Content} [{MetaData.CreateTime}]"; + return $"{MetaData.Role}: {Content} [{MetaData?.CreateTime}]"; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs index dea85881..f6e1ecfe 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs @@ -26,6 +26,8 @@ public class RoleDialogModel : ITrackableMessage public string Content { get; set; } + public string? SecondaryContent { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string CurrentAgentId { get; set; } @@ -57,6 +59,9 @@ public class RoleDialogModel : ITrackableMessage [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public RichContent? RichContent { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public RichContent? SecondaryRichContent { get; set; } + /// /// Stop conversation completion /// diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/IRichMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/IRichMessage.cs index e04692e6..84d8fcfb 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/IRichMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/IRichMessage.cs @@ -1,12 +1,15 @@ using BotSharp.Abstraction.Messaging.Enums; +using Newtonsoft.Json; namespace BotSharp.Abstraction.Messaging; public interface IRichMessage { [JsonPropertyName("text")] + [JsonProperty("text")] string Text { get; set; } [JsonPropertyName("rich_type")] + [JsonProperty("rich_type")] string RichType => RichTypeEnum.Text; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/ITemplateMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/ITemplateMessage.cs index 63890240..7c9ccadf 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/ITemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/ITemplateMessage.cs @@ -1,7 +1,10 @@ +using Newtonsoft.Json; + namespace BotSharp.Abstraction.Messaging; public interface ITemplateMessage { [JsonPropertyName("template_type")] + [JsonProperty("template_type")] string TemplateType => string.Empty; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementAction.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementAction.cs index 5a2682bd..39d03e86 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementAction.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementAction.cs @@ -1,3 +1,6 @@ +using Newtonsoft.Json; +using JsonIgnoreAttribute = System.Text.Json.Serialization.JsonIgnoreAttribute; + namespace BotSharp.Abstraction.Messaging.Models.RichContent; public class ElementAction @@ -8,6 +11,7 @@ public class ElementAction public string Url { get; set; } [JsonPropertyName("webview_height_ratio")] + [JsonProperty("webview_height_ratio")] public string WebViewHeightRatio { get; set; } [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs index 294b1835..47e7cc9a 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs @@ -1,3 +1,6 @@ +using Newtonsoft.Json; +using JsonIgnoreAttribute = System.Text.Json.Serialization.JsonIgnoreAttribute; + namespace BotSharp.Abstraction.Messaging.Models.RichContent; /// @@ -16,11 +19,14 @@ public class ElementButton public string Payload { get; set; } [JsonPropertyName("is_primary")] + [JsonProperty("is_primary")] public bool IsPrimary { get; set; } [JsonPropertyName("is_secondary")] + [JsonProperty("is_secondary")] public bool IsSecondary { get; set; } [JsonPropertyName("post_action_disclaimer")] + [JsonProperty("post_action_disclaimer")] public string? PostActionDisclaimer { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/QuickReplyElement.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/QuickReplyElement.cs index 4ad88ab9..583cf966 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/QuickReplyElement.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/QuickReplyElement.cs @@ -1,3 +1,6 @@ +using Newtonsoft.Json; +using JsonIgnoreAttribute = System.Text.Json.Serialization.JsonIgnoreAttribute; + namespace BotSharp.Abstraction.Messaging.Models.RichContent; public class QuickReplyElement @@ -9,6 +12,7 @@ public class QuickReplyElement public string? Payload { get; set; } [JsonPropertyName("image_url")] + [JsonProperty("image_url")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ImageUrl { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/QuickReplyMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/QuickReplyMessage.cs index efb177f0..73690649 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/QuickReplyMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/QuickReplyMessage.cs @@ -1,13 +1,16 @@ using BotSharp.Abstraction.Messaging.Enums; +using Newtonsoft.Json; namespace BotSharp.Abstraction.Messaging.Models.RichContent; public class QuickReplyMessage : IRichMessage { [JsonPropertyName("rich_type")] + [JsonProperty("rich_type")] public string RichType => RichTypeEnum.QuickReply; public string Text { get; set; } = string.Empty; [JsonPropertyName("quick_replies")] + [JsonProperty("quick_replies")] public List QuickReplies { get; set; } = new List(); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ButtonTemplateMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ButtonTemplateMessage.cs index 32ad73e2..ce8a5a93 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ButtonTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ButtonTemplateMessage.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Messaging.Enums; +using Newtonsoft.Json; namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template; @@ -8,17 +9,22 @@ namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template; public class ButtonTemplateMessage : IRichMessage, ITemplateMessage { [JsonPropertyName("rich_type")] + [JsonProperty("rich_type")] public string RichType => RichTypeEnum.ButtonTemplate; [JsonPropertyName("text")] + [JsonProperty("text")] public string Text { get; set; } = string.Empty; [JsonPropertyName("template_type")] + [JsonProperty("template_type")] public string TemplateType => TemplateTypeEnum.Button; [JsonPropertyName("buttons")] + [JsonProperty("buttons")] public ElementButton[] Buttons { get; set; } = new ElementButton[0]; [JsonPropertyName("is_horizontal")] + [JsonProperty("is_horizontal")] public bool IsHorizontal { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/CouponTemplateMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/CouponTemplateMessage.cs index 1ca47b5b..171947ab 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/CouponTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/CouponTemplateMessage.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Messaging.Enums; +using Newtonsoft.Json; namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template; @@ -9,30 +10,40 @@ namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template; public class CouponTemplateMessage : IRichMessage, ITemplateMessage { [JsonPropertyName("rich_type")] + [JsonProperty("rich_type")] public string RichType => RichTypeEnum.CouponTemplate; + [JsonPropertyName("text")] + [JsonProperty("text")] public string Text { get; set; } public string Title { get; set; } public string Subtitle { get; set; } [JsonPropertyName("template_type")] + [JsonProperty("template_type")] public string TemplateType => TemplateTypeEnum.Coupon; [JsonPropertyName("coupon_code")] + [JsonProperty("coupon_code")] public string CouponCode { get; set; } [JsonPropertyName("coupon_url")] + [JsonProperty("coupon_url")] public string CouponUrl { get; set; } [JsonPropertyName("coupon_url_button_title")] + [JsonProperty("coupon_url_button_title")] public string CouponUrlButtonTitle { get; set; } = "Shop now"; [JsonPropertyName("coupon_pre_message")] + [JsonProperty("coupon_pre_message")] public string CouponPreMessage { get; set; } = "Here's a deal just for you!"; [JsonPropertyName("image_url")] + [JsonProperty("image_url")] public string ImageUrl { get; set; } [JsonPropertyName("payload")] + [JsonProperty("payload")] public string Payload { get; set; } } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/GenericTemplateMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/GenericTemplateMessage.cs index 6578b363..c846e4ea 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/GenericTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/GenericTemplateMessage.cs @@ -1,28 +1,36 @@ using BotSharp.Abstraction.Messaging.Enums; +using Newtonsoft.Json; namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template; public class GenericTemplateMessage : IRichMessage, ITemplateMessage { [JsonPropertyName("rich_type")] + [JsonProperty("rich_type")] public string RichType => RichTypeEnum.GenericTemplate; [JsonPropertyName("text")] + [JsonProperty("text")] public string Text { get; set; } = string.Empty; [JsonPropertyName("template_type")] + [JsonProperty("template_type")] public virtual string TemplateType { get; set; } = TemplateTypeEnum.Generic; [JsonPropertyName("elements")] + [JsonProperty("elements")] public List Elements { get; set; } = new List(); [JsonPropertyName("is_horizontal")] + [JsonProperty("is_horizontal")] public bool IsHorizontal { get; set; } [JsonPropertyName("is_popup")] + [JsonProperty("is_popup")] public bool IsPopup { get; set; } [JsonPropertyName("element_type")] + [JsonProperty("element_type")] public string ElementType => typeof(T).Name; } @@ -30,9 +38,13 @@ public class GenericElement { public string Title { get; set; } public string Subtitle { get; set; } + [JsonPropertyName("image_url")] + [JsonProperty("image_url")] public string ImageUrl { get; set; } + [JsonPropertyName("default_action")] + [JsonProperty("default_action")] public ElementAction DefaultAction { get; set; } public ElementButton[] Buttons { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/MultiSelectTemplateMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/MultiSelectTemplateMessage.cs index 6919a1a4..c57f144a 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/MultiSelectTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/MultiSelectTemplateMessage.cs @@ -1,21 +1,28 @@ using BotSharp.Abstraction.Messaging.Enums; +using Newtonsoft.Json; namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template; public class MultiSelectTemplateMessage : IRichMessage, ITemplateMessage { [JsonPropertyName("rich_type")] + [JsonProperty("rich_type")] public string RichType => RichTypeEnum.MultiSelectTemplate; + [JsonPropertyName("text")] + [JsonProperty("text")] public string Text { get; set; } = string.Empty; [JsonPropertyName("template_type")] + [JsonProperty("template_type")] public string TemplateType => TemplateTypeEnum.MultiSelect; [JsonPropertyName("options")] + [JsonProperty("options")] public List Options { get; set; } = new List(); [JsonPropertyName("is_horizontal")] + [JsonProperty("is_horizontal")] public bool IsHorizontal { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProductTemplateMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProductTemplateMessage.cs index 59a1e911..61d22dcc 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProductTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProductTemplateMessage.cs @@ -1,16 +1,20 @@ using BotSharp.Abstraction.Messaging.Enums; +using Newtonsoft.Json; namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template; public class ProductTemplateMessage : IRichMessage, ITemplateMessage { [JsonPropertyName("rich_type")] + [JsonProperty("rich_type")] public string RichType => RichTypeEnum.GenericTemplate; [JsonPropertyName("text")] + [JsonProperty("text")] public string Text { get; set; } = string.Empty; [JsonPropertyName("template_type")] + [JsonProperty("template_type")] public string TemplateType => TemplateTypeEnum.Product; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/TextMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/TextMessage.cs index 899a9265..d2639ef7 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/TextMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/TextMessage.cs @@ -1,10 +1,12 @@ using BotSharp.Abstraction.Messaging.Enums; +using Newtonsoft.Json; namespace BotSharp.Abstraction.Messaging.Models.RichContent; public class TextMessage : IRichMessage { [JsonPropertyName("rich_type")] + [JsonProperty("rich_type")] public string RichType => RichTypeEnum.Text; public string Text { get; set; } = string.Empty; diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingContext.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingContext.cs index 2c0aa15e..3c93976b 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingContext.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingContext.cs @@ -3,7 +3,8 @@ namespace BotSharp.Abstraction.Routing; public interface IRoutingContext { string GetCurrentAgentId(); - string PreviousAgentId(); + string FirstGoalAgentId(); + bool ContainsAgentId(string agentId); string OriginAgentId { get; } string ConversationId { get; } string MessageId { get; } @@ -13,6 +14,7 @@ public interface IRoutingContext int AgentCount { get; } void Push(string agentId, string? reason = null); void Pop(string? reason = null); + void PopTo(string agentId, string reason); void Replace(string agentId, string? reason = null); void Empty(string? reason = null); } diff --git a/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs b/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs index 7df5fa02..eb21fb31 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs @@ -14,6 +14,13 @@ public class AgentPlugin : IBotSharpPlugin public SettingsMeta Settings => new SettingsMeta("Agent"); + public string[] AgentIds => new string[] + { + "01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a", + "01e2fc5c-2c89-4ec7-8470-7688608b496c", + "01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b" + }; + public object GetNewSettingsInstance() => new AgentSettings(); diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index 4b7bfff6..0cd3468f 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -46,6 +46,9 @@ + + + @@ -67,6 +70,15 @@ + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index e86f4610..636d622e 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -127,6 +127,8 @@ public class ConversationStateService : IConversationStateService, IDisposable _historyStates = _db.GetConversationStates(conversationId); var dialogs = _db.GetConversationDialogs(conversationId); var userDialogs = dialogs.Where(x => x.MetaData?.Role == AgentRole.User || x.MetaData?.Role == UserRole.Client) + .GroupBy(x => x.MetaData?.MessageId) + .Select(g => g.First()) .OrderBy(x => x.MetaData?.CreateTime) .ToList(); var curMsgIndex = userDialogs.FindIndex(x => !string.IsNullOrEmpty(curMsgId) && x.MetaData?.MessageId == curMsgId); @@ -342,9 +344,17 @@ public class ConversationStateService : IConversationStateService, IDisposable { foreach (JsonProperty property in root.EnumerateObject()) { - if (!string.IsNullOrEmpty(property.Value.ToString())) + var propertyValue = property.Value; + var stateValue = propertyValue.ToString(); + if (!string.IsNullOrEmpty(stateValue)) { - SetState(property.Name, property.Value, source: StateSource.Application); + if (propertyValue.ValueKind == JsonValueKind.True || + propertyValue.ValueKind == JsonValueKind.False) + { + stateValue = stateValue?.ToLower(); + } + + SetState(property.Name, stateValue, source: StateSource.Application); } } } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs index ba0d9198..4ab447b8 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs @@ -50,7 +50,7 @@ public class ConversationStorage : IConversationStorage { return; } - dialogElements.Add(new DialogElement(meta, content)); + dialogElements.Add(new DialogElement(meta, content, dialog.SecondaryContent)); } else { @@ -71,7 +71,8 @@ public class ConversationStorage : IConversationStorage } var richContent = dialog.RichContent != null ? JsonSerializer.Serialize(dialog.RichContent, _options.JsonSerializerOptions) : null; - dialogElements.Add(new DialogElement(meta, content, richContent)); + var secondaryRichContent = dialog.SecondaryRichContent != null ? JsonSerializer.Serialize(dialog.SecondaryRichContent, _options.JsonSerializerOptions) : null; + dialogElements.Add(new DialogElement(meta, content, richContent, dialog.SecondaryContent, secondaryRichContent)); } db.AppendConversationDialogs(conversationId, dialogElements); @@ -88,6 +89,7 @@ public class ConversationStorage : IConversationStorage { var meta = dialog.MetaData; var content = dialog.Content; + var secondaryContent = dialog.SecondaryContent; var role = meta.Role; var currentAgentId = meta.AgentId; var messageId = meta.MessageId; @@ -96,6 +98,8 @@ public class ConversationStorage : IConversationStorage var createdAt = meta.CreateTime; var richContent = !string.IsNullOrEmpty(dialog.RichContent) ? JsonSerializer.Deserialize>(dialog.RichContent, _options.JsonSerializerOptions) : null; + var secondaryRichContent = !string.IsNullOrEmpty(dialog.SecondaryRichContent) ? + JsonSerializer.Deserialize>(dialog.SecondaryRichContent, _options.JsonSerializerOptions) : null; var record = new RoleDialogModel(role, content) { @@ -104,7 +108,9 @@ public class ConversationStorage : IConversationStorage CreatedAt = createdAt, SenderId = senderId, FunctionName = function, - RichContent = richContent + RichContent = richContent, + SecondaryContent = secondaryContent, + SecondaryRichContent = secondaryRichContent, }; results.Add(record); diff --git a/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs b/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs index 72d55b45..f6de19ac 100644 --- a/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs +++ b/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs @@ -5,7 +5,6 @@ using System.Drawing; using System.IO; using System.Reflection; using System.Xml; -using BotSharp.Abstraction.Repositories; namespace BotSharp.Core.Plugins; diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs index 70f28bbe..b07ba3ce 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs @@ -506,11 +506,14 @@ namespace BotSharp.Core.Repository var rawDialogs = File.ReadAllLines(dialogDir); if (!rawDialogs.IsNullOrEmpty()) { - for (int i = 0; i < rawDialogs.Count(); i += 2) + for (int i = 0; i < rawDialogs.Count(); i += 5) { var blocks = rawDialogs[i].Split("|"); - var content = rawDialogs[i + 1]; - var trimmed = content.Substring(4); + var content = rawDialogs[i + 2]; + var trimmedContent = content.Substring(4); + var secondaryContent = rawDialogs[i + 4]; + var trimmedSecondaryContent = secondaryContent.Substring(4); + var meta = new DialogMetaData { Role = blocks[1], @@ -521,8 +524,9 @@ namespace BotSharp.Core.Repository CreateTime = DateTime.Parse(blocks[0]) }; - var richContent = blocks.Count() > 6 ? DecodeRichContent(blocks[6]) : null; - dialogs.Add(new DialogElement(meta, trimmed, richContent)); + var richContent = DecodeRichContent(rawDialogs[i + 1]); + var secondaryRichContent = DecodeRichContent(rawDialogs[i + 3]); + dialogs.Add(new DialogElement(meta, trimmedContent, richContent, trimmedSecondaryContent, secondaryRichContent)); } } return dialogs; @@ -538,10 +542,17 @@ namespace BotSharp.Core.Repository var meta = element.MetaData; var createTime = meta.CreateTime.ToString("MM/dd/yyyy hh:mm:ss.ffffff tt", CultureInfo.InvariantCulture); var encodedRichContent = EncodeRichContent(element.RichContent); - var metaStr = $"{createTime}|{meta.Role}|{meta.AgentId}|{meta.MessageId}|{meta.SenderId}|{meta.FunctionName}|{encodedRichContent}"; + var encodedSecondaryRichContent = EncodeRichContent(element.SecondaryRichContent); + var metaStr = $"{createTime}|{meta.Role}|{meta.AgentId}|{meta.MessageId}|{meta.SenderId}|{meta.FunctionName}"; dialogTexts.Add(metaStr); + + dialogTexts.Add(encodedRichContent); var content = $" - {element.Content}"; dialogTexts.Add(content); + + dialogTexts.Add(encodedSecondaryRichContent); + var secondaryContent = $" - {element.SecondaryContent}"; + dialogTexts.Add(secondaryContent); } return dialogTexts; diff --git a/src/Infrastructure/BotSharp.Core/Routing/Functions/HumanInterventionNeededFn.cs b/src/Infrastructure/BotSharp.Core/Routing/Functions/HumanInterventionNeededFn.cs new file mode 100644 index 00000000..d0c26be0 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Routing/Functions/HumanInterventionNeededFn.cs @@ -0,0 +1,29 @@ +using BotSharp.Abstraction.Functions; + +namespace BotSharp.Core.Routing.Functions; + +public class HumanInterventionNeededFn : IFunctionCallback +{ + public string Name => "human_intervention_needed"; + + private readonly IServiceProvider _services; + + public HumanInterventionNeededFn(IServiceProvider services) + { + _services = services; + } + + public async Task Execute(RoleDialogModel message) + { + var hooks = _services.GetServices() + .OrderBy(x => x.Priority) + .ToList(); + + foreach (var hook in hooks) + { + await hook.OnHumanInterventionNeeded(message); + } + + return true; + } +} diff --git a/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs b/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs index 8221cfb5..2450a6fc 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs @@ -51,7 +51,7 @@ public partial class RouteToAgentFn : IFunctionCallback var originalAgent = db.GetAgents(filter).FirstOrDefault(); if (originalAgent != null) { - _context.Push(originalAgent.Id, $"user goal agent{(correctToOriginalAgent ? " & is corrected" : "")}"); + _context.Push(originalAgent.Id, $"user goal agent{(correctToOriginalAgent ? " " + originalAgent.Name + " & is corrected" : "")}"); } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs deleted file mode 100644 index 4aca2c3a..00000000 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs +++ /dev/null @@ -1,43 +0,0 @@ -using BotSharp.Abstraction.Routing.Settings; - -namespace BotSharp.Core.Routing.Handlers; - -public class HumanInterventionNeededHandler : RoutingHandlerBase, IRoutingHandler -{ - public string Name => "human_intervention_needed"; - - public string Description => "Reach out to human customer service."; - - public List Parameters => new List - { - new ParameterPropertyDef("reason", "why need customer service"), - new ParameterPropertyDef("summary", "the whole conversation summary with important information"), - new ParameterPropertyDef("response", "asking user whether to connect with customer service representative") - }; - - public HumanInterventionNeededHandler(IServiceProvider services, ILogger logger, RoutingSettings settings) - : base(services, logger, settings) - { - - } - - public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message) - { - var response = RoleDialogModel.From(message, - role: AgentRole.Assistant, - content: inst.Response); - - _dialogs.Add(response); - - var hooks = _services.GetServices() - .OrderBy(x => x.Priority) - .ToList(); - - foreach (var hook in hooks) - { - await hook.OnHumanInterventionNeeded(response); - } - - return true; - } -} diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs index 87e55840..4d8eba43 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Routing.Settings; +using BotSharp.Abstraction.Utilities; namespace BotSharp.Core.Routing; @@ -137,7 +138,18 @@ public class RoutingContext : IRoutingContext } } - public string PreviousAgentId() + public void PopTo(string agentId, string reason) + { + var currentAgentId = GetCurrentAgentId(); + while (!string.IsNullOrEmpty(currentAgentId) && + currentAgentId != agentId) + { + Pop(reason); + currentAgentId = GetCurrentAgentId(); + } + } + + public string FirstGoalAgentId() { if (_stack.Count == 1) { @@ -151,6 +163,11 @@ public class RoutingContext : IRoutingContext return string.Empty; } + public bool ContainsAgentId(string agentId) + { + return _stack.ToArray().Contains(agentId); + } + public void Replace(string agentId, string? reason = null) { var fromAgent = agentId; diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/agent.json b/src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/agent.json new file mode 100644 index 00000000..d32e4680 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/agent.json @@ -0,0 +1,11 @@ +{ + "id": "01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b", + "name": "Human Support", + "description": "Reach out to human customer service representative.", + "type": "task", + "createdDateTime": "2024-04-22T10:00:00Z", + "updatedDateTime": "2024-04-22T10:00:00Z", + "disabled": false, + "isPublic": true, + "profiles": [ "human" ] +} \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/functions.json b/src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/functions.json new file mode 100644 index 00000000..240f5b7c --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/functions.json @@ -0,0 +1,20 @@ +[ + { + "name": "human_intervention_needed", + "description": "If user wants to speak to human customer service.", + "parameters": { + "type": "object", + "properties": { + "reason": { + "type": "string", + "description": "why customer needs customer service." + }, + "summary": { + "type": "string", + "description": "the whole conversation summary with important information" + } + }, + "required": [ "reason", "summary" ] + } + } +] diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/instruction.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/instruction.liquid new file mode 100644 index 00000000..3b1aab42 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/instruction.liquid @@ -0,0 +1,2 @@ +You are a human customer service connection program. +When other AI customer service cannot solve user problems, you know how to call the API to transfer users to human customer service for answers. \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.naive.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.naive.liquid index 3a332350..f7388be6 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.naive.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.naive.liquid @@ -10,5 +10,3 @@ Expected user goal agent is {{ expected_user_goal_agent }}. {%- else -%} User goal agent is inferred based on user initial request. {%- endif %} -If user wants to speak to human customer service, use function human_intervention_needed. -If user wants to or is processing with a specific task that can be handled by agents, respond in appropriate output format defined to let proper agent to handle the task. diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index 8bf3b757..04e61773 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -84,6 +84,7 @@ public class ConversationController : ControllerBase MessageId = message.MessageId, CreatedAt = message.CreatedAt, Text = message.Content, + SecondaryText = message.SecondaryContent, Data = message.Data, Sender = UserViewModel.FromUser(user) }); @@ -97,6 +98,7 @@ public class ConversationController : ControllerBase MessageId = message.MessageId, CreatedAt = message.CreatedAt, Text = message.Content, + SecondaryText = message.SecondaryContent, Function = message.FunctionName, Data = message.Data, Sender = new UserViewModel @@ -104,7 +106,8 @@ public class ConversationController : ControllerBase FirstName = agent.Name, Role = message.Role, }, - RichContent = message.RichContent + RichContent = message.RichContent, + SecondaryRichContent = message.SecondaryRichContent }); } } diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ChatResponseModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ChatResponseModel.cs index cf71b8f9..86903800 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ChatResponseModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ChatResponseModel.cs @@ -21,6 +21,9 @@ public class ChatResponseModel : InstructResult [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public FunctionCallFromLlm? Instruction { get; set; } + [JsonPropertyName("secondary_text")] + public string? SecondaryText { get; set; } + /// /// Rich message for UI rendering /// @@ -28,6 +31,10 @@ public class ChatResponseModel : InstructResult [JsonPropertyName("rich_content")] public RichContent? RichContent { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("secondary_rich_content")] + public RichContent? SecondaryRichContent { get; set; } + [JsonPropertyName("created_at")] public DateTime CreatedAt { get; set; } = DateTime.UtcNow; } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/DialogMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/DialogMongoElement.cs index a35d3fc1..6ebc16ff 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/DialogMongoElement.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/DialogMongoElement.cs @@ -6,7 +6,9 @@ public class DialogMongoElement { public DialogMetaDataMongoElement MetaData { get; set; } public string Content { get; set; } + public string? SecondaryContent { get; set; } public string? RichContent { get; set; } + public string? SecondaryRichContent { get; set; } public DialogMongoElement() { @@ -19,7 +21,9 @@ public class DialogMongoElement { MetaData = DialogMetaDataMongoElement.ToMongoElement(dialog.MetaData), Content = dialog.Content, - RichContent = dialog.RichContent + SecondaryContent = dialog.SecondaryContent, + RichContent = dialog.RichContent, + SecondaryRichContent = dialog.SecondaryRichContent }; } @@ -29,7 +33,9 @@ public class DialogMongoElement { MetaData = DialogMetaDataMongoElement.ToDomainElement(dialog.MetaData), Content = dialog.Content, - RichContent = dialog.RichContent + SecondaryContent = dialog.SecondaryContent, + RichContent = dialog.RichContent, + SecondaryRichContent = dialog.SecondaryRichContent }; } }