From a6d57e417c77affdb276c8485b3ee745d3d5c7b6 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 2 Apr 2024 09:44:40 -0500 Subject: [PATCH 001/201] minor change --- .../Messaging/JsonConverters/RichContentJsonConverter .cs | 1 - .../Messaging/JsonConverters/TemplateMessageJsonConverter.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/RichContentJsonConverter .cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/RichContentJsonConverter .cs index 502b0785..98230871 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/RichContentJsonConverter .cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/RichContentJsonConverter .cs @@ -9,7 +9,6 @@ public class RichContentJsonConverter : JsonConverter { using var jsonDoc = JsonDocument.ParseValue(ref reader); var root = jsonDoc.RootElement; - var jsonText = root.GetRawText(); var res = MessageParser.ParseRichMessage(root, options); return res; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/TemplateMessageJsonConverter.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/TemplateMessageJsonConverter.cs index f0f14730..da5fcfd1 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/TemplateMessageJsonConverter.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/TemplateMessageJsonConverter.cs @@ -9,7 +9,6 @@ public class TemplateMessageJsonConverter : JsonConverter { using var jsonDoc = JsonDocument.ParseValue(ref reader); var root = jsonDoc.RootElement; - var jsonText = root.GetRawText(); var res = MessageParser.ParseTemplateMessage(root, options); return res; } From 7ae9e3ef60f2c62d89c6ad95069861fc600f1183 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 2 Apr 2024 11:38:18 -0500 Subject: [PATCH 002/201] refine states --- .../BotSharp.Abstraction.csproj | 1 + .../Conversations/Enums/StateDataType.cs | 10 ++++++ .../Conversations/Enums/StateSource.cs | 8 +++++ .../IConversationStateService.cs | 4 ++- .../Conversations/Models/StateKeyValue.cs | 21 +++++++++++ ...sageParser.cs => BotSharpMessageParser.cs} | 29 ++++++++------- .../RichContentJsonConverter .cs | 2 +- .../TemplateMessageJsonConverter.cs | 2 +- .../Services/ConversationStateService.cs | 35 ++++++++++++------- 9 files changed, 84 insertions(+), 28 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateDataType.cs create mode 100644 src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateSource.cs rename src/Infrastructure/BotSharp.Abstraction/Messaging/{MessageParser.cs => BotSharpMessageParser.cs} (75%) diff --git a/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj b/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj index bbade11e..65424adb 100644 --- a/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj +++ b/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj @@ -22,6 +22,7 @@ + diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateDataType.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateDataType.cs new file mode 100644 index 00000000..20d635f4 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateDataType.cs @@ -0,0 +1,10 @@ +namespace BotSharp.Abstraction.Conversations.Enums; + +public class StateDataType +{ + public const string String = "string"; + public const string Boolean = "boolean"; + public const string Number = "number"; + public const string Currency = "currency"; + public const string Date = "date"; +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateSource.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateSource.cs new file mode 100644 index 00000000..31f32e16 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateSource.cs @@ -0,0 +1,8 @@ +namespace BotSharp.Abstraction.Conversations.Enums; + +public class StateSource +{ + public const string External = "external"; + public const string Application = "application"; + public const string User = "user"; +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs index 07eab6c1..d71111fd 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.Conversations.Enums; using System.Text.Json; namespace BotSharp.Abstraction.Conversations; @@ -12,7 +13,8 @@ public interface IConversationStateService string GetState(string name, string defaultValue = ""); bool ContainsState(string name); Dictionary GetStates(); - IConversationStateService SetState(string name, T value, bool isNeedVersion = true, int activeRounds = -1); + IConversationStateService SetState(string name, T value, bool isNeedVersion = true, + int activeRounds = -1, string valueType = StateDataType.String, string source = StateSource.User); void SaveStateByArgs(JsonDocument args); void CleanStates(); void Save(); diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateKeyValue.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateKeyValue.cs index 53618242..c70dbf03 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateKeyValue.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateKeyValue.cs @@ -1,3 +1,5 @@ +using BotSharp.Abstraction.Conversations.Enums; + namespace BotSharp.Abstraction.Conversations.Models; public class StateKeyValue @@ -16,6 +18,12 @@ public class StateKeyValue Key = key; Values = values; } + + public override string ToString() + { + var lastValue = Values.LastOrDefault(); + return $"{Key} => ({lastValue?.ToString()})"; + } } public class StateValue @@ -30,6 +38,12 @@ public class StateValue [JsonPropertyName("active_rounds")] public int ActiveRounds { get; set; } + [JsonPropertyName("data_type")] + public string DataType { get; set; } = StateDataType.String; + + [JsonPropertyName("source")] + public string Source { get; set; } + [JsonPropertyName("update_time")] public DateTime UpdateTime { get; set; } @@ -37,4 +51,11 @@ public class StateValue { } + + public override string ToString() + { + var isActive = Active ? "Yes" : "No"; + var activeRounds = ActiveRounds <= 0 ? "infinity" : ActiveRounds.ToString(); + return $"Data: {Data}, Active: {isActive}, Active rounds: {activeRounds}, Source: {Source}"; + } } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/MessageParser.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs similarity index 75% rename from src/Infrastructure/BotSharp.Abstraction/Messaging/MessageParser.cs rename to src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs index b739709c..6697ef34 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/MessageParser.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs @@ -3,10 +3,13 @@ using BotSharp.Abstraction.Messaging.Enums; using BotSharp.Abstraction.Messaging.Models.RichContent.Template; using BotSharp.Abstraction.Messaging.Models.RichContent; using System.Text.Json; +using System.Reflection; +using Newtonsoft.Json; +using JsonSerializer = System.Text.Json.JsonSerializer; namespace BotSharp.Core.Messaging; -public static class MessageParser +public static class BotSharpMessageParser { public static IRichMessage? ParseRichMessage(JsonElement root, JsonSerializerOptions options) @@ -43,13 +46,13 @@ public static class MessageParser if (root.TryGetProperty("element_type", out element)) { var elementType = element.GetString(); - if (elementType == typeof(GenericElement).Name) + var wrapperType = typeof(GenericTemplateMessage<>); + var genericType = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(x => x.Name == elementType); + + if (wrapperType != null && genericType != null) { - res = JsonSerializer.Deserialize>(jsonText, options); - } - else if (elementType == typeof(ButtonElement).Name) - { - res = JsonSerializer.Deserialize>(jsonText, options); + var targetType = wrapperType.MakeGenericType(genericType); + res = JsonConvert.DeserializeObject(jsonText, targetType) as IRichMessage; } } } @@ -88,13 +91,13 @@ public static class MessageParser if (root.TryGetProperty("element_type", out element)) { var elementType = element.GetString(); - if (elementType == typeof(GenericElement).Name) + var wrapperType = typeof(GenericTemplateMessage<>); + var genericType = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(x => x.Name == elementType); + + if (wrapperType != null && genericType != null) { - res = JsonSerializer.Deserialize>(jsonText, options); - } - else if (elementType == typeof(ButtonElement).Name) - { - res = JsonSerializer.Deserialize>(jsonText, options); + var targetType = wrapperType.MakeGenericType(genericType); + res = JsonConvert.DeserializeObject(jsonText, targetType) as ITemplateMessage; } } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/RichContentJsonConverter .cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/RichContentJsonConverter .cs index 98230871..94d12796 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/RichContentJsonConverter .cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/RichContentJsonConverter .cs @@ -9,7 +9,7 @@ public class RichContentJsonConverter : JsonConverter { using var jsonDoc = JsonDocument.ParseValue(ref reader); var root = jsonDoc.RootElement; - var res = MessageParser.ParseRichMessage(root, options); + var res = BotSharpMessageParser.ParseRichMessage(root, options); return res; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/TemplateMessageJsonConverter.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/TemplateMessageJsonConverter.cs index da5fcfd1..84963d39 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/TemplateMessageJsonConverter.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/TemplateMessageJsonConverter.cs @@ -9,7 +9,7 @@ public class TemplateMessageJsonConverter : JsonConverter { using var jsonDoc = JsonDocument.ParseValue(ref reader); var root = jsonDoc.RootElement; - var res = MessageParser.ParseTemplateMessage(root, options); + var res = BotSharpMessageParser.ParseTemplateMessage(root, options); return res; } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index 9356e2f5..32fbb30a 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.Conversations.Enums; using BotSharp.Abstraction.Users.Enums; namespace BotSharp.Core.Conversations.Services; @@ -33,7 +34,8 @@ public class ConversationStateService : IConversationStateService, IDisposable /// /// whether the state is related to message or not /// - public IConversationStateService SetState(string name, T value, bool isNeedVersion = true, int activeRounds = -1) + public IConversationStateService SetState(string name, T value, bool isNeedVersion = true, + int activeRounds = -1, string valueType = StateDataType.String, string source = StateSource.User) { if (value == null) { @@ -56,18 +58,21 @@ public class ConversationStateService : IConversationStateService, IDisposable _logger.LogInformation($"[STATE] {name} = {value}"); var routingCtx = _services.GetRequiredService(); - foreach (var hook in hooks) + if (!ContainsState(name) || preValue != currentValue || preActiveRounds != curActiveRounds) { - hook.OnStateChanged(new StateChangeModel + foreach (var hook in hooks) { - ConversationId = _conversationId, - MessageId = routingCtx.MessageId, - Name = name, - BeforeValue = preValue, - BeforeActiveRounds = preActiveRounds, - AfterValue = currentValue, - AfterActiveRounds = curActiveRounds - }).Wait(); + hook.OnStateChanged(new StateChangeModel + { + ConversationId = _conversationId, + MessageId = routingCtx.MessageId, + Name = name, + BeforeValue = preValue, + BeforeActiveRounds = preActiveRounds, + AfterValue = currentValue, + AfterActiveRounds = curActiveRounds + }).Wait(); + } } var newPair = new StateKeyValue @@ -82,6 +87,8 @@ public class ConversationStateService : IConversationStateService, IDisposable MessageId = routingCtx.MessageId, Active = true, ActiveRounds = curActiveRounds, + DataType = valueType, + Source = source, UpdateTime = DateTime.UtcNow, }; @@ -132,6 +139,8 @@ public class ConversationStateService : IConversationStateService, IDisposable MessageId = curMsgId, Active = false, ActiveRounds = value.ActiveRounds, + DataType = value.DataType, + Source = value.Source, UpdateTime = DateTime.UtcNow }); continue; @@ -192,6 +201,8 @@ public class ConversationStateService : IConversationStateService, IDisposable MessageId = curMsgId, Active = false, ActiveRounds = lastValue.ActiveRounds, + DataType = lastValue.DataType, + Source = lastValue.Source, UpdateTime = utcNow }); } @@ -246,7 +257,7 @@ public class ConversationStateService : IConversationStateService, IDisposable { if (!string.IsNullOrEmpty(property.Value.ToString())) { - SetState(property.Name, property.Value); + SetState(property.Name, property.Value, source: StateSource.Application); } } } From 8168b22707ff2e09ca9c435c3b099ab94165f170 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 2 Apr 2024 14:09:42 -0500 Subject: [PATCH 003/201] refine element button --- .../Conversations/Models/StateChangeModel.cs | 6 +++ .../Messaging/BotSharpMessageParser.cs | 43 +++++++++++-------- .../RichContentJsonConverter .cs | 3 +- .../TemplateMessageJsonConverter.cs | 3 +- .../Models/RichContent/ElementButton.cs | 10 ++++- .../Template/ButtonTemplateMessage.cs | 21 +-------- .../Services/ConversationService.cs | 3 +- .../Services/ConversationStateService.cs | 4 +- .../Controllers/ConversationController.cs | 11 +++-- .../Hooks/StreamingLogHook.cs | 2 + .../Models/StateMongoElement.cs | 7 +++ 11 files changed, 62 insertions(+), 51 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateChangeModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateChangeModel.cs index f98a896d..ea602ad3 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateChangeModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateChangeModel.cs @@ -22,4 +22,10 @@ public class StateChangeModel [JsonPropertyName("after_active_rounds")] public int? AfterActiveRounds { get; set; } + + [JsonPropertyName("data_type")] + public string DataType { get; set; } + + [JsonPropertyName("source")] + public string Source { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs index 6697ef34..e22150e9 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs @@ -1,20 +1,19 @@ -using BotSharp.Abstraction.Messaging; using BotSharp.Abstraction.Messaging.Enums; using BotSharp.Abstraction.Messaging.Models.RichContent.Template; using BotSharp.Abstraction.Messaging.Models.RichContent; using System.Text.Json; using System.Reflection; using Newtonsoft.Json; -using JsonSerializer = System.Text.Json.JsonSerializer; -namespace BotSharp.Core.Messaging; +namespace BotSharp.Abstraction.Messaging; public static class BotSharpMessageParser { - public static IRichMessage? ParseRichMessage(JsonElement root, JsonSerializerOptions options) + public static IRichMessage? ParseRichMessage(JsonElement root) { IRichMessage? res = null; + Type? targetType = null; JsonElement element; var jsonText = root.GetRawText(); @@ -23,23 +22,23 @@ public static class BotSharpMessageParser var richType = element.GetString(); if (richType == RichTypeEnum.ButtonTemplate) { - res = JsonSerializer.Deserialize(jsonText, options); + targetType = typeof(ButtonTemplateMessage); } else if (richType == RichTypeEnum.MultiSelectTemplate) { - res = JsonSerializer.Deserialize(jsonText, options); + targetType = typeof(MultiSelectTemplateMessage); } else if (richType == RichTypeEnum.QuickReply) { - res = JsonSerializer.Deserialize(jsonText, options); + targetType = typeof(QuickReplyMessage); } else if (richType == RichTypeEnum.CouponTemplate) { - res = JsonSerializer.Deserialize(jsonText, options); + targetType = typeof(CouponTemplateMessage); } else if (richType == RichTypeEnum.Text) { - res = JsonSerializer.Deserialize(jsonText, options); + targetType = typeof(TextMessage); } else if (richType == RichTypeEnum.GenericTemplate) { @@ -51,19 +50,24 @@ public static class BotSharpMessageParser if (wrapperType != null && genericType != null) { - var targetType = wrapperType.MakeGenericType(genericType); - res = JsonConvert.DeserializeObject(jsonText, targetType) as IRichMessage; + targetType = wrapperType.MakeGenericType(genericType); } } } } + if (targetType != null) + { + res = JsonConvert.DeserializeObject(jsonText, targetType) as IRichMessage; + } + return res; } - public static ITemplateMessage? ParseTemplateMessage(JsonElement root, JsonSerializerOptions options) + public static ITemplateMessage? ParseTemplateMessage(JsonElement root) { ITemplateMessage? res = null; + Type? targetType = null; JsonElement element; var jsonText = root.GetRawText(); @@ -72,19 +76,19 @@ public static class BotSharpMessageParser var templateType = element.GetString(); if (templateType == TemplateTypeEnum.Button) { - res = JsonSerializer.Deserialize(jsonText, options); + targetType = typeof(ButtonTemplateMessage); } else if (templateType == TemplateTypeEnum.MultiSelect) { - res = JsonSerializer.Deserialize(jsonText, options); + targetType = typeof(MultiSelectTemplateMessage); } else if (templateType == TemplateTypeEnum.Coupon) { - res = JsonSerializer.Deserialize(jsonText, options); + targetType = typeof(CouponTemplateMessage); } else if (templateType == TemplateTypeEnum.Product) { - res = JsonSerializer.Deserialize(jsonText, options); + targetType = typeof(ProductTemplateMessage); } else if (templateType == TemplateTypeEnum.Generic) { @@ -96,13 +100,18 @@ public static class BotSharpMessageParser if (wrapperType != null && genericType != null) { - var targetType = wrapperType.MakeGenericType(genericType); + targetType = wrapperType.MakeGenericType(genericType); res = JsonConvert.DeserializeObject(jsonText, targetType) as ITemplateMessage; } } } } + if (targetType != null) + { + res = JsonConvert.DeserializeObject(jsonText, targetType) as ITemplateMessage; + } + return res; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/RichContentJsonConverter .cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/RichContentJsonConverter .cs index 94d12796..1748eb5a 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/RichContentJsonConverter .cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/RichContentJsonConverter .cs @@ -1,4 +1,3 @@ -using BotSharp.Core.Messaging; using System.Text.Json; namespace BotSharp.Abstraction.Messaging.JsonConverters; @@ -9,7 +8,7 @@ public class RichContentJsonConverter : JsonConverter { using var jsonDoc = JsonDocument.ParseValue(ref reader); var root = jsonDoc.RootElement; - var res = BotSharpMessageParser.ParseRichMessage(root, options); + var res = BotSharpMessageParser.ParseRichMessage(root); return res; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/TemplateMessageJsonConverter.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/TemplateMessageJsonConverter.cs index 84963d39..ce42c489 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/TemplateMessageJsonConverter.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/TemplateMessageJsonConverter.cs @@ -1,4 +1,3 @@ -using BotSharp.Core.Messaging; using System.Text.Json; namespace BotSharp.Abstraction.Messaging.JsonConverters; @@ -9,7 +8,7 @@ public class TemplateMessageJsonConverter : JsonConverter { using var jsonDoc = JsonDocument.ParseValue(ref reader); var root = jsonDoc.RootElement; - var res = BotSharpMessageParser.ParseTemplateMessage(root, options); + var res = BotSharpMessageParser.ParseTemplateMessage(root); return res; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs index b3642194..bc13660a 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs @@ -5,13 +5,19 @@ namespace BotSharp.Abstraction.Messaging.Models.RichContent; /// public class ElementButton { - public string Type { get; set; } + public string Type { get; set; } = "web_url"; [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string Url { get; set; } - public string Title { get; set; } + public string Title { get; set; } = string.Empty; [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string Payload { get; set; } + + [JsonPropertyName("is_primary")] + public bool IsPrimary { get; set; } + + [JsonPropertyName("is_secondary")] + public bool IsSecondary { get; set; } } 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 4c92a3fb..32ad73e2 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ButtonTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ButtonTemplateMessage.cs @@ -17,27 +17,8 @@ public class ButtonTemplateMessage : IRichMessage, ITemplateMessage public string TemplateType => TemplateTypeEnum.Button; [JsonPropertyName("buttons")] - public ButtonElement[] Buttons { get; set; } = new ButtonElement[0]; + public ElementButton[] Buttons { get; set; } = new ElementButton[0]; [JsonPropertyName("is_horizontal")] public bool IsHorizontal { get; set; } } - -public class ButtonElement -{ - /// - /// web_url, postback, phone_number - /// - public string Type { get; set; } = "web_url"; - - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string? Url { get; set; } - - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string? Payload { get; set; } - - public string Title { get; set; } = string.Empty; - - [JsonPropertyName("is_primary")] - public bool IsPrimary { get; set; } -} diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs index 8b3bb0fc..188715d4 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.Conversations.Enums; using BotSharp.Abstraction.Models; namespace BotSharp.Core.Conversations.Services; @@ -126,6 +127,6 @@ public partial class ConversationService : IConversationService { _conversationId = conversationId; _state.Load(_conversationId); - states.ForEach(x => _state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds)); + states.ForEach(x => _state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); } } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index 32fbb30a..c413d501 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -70,7 +70,9 @@ public class ConversationStateService : IConversationStateService, IDisposable BeforeValue = preValue, BeforeActiveRounds = preActiveRounds, AfterValue = currentValue, - AfterActiveRounds = curActiveRounds + AfterActiveRounds = curActiveRounds, + DataType = valueType, + Source = source }).Wait(); } } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index daf2b116..e01a0f46 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -1,5 +1,4 @@ using BotSharp.Abstraction.Routing; -using BotSharp.Abstraction.Users.Models; namespace BotSharp.OpenAPI.Controllers; @@ -166,11 +165,11 @@ public class ConversationController : ControllerBase routing.Context.SetMessageId(conversationId, inputMsg.MessageId); conv.SetConversationId(conversationId, input.States); - conv.States.SetState("channel", input.Channel) - .SetState("provider", input.Provider) - .SetState("model", input.Model) - .SetState("temperature", input.Temperature) - .SetState("sampling_factor", input.SamplingFactor); + conv.States.SetState("channel", input.Channel, source: StateSource.External) + .SetState("provider", input.Provider, source: StateSource.External) + .SetState("model", input.Model, source: StateSource.External) + .SetState("temperature", input.Temperature, source: StateSource.External) + .SetState("sampling_factor", input.SamplingFactor, source: StateSource.External); var response = new ChatResponseModel(); diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs index 2162bcb9..20f7a0a2 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs @@ -436,6 +436,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR BeforeActiveRounds = stateChange.BeforeActiveRounds, AfterValue = stateChange.AfterValue, AfterActiveRounds = stateChange.AfterActiveRounds, + DataType = stateChange.DataType, + Source = stateChange.Source, CreateTime = DateTime.UtcNow }; diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/StateMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/StateMongoElement.cs index b56483e1..54f67350 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/StateMongoElement.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/StateMongoElement.cs @@ -35,6 +35,9 @@ public class StateValueMongoElement public string? MessageId { get; set; } public bool Active { get; set; } public int ActiveRounds { get; set; } + public string DataType { get; set; } + public string Source { get; set; } + public DateTime UpdateTime { get; set; } public static StateValueMongoElement ToMongoElement(StateValue element) @@ -45,6 +48,8 @@ public class StateValueMongoElement MessageId = element.MessageId, Active = element.Active, ActiveRounds = element.ActiveRounds, + DataType = element.DataType, + Source = element.Source, UpdateTime = element.UpdateTime }; } @@ -57,6 +62,8 @@ public class StateValueMongoElement MessageId = element.MessageId, Active = element.Active, ActiveRounds = element.ActiveRounds, + DataType= element.DataType, + Source = element.Source, UpdateTime = element.UpdateTime }; } From da42be0477de0dfe3a941a3a479b7243970b9958 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 2 Apr 2024 14:24:04 -0500 Subject: [PATCH 004/201] minor change --- .../Conversations/Services/TokenStatistics.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/TokenStatistics.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/TokenStatistics.cs index 1ee38e2e..ca230ff3 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/TokenStatistics.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/TokenStatistics.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.Conversations.Enums; using BotSharp.Abstraction.MLTasks; using System.Diagnostics; using System.Drawing; @@ -47,14 +48,14 @@ public class TokenStatistics : ITokenStatistics // Accumulated Token var stat = _services.GetRequiredService(); var inputCount = int.Parse(stat.GetState("prompt_total", "0")); - stat.SetState("prompt_total", stats.PromptCount + inputCount, false); + stat.SetState("prompt_total", stats.PromptCount + inputCount, isNeedVersion: false, source: StateSource.Application); var outputCount = int.Parse(stat.GetState("completion_total", "0")); - stat.SetState("completion_total", stats.CompletionCount + outputCount, false); + stat.SetState("completion_total", stats.CompletionCount + outputCount, isNeedVersion: false, source: StateSource.Application); // Total cost var total_cost = float.Parse(stat.GetState("llm_total_cost", "0")); total_cost += Cost; - stat.SetState("llm_total_cost", total_cost, false); + stat.SetState("llm_total_cost", total_cost, isNeedVersion: false, source: StateSource.Application); } public void PrintStatistics() From 40bafdc327bff9e0b1ffcd648a2df3520ebd03fe Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 2 Apr 2024 15:39:14 -0500 Subject: [PATCH 005/201] add readonly --- .../Conversations/IConversationStateService.cs | 2 +- .../Conversations/Models/StateChangeModel.cs | 3 +++ .../Conversations/Models/StateKeyValue.cs | 1 + .../Conversations/Services/ConversationStateService.cs | 8 +++++--- .../BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs | 1 + .../Models/StateMongoElement.cs | 3 +++ 6 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs index d71111fd..4b28c1d3 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs @@ -14,7 +14,7 @@ public interface IConversationStateService bool ContainsState(string name); Dictionary GetStates(); IConversationStateService SetState(string name, T value, bool isNeedVersion = true, - int activeRounds = -1, string valueType = StateDataType.String, string source = StateSource.User); + int activeRounds = -1, string valueType = StateDataType.String, string source = StateSource.User, bool readOnly = false); void SaveStateByArgs(JsonDocument args); void CleanStates(); void Save(); diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateChangeModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateChangeModel.cs index ea602ad3..26c10a71 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateChangeModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateChangeModel.cs @@ -28,4 +28,7 @@ public class StateChangeModel [JsonPropertyName("source")] public string Source { get; set; } + + [JsonPropertyName("readonly")] + public bool Readonly { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateKeyValue.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateKeyValue.cs index c70dbf03..4afea9da 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateKeyValue.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateKeyValue.cs @@ -6,6 +6,7 @@ public class StateKeyValue { public string Key { get; set; } public bool Versioning { get; set; } + public bool Readonly { get; set; } public List Values { get; set; } = new List(); public StateKeyValue() diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index c413d501..b87a8fd7 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -35,7 +35,7 @@ public class ConversationStateService : IConversationStateService, IDisposable /// whether the state is related to message or not /// public IConversationStateService SetState(string name, T value, bool isNeedVersion = true, - int activeRounds = -1, string valueType = StateDataType.String, string source = StateSource.User) + int activeRounds = -1, string valueType = StateDataType.String, string source = StateSource.User, bool readOnly = false) { if (value == null) { @@ -72,7 +72,8 @@ public class ConversationStateService : IConversationStateService, IDisposable AfterValue = currentValue, AfterActiveRounds = curActiveRounds, DataType = valueType, - Source = source + Source = source, + Readonly = readOnly }).Wait(); } } @@ -80,7 +81,8 @@ public class ConversationStateService : IConversationStateService, IDisposable var newPair = new StateKeyValue { Key = name, - Versioning = isNeedVersion + Versioning = isNeedVersion, + Readonly = readOnly }; var newValue = new StateValue diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs index 20f7a0a2..2e2ed3d6 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs @@ -438,6 +438,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR AfterActiveRounds = stateChange.AfterActiveRounds, DataType = stateChange.DataType, Source = stateChange.Source, + Readonly = stateChange.Readonly, CreateTime = DateTime.UtcNow }; diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/StateMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/StateMongoElement.cs index 54f67350..a939f59c 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/StateMongoElement.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/StateMongoElement.cs @@ -6,6 +6,7 @@ public class StateMongoElement { public string Key { get; set; } public bool Versioning { get; set; } + public bool Readonly { get; set; } public List Values { get; set; } public static StateMongoElement ToMongoElement(StateKeyValue state) @@ -14,6 +15,7 @@ public class StateMongoElement { Key = state.Key, Versioning = state.Versioning, + Readonly = state.Readonly, Values = state.Values?.Select(x => StateValueMongoElement.ToMongoElement(x))?.ToList() ?? new List() }; } @@ -24,6 +26,7 @@ public class StateMongoElement { Key = state.Key, Versioning = state.Versioning, + Readonly = state.Readonly, Values = state.Values?.Select(x => StateValueMongoElement.ToDomainElement(x))?.ToList() ?? new List() }; } From 551c2f8ed4d1db9ca1fe58923a21dd384e30de68 Mon Sep 17 00:00:00 2001 From: Haiping Chen <101423@smsassist.com> Date: Wed, 3 Apr 2024 09:47:32 -0500 Subject: [PATCH 006/201] check field type in routing. --- .../RoutingService.HasMissingRequiredField.cs | 12 ++++++++++++ .../Models/RoutingRuleMongoElement.cs | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.HasMissingRequiredField.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.HasMissingRequiredField.cs index 20d75a95..2a6b676c 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.HasMissingRequiredField.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.HasMissingRequiredField.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.Conversations.Enums; using BotSharp.Abstraction.Routing.Models; using System.Drawing; @@ -49,6 +50,17 @@ public partial class RoutingService if (!string.IsNullOrEmpty(states.GetState(field))) { var value = states.GetState(field); + + // Check if the value is correct data type + var rule = routingRules.First(x => x.Field == field); + if (rule.FieldType == "number") + { + if (!long.TryParse(value, out var longValue)) + { + states.SetState(field, "", isNeedVersion: true, source: StateSource.Application); + continue; + } + } message.FunctionArgs = AppendPropertyToArgs(message.FunctionArgs, field, value); missingFields.Remove(field); } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/RoutingRuleMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/RoutingRuleMongoElement.cs index 571c6d09..293ffc47 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/RoutingRuleMongoElement.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/RoutingRuleMongoElement.cs @@ -10,6 +10,7 @@ public class RoutingRuleMongoElement public bool Required { get; set; } public string? RedirectTo { get; set; } public string Type { get; set; } + public string FieldType { get; set; } public RoutingRuleMongoElement() { @@ -25,6 +26,7 @@ public class RoutingRuleMongoElement Required = routingRule.Required, RedirectTo = routingRule.RedirectTo, Type = routingRule.Type, + FieldType = routingRule.FieldType }; } @@ -39,6 +41,12 @@ public class RoutingRuleMongoElement Required = rule.Required, RedirectTo = rule.RedirectTo, Type = rule.Type, + FieldType= rule.FieldType }; } + + public override string ToString() + { + return $"{Field} - {FieldType}, Required: {Required} ({Type})"; + } } From d2d28be378a37cba7f9249b525b085956f99d472 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 3 Apr 2024 12:12:31 -0500 Subject: [PATCH 007/201] optimize state --- .../IConversationStateService.cs | 1 + .../Services/ConversationStateService.cs | 174 ++++++++++++------ .../Functions/GetPizzaTypesFn.cs | 2 +- 3 files changed, 121 insertions(+), 56 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs index 4b28c1d3..655e657a 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs @@ -16,6 +16,7 @@ public interface IConversationStateService IConversationStateService SetState(string name, T value, bool isNeedVersion = true, int activeRounds = -1, string valueType = StateDataType.String, string source = StateSource.User, bool readOnly = false); void SaveStateByArgs(JsonDocument args); + bool RemoveState(string name); void CleanStates(); void Save(); } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index b87a8fd7..5f632bd6 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -10,9 +10,16 @@ public class ConversationStateService : IConversationStateService, IDisposable { private readonly ILogger _logger; private readonly IServiceProvider _services; - private ConversationState _states; - private string _conversationId; private readonly IBotSharpRepository _db; + private string _conversationId; + /// + /// States in the current round of conversation + /// + private ConversationState _curStates; + /// + /// States in the previous rounds of conversation + /// + private ConversationState _historyStates; public ConversationStateService(ILogger logger, IServiceProvider services, @@ -21,7 +28,8 @@ public class ConversationStateService : IConversationStateService, IDisposable _logger = logger; _services = services; _db = db; - _states = new ConversationState(); + _curStates = new ConversationState(); + _historyStates = new ConversationState(); } public string GetConversationId() => _conversationId; @@ -48,7 +56,7 @@ public class ConversationStateService : IConversationStateService, IDisposable var curActiveRounds = activeRounds > 0 ? activeRounds : -1; int? preActiveRounds = null; - if (ContainsState(name) && _states.TryGetValue(name, out var pair)) + if (ContainsState(name) && _curStates.TryGetValue(name, out var pair)) { var lastNode = pair?.Values?.LastOrDefault(); preActiveRounds = lastNode?.ActiveRounds; @@ -96,14 +104,14 @@ public class ConversationStateService : IConversationStateService, IDisposable UpdateTime = DateTime.UtcNow, }; - if (!isNeedVersion || !_states.ContainsKey(name)) + if (!isNeedVersion || !_curStates.ContainsKey(name)) { newPair.Values = new List { newValue }; - _states[name] = newPair; + _curStates[name] = newPair; } else { - _states[name].Values.Add(newValue); + _curStates[name].Values.Add(newValue); } return this; @@ -115,56 +123,68 @@ public class ConversationStateService : IConversationStateService, IDisposable var routingCtx = _services.GetRequiredService(); var curMsgId = routingCtx.MessageId; - _states = _db.GetConversationStates(_conversationId); + + _historyStates = _db.GetConversationStates(_conversationId); var dialogs = _db.GetConversationDialogs(_conversationId); var userDialogs = dialogs.Where(x => x.MetaData?.Role == AgentRole.User || x.MetaData?.Role == UserRole.Client) .OrderBy(x => x.MetaData?.CreateTime) .ToList(); - var curMsgIndex = userDialogs.FindIndex(x => !string.IsNullOrEmpty(curMsgId) && x.MetaData?.MessageId == curMsgId); curMsgIndex = curMsgIndex < 0 ? userDialogs.Count() : curMsgIndex; - var curStates = new Dictionary(); - if (!_states.IsNullOrEmpty()) + var endNodes = new Dictionary(); + if (_historyStates.IsNullOrEmpty()) return endNodes; + + foreach (var state in _historyStates) { - foreach (var state in _states) + var key = state.Key; + var value = state.Value; + var leafNode = value.Values.LastOrDefault(); + if (leafNode == null) continue; + + _curStates[key] = new StateKeyValue { - var value = state.Value?.Values?.LastOrDefault(); - if (value == null || !value.Active) continue; + Key = key, + Versioning = value.Versioning, + Readonly = value.Readonly, + Values = new List { leafNode } + }; - if (value.ActiveRounds > 0) + if (!leafNode.Active) continue; + + // Handle state active rounds + if (leafNode.ActiveRounds > 0) + { + var stateMsgIndex = userDialogs.FindIndex(x => !string.IsNullOrEmpty(x.MetaData?.MessageId) && x.MetaData.MessageId == leafNode.MessageId); + if (stateMsgIndex >= 0 && curMsgIndex - stateMsgIndex >= leafNode.ActiveRounds) { - var stateMsgIndex = userDialogs.FindIndex(x => !string.IsNullOrEmpty(x.MetaData?.MessageId) && x.MetaData.MessageId == value.MessageId); - if (stateMsgIndex >= 0 && curMsgIndex - stateMsgIndex >= value.ActiveRounds) + _curStates[key].Values.Add(new StateValue { - state.Value.Values.Add(new StateValue - { - Data = value.Data, - MessageId = curMsgId, - Active = false, - ActiveRounds = value.ActiveRounds, - DataType = value.DataType, - Source = value.Source, - UpdateTime = DateTime.UtcNow - }); - continue; - } + Data = leafNode.Data, + MessageId = curMsgId, + Active = false, + ActiveRounds = leafNode.ActiveRounds, + DataType = leafNode.DataType, + Source = leafNode.Source, + UpdateTime = DateTime.UtcNow + }); + continue; } - - var data = value.Data ?? string.Empty; - curStates[state.Key] = data; - _logger.LogInformation($"[STATE] {state.Key} : {data}"); } + + var data = leafNode.Data ?? string.Empty; + endNodes[state.Key] = data; + _logger.LogInformation($"[STATE] {key} : {data}"); } _logger.LogInformation($"Loaded conversation states: {_conversationId}"); var hooks = _services.GetServices(); foreach (var hook in hooks) { - hook.OnStateLoaded(_states).Wait(); + hook.OnStateLoaded(_curStates).Wait(); } - return curStates; + return endNodes; } public void Save() @@ -176,37 +196,81 @@ public class ConversationStateService : IConversationStateService, IDisposable var states = new List(); - foreach (var dic in _states) + foreach (var pair in _curStates) { - states.Add(dic.Value); + var key = pair.Key; + var curValue = pair.Value; + + if (!_historyStates.TryGetValue(key, out var historyValue) + || historyValue == null + || historyValue.Values.IsNullOrEmpty() + || !curValue.Versioning) + { + states.Add(curValue); + } + else + { + var historyValues = historyValue.Values.Take(historyValue.Values.Count - 1).ToList(); + var newValues = historyValues.Concat(curValue.Values).ToList(); + var updatedNode = new StateKeyValue + { + Key = pair.Key, + Versioning = curValue.Versioning, + Readonly = curValue.Readonly, + Values = newValues + }; + states.Add(updatedNode); + } } _db.UpdateConversationStates(_conversationId, states); _logger.LogInformation($"Saved states of conversation {_conversationId}"); } + public bool RemoveState(string name) + { + if (!ContainsState(name)) return false; + + var routingCtx = _services.GetRequiredService(); + var leafNode = _curStates[name].Values?.LastOrDefault(); + if (leafNode == null) return false; + + _curStates[name].Values.Add(new StateValue + { + Data = leafNode.Data, + MessageId = routingCtx.MessageId, + Active = false, + ActiveRounds = leafNode.ActiveRounds, + DataType = leafNode.DataType, + Source = leafNode.Source, + UpdateTime = DateTime.UtcNow + }); + + return true; + } + public void CleanStates() { var routingCtx = _services.GetRequiredService(); var curMsgId = routingCtx.MessageId; var utcNow = DateTime.UtcNow; - foreach (var key in _states.Keys) + foreach (var key in _curStates.Keys) { - var value = _states[key]; + var value = _curStates[key]; if (value == null || !value.Versioning || value.Values.IsNullOrEmpty()) continue; - var lastValue = value.Values.LastOrDefault(); - if (lastValue == null || !lastValue.Active) continue; + var leafNode = value.Values.LastOrDefault(); + if (leafNode == null || !leafNode.Active) continue; value.Values.Add(new StateValue { - Data = lastValue.Data, + Data = leafNode.Data, MessageId = curMsgId, Active = false, - ActiveRounds = lastValue.ActiveRounds, - DataType = lastValue.DataType, - Source = lastValue.Source, + ActiveRounds = leafNode.ActiveRounds, + DataType = leafNode.DataType, + Source = leafNode.Source, UpdateTime = utcNow }); } @@ -214,25 +278,25 @@ public class ConversationStateService : IConversationStateService, IDisposable public Dictionary GetStates() { - var curStates = new Dictionary(); - foreach (var state in _states) + var endNodes = new Dictionary(); + foreach (var state in _curStates) { var value = state.Value?.Values?.LastOrDefault(); if (value == null || !value.Active) continue; - curStates[state.Key] = value.Data ?? string.Empty; + endNodes[state.Key] = value.Data ?? string.Empty; } - return curStates; + return endNodes; } public string GetState(string name, string defaultValue = "") { - if (!_states.ContainsKey(name) || _states[name].Values.IsNullOrEmpty() || !_states[name].Values.Last().Active) + if (!_curStates.ContainsKey(name) || _curStates[name].Values.IsNullOrEmpty() || !_curStates[name].Values.Last().Active) { return defaultValue; } - return _states[name].Values.Last().Data; + return _curStates[name].Values.Last().Data; } public void Dispose() @@ -242,10 +306,10 @@ public class ConversationStateService : IConversationStateService, IDisposable public bool ContainsState(string name) { - return _states.ContainsKey(name) - && !_states[name].Values.IsNullOrEmpty() - && _states[name].Values.LastOrDefault()?.Active == true - && !string.IsNullOrEmpty(_states[name].Values.Last().Data); + return _curStates.ContainsKey(name) + && !_curStates[name].Values.IsNullOrEmpty() + && _curStates[name].Values.LastOrDefault()?.Active == true + && !string.IsNullOrEmpty(_curStates[name].Values.Last().Data); } public void SaveStateByArgs(JsonDocument args) diff --git a/tests/BotSharp.Plugin.PizzaBot/Functions/GetPizzaTypesFn.cs b/tests/BotSharp.Plugin.PizzaBot/Functions/GetPizzaTypesFn.cs index 1842f557..9a239fd2 100644 --- a/tests/BotSharp.Plugin.PizzaBot/Functions/GetPizzaTypesFn.cs +++ b/tests/BotSharp.Plugin.PizzaBot/Functions/GetPizzaTypesFn.cs @@ -38,7 +38,7 @@ public class GetPizzaTypesFn : IFunctionCallback Message = new ButtonTemplateMessage { Text = "Please select a pizza type", - Buttons = pizzaTypes.Select(x => new ButtonElement + Buttons = pizzaTypes.Select(x => new ElementButton { Type = "text", Title = x, From 093663d436032d8c0f4dd7102c1b0dd7f87de497 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 3 Apr 2024 14:51:07 -0500 Subject: [PATCH 008/201] add channel --- .../BotSharp.Abstraction/Google/Models/GoogleVideoResult.cs | 3 ++- .../BotSharp.Abstraction/Google/Settings/GoogleApiSettings.cs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Google/Models/GoogleVideoResult.cs b/src/Infrastructure/BotSharp.Abstraction/Google/Models/GoogleVideoResult.cs index 442290aa..49e50c30 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Google/Models/GoogleVideoResult.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Google/Models/GoogleVideoResult.cs @@ -3,7 +3,7 @@ namespace BotSharp.Abstraction.Google.Models; public class GoogleVideoResult { public string Kind { get; set; } - public IList Items { get; set; } = new List(); + public List Items { get; set; } = new List(); } public class VideoItem @@ -25,6 +25,7 @@ public class VideoSnippet { public string Title { get; set; } public string Description { get; set; } + public string ChannelId { get; set; } public string ChannelTitle { get; set; } public VideoThumbnails Thumbnails { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Google/Settings/GoogleApiSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Google/Settings/GoogleApiSettings.cs index be7ef6a2..dfa66f60 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Google/Settings/GoogleApiSettings.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Google/Settings/GoogleApiSettings.cs @@ -19,4 +19,5 @@ public class YoutubeSettings public string Endpoint { get; set; } public string Part { get; set; } public string RegionCode { get; set; } + public IList Channels { get; set; } } \ No newline at end of file From 2a4c53f4da45a670e3f06cd4004547b1690001e2 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 3 Apr 2024 15:03:43 -0500 Subject: [PATCH 009/201] add setting --- src/WebStarter/appsettings.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index 0db89cbd..83b870b7 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -229,7 +229,8 @@ "Youtube": { "Endpoint": "https://www.googleapis.com/youtube/v3/search", "RegionCode": "US", - "Part": "id,snippet" + "Part": "id,snippet", + "Channels": [] } }, From cc6244a301607c31476c66f709906aa9eae35015 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Wed, 3 Apr 2024 20:05:10 -0500 Subject: [PATCH 010/201] HttpRequestParams --- .../Browsing/IWebBrowser.cs | 2 +- .../Browsing/Models/BrowsingContextIn.cs | 6 ----- .../Browsing/Models/HttpRequestParams.cs | 25 +++++++++++++++++++ .../PlaywrightWebDriver.HttpRequest.cs | 17 +++++++++---- .../Functions/HttpRequestFn.cs | 4 +-- 5 files changed, 40 insertions(+), 14 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Browsing/Models/HttpRequestParams.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs index 376c7cac..f732b801 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs @@ -23,6 +23,6 @@ public interface IWebBrowser Task ExtractData(BrowserActionParams actionParams); Task EvaluateScript(string conversationId, string script); Task CloseBrowser(string conversationId); - Task SendHttpRequest(BrowserActionParams actionParams); + Task SendHttpRequest(HttpRequestParams actionParams); Task GetAttributeValue(MessageInfo message, ElementLocatingArgs location, BrowserActionResult result); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowsingContextIn.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowsingContextIn.cs index f613738b..ff3b94f3 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowsingContextIn.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowsingContextIn.cs @@ -40,10 +40,4 @@ public class BrowsingContextIn [JsonPropertyName("direction")] public string? Direction { get; set; } - - /// - /// Http request payload - /// - [JsonPropertyName("payload")] - public string? Payload { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/HttpRequestParams.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/HttpRequestParams.cs new file mode 100644 index 00000000..2b8cc052 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/HttpRequestParams.cs @@ -0,0 +1,25 @@ +using System.Net.Http; + +namespace BotSharp.Abstraction.Browsing.Models; + +public class HttpRequestParams +{ + [JsonPropertyName("url")] + public string Url { get; set; } = string.Empty; + + [JsonPropertyName("method")] + public HttpMethod Method { get; set; } + + /// + /// Http request payload + /// + [JsonPropertyName("payload")] + public string? Payload { get; set; } + + public HttpRequestParams(string url, HttpMethod method, string? payload = null) + { + Method = HttpMethod.Get; + Url = url; + Payload = payload; + } +} diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs index 404fd3c9..8d54a430 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs @@ -1,27 +1,34 @@ +using System.Net.Http; + namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver { - public async Task SendHttpRequest(BrowserActionParams actionParams) + public async Task SendHttpRequest(HttpRequestParams args) { var result = new BrowserActionResult(); + + var body = args.Method == HttpMethod.Post ? + $"body: '{args.Payload}'" : string.Empty; + // Send AJAX request string script = $@" (async () => {{ - const response = await fetch('{actionParams.Context.Url}', {{ - method: 'POST', + const response = await fetch('{args.Url}', {{ + method: '{args.Method}', headers: {{ 'Content-Type': 'application/json' }}, - body: '{actionParams.Context.Payload}' + {body} }}); return await response.json(); }})(); "; + var conv = _services.GetRequiredService(); try { - var response = await EvaluateScript(actionParams.ConversationId, script); + var response = await EvaluateScript(conv.ConversationId, script); result.IsSuccess = true; result.Body = JsonSerializer.Serialize(response); } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/HttpRequestFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/HttpRequestFn.cs index 17b84a1b..1e42df1f 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/HttpRequestFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/HttpRequestFn.cs @@ -17,11 +17,11 @@ public class HttpRequestFn : IFunctionCallback public async Task Execute(RoleDialogModel message) { var convService = _services.GetRequiredService(); - var args = JsonSerializer.Deserialize(message.FunctionArgs); + var args = JsonSerializer.Deserialize(message.FunctionArgs); var agentService = _services.GetRequiredService(); var agent = await agentService.LoadAgent(message.CurrentAgentId); - var result = await _browser.SendHttpRequest(new BrowserActionParams(agent, args, convService.ConversationId, message.MessageId)); + var result = await _browser.SendHttpRequest(args); message.Content = result.IsSuccess ? result.Body : From a432e3ca9eef962b4fb9861b7d3cd72ba69aeeeb Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 4 Apr 2024 17:41:52 -0500 Subject: [PATCH 011/201] enable plugin by dependency --- .../BotSharp.Core/Plugins/PluginLoader.cs | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs b/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs index 0932041d..06ebd0a6 100644 --- a/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs +++ b/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs @@ -144,9 +144,12 @@ public class PluginLoader var config = db.GetPluginConfig(); if (enable) { - if (!config.EnabledPlugins.Exists(x => x == id)) + var dependentPlugins = new HashSet(); + FindPluginDependency(id, enable, ref dependentPlugins); + var missingPlugins = dependentPlugins.Where(x => !config.EnabledPlugins.Contains(x)).ToList(); + if (!missingPlugins.IsNullOrEmpty()) { - config.EnabledPlugins.Add(id); + config.EnabledPlugins.AddRange(missingPlugins); db.SavePluginConfig(config); } @@ -186,6 +189,36 @@ public class PluginLoader return plugin; } + private void FindPluginDependency(string pluginId, bool enabled, ref HashSet dependentPlugins) + { + var pluginDef = _plugins.FirstOrDefault(x => x.Id == pluginId); + if (pluginDef == null) return; + + if (!pluginDef.IsCore) + { + pluginDef.Enabled = enabled; + dependentPlugins.Add(pluginId); + } + + var foundPlugin = _modules.FirstOrDefault(x => x.Id == pluginId); + if (foundPlugin == null) return; + + var attr = foundPlugin.GetType().GetCustomAttribute(); + if (attr != null && !attr.PluginNames.IsNullOrEmpty()) + { + foreach (var name in attr.PluginNames) + { + var plugins = _plugins.Where(x => x.Assembly == name).ToList(); + if (plugins.IsNullOrEmpty()) return; + + foreach (var plugin in plugins) + { + FindPluginDependency(plugin.Id, enabled, ref dependentPlugins); + } + } + } + } + public string GetSummaryComment(Type member) { string summary = string.Empty; From 575d2984010165fcdacb313b8d73267aac33f0f1 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Thu, 4 Apr 2024 19:14:09 -0500 Subject: [PATCH 012/201] enable agent id --- .../BotSharp.Core/Plugins/PluginLoader.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs b/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs index 06ebd0a6..72d55b45 100644 --- a/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs +++ b/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs @@ -145,7 +145,8 @@ public class PluginLoader if (enable) { var dependentPlugins = new HashSet(); - FindPluginDependency(id, enable, ref dependentPlugins); + var dependentAgentIds = new HashSet(); + FindPluginDependency(id, enable, ref dependentPlugins, ref dependentAgentIds); var missingPlugins = dependentPlugins.Where(x => !config.EnabledPlugins.Contains(x)).ToList(); if (!missingPlugins.IsNullOrEmpty()) { @@ -155,7 +156,7 @@ public class PluginLoader // enable agents var agentService = services.GetRequiredService(); - foreach (var agentId in plugin.AgentIds) + foreach (var agentId in dependentAgentIds) { var agent = agentService.LoadAgent(agentId).Result; agent.Disabled = false; @@ -189,7 +190,7 @@ public class PluginLoader return plugin; } - private void FindPluginDependency(string pluginId, bool enabled, ref HashSet dependentPlugins) + private void FindPluginDependency(string pluginId, bool enabled, ref HashSet dependentPlugins, ref HashSet dependentAgentIds) { var pluginDef = _plugins.FirstOrDefault(x => x.Id == pluginId); if (pluginDef == null) return; @@ -198,6 +199,13 @@ public class PluginLoader { pluginDef.Enabled = enabled; dependentPlugins.Add(pluginId); + if (!pluginDef.AgentIds.IsNullOrEmpty()) + { + foreach (var agentId in pluginDef.AgentIds) + { + dependentAgentIds.Add(agentId); + } + } } var foundPlugin = _modules.FirstOrDefault(x => x.Id == pluginId); @@ -213,7 +221,7 @@ public class PluginLoader foreach (var plugin in plugins) { - FindPluginDependency(plugin.Id, enabled, ref dependentPlugins); + FindPluginDependency(plugin.Id, enabled, ref dependentPlugins, ref dependentAgentIds); } } } From b7c80cf11ba9ec050bf8f45b0b773ae2b86345e1 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Thu, 4 Apr 2024 21:43:13 -0500 Subject: [PATCH 013/201] Upgrade EntityFrameworkCore.BootKit --- src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index da47ccb5..ece7daa7 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -133,7 +133,7 @@ - + From d530b59b1afac7ea8eb3d2eb1f3bf7e287b7ed9f Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Fri, 5 Apr 2024 13:53:37 -0500 Subject: [PATCH 014/201] patch state source --- .../Controllers/InstructModeController.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs index 725b6e2b..985526d9 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs @@ -22,12 +22,12 @@ public class InstructModeController : ControllerBase [FromBody] InstructMessageModel input) { var state = _services.GetRequiredService(); - input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds)); - state.SetState("provider", input.Provider) - .SetState("model", input.Model) - .SetState("model_id", input.ModelId) - .SetState("instruction", input.Instruction) - .SetState("input_text", input.Text); + input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); + state.SetState("provider", input.Provider, source: StateSource.External) + .SetState("model", input.Model, source: StateSource.External) + .SetState("model_id", input.ModelId, source: StateSource.External) + .SetState("instruction", input.Instruction, source: StateSource.External) + .SetState("input_text", input.Text,source: StateSource.External); var instructor = _services.GetRequiredService(); var result = await instructor.Execute(agentId, @@ -44,10 +44,10 @@ public class InstructModeController : ControllerBase public async Task TextCompletion([FromBody] IncomingMessageModel input) { var state = _services.GetRequiredService(); - input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds)); - state.SetState("provider", input.Provider) - .SetState("model", input.Model) - .SetState("model_id", input.ModelId); + input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); + state.SetState("provider", input.Provider, source: StateSource.External) + .SetState("model", input.Model, source: StateSource.External) + .SetState("model_id", input.ModelId, source: StateSource.External); var textCompletion = CompletionProvider.GetTextCompletion(_services); return await textCompletion.GetCompletion(input.Text, Guid.Empty.ToString(), Guid.NewGuid().ToString()); @@ -57,10 +57,10 @@ public class InstructModeController : ControllerBase public async Task ChatCompletion([FromBody] IncomingMessageModel input) { var state = _services.GetRequiredService(); - input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds)); - state.SetState("provider", input.Provider) - .SetState("model", input.Model) - .SetState("model_id", input.ModelId); + input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); + state.SetState("provider", input.Provider, source: StateSource.External) + .SetState("model", input.Model, source: StateSource.External) + .SetState("model_id", input.ModelId, source: StateSource.External); var textCompletion = CompletionProvider.GetChatCompletion(_services); var message = await textCompletion.GetChatCompletions(new Agent() From 3c968bd068c6f4559c34f701121da57c75d5398e Mon Sep 17 00:00:00 2001 From: Haiping Chen <101423@smsassist.com> Date: Sat, 6 Apr 2024 18:33:49 -0500 Subject: [PATCH 015/201] PostbackFunctionName --- .../Conversations/Models/RoleDialogModel.cs | 4 ++ .../ConversationService.SendMessage.cs | 15 ++++-- .../Services/ConversationStorage.cs | 4 +- .../Routing/RoutingService.InvokeFunction.cs | 51 ++++++++++--------- .../Hooks/StreamingLogHook.cs | 27 +++++++++- 5 files changed, 70 insertions(+), 31 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs index 96d933f5..dea85881 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs @@ -35,6 +35,9 @@ public class RoleDialogModel : ITrackableMessage [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? FunctionName { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? PostbackFunctionName { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? FunctionArgs { get; set; } @@ -95,6 +98,7 @@ public class RoleDialogModel : ITrackableMessage MessageId = source.MessageId, FunctionArgs = source.FunctionArgs, FunctionName = source.FunctionName, + PostbackFunctionName = source.PostbackFunctionName, RichContent = source.RichContent, StopCompletion = source.StopCompletion, Instruction = source.Instruction, diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs index 9d01f355..eaa24fa3 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -72,10 +72,13 @@ public partial class ConversationService } // Persist to storage - _storage.Append(_conversationId, message); + if (!message.StopCompletion) + { + _storage.Append(_conversationId, message); - // Add to thread - dialogs.Add(RoleDialogModel.From(message)); + // Add to thread + dialogs.Add(RoleDialogModel.From(message)); + } if (!stopCompletion) { @@ -147,6 +150,12 @@ public partial class ConversationService Message = new TextMessage(response.Content) }; + // Patch return function name + if (response.PostbackFunctionName != null) + { + response.FunctionName = response.PostbackFunctionName; + } + var hooks = _services.GetServices().ToList(); if (response.Instruction != null) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs index e523ce48..532f56d4 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs @@ -28,11 +28,11 @@ public class ConversationStorage : IConversationStorage var dialogElements = new List(); // Prevent duplicate record to be inserted - var dialogs = db.GetConversationDialogs(conversationId); + /*var dialogs = db.GetConversationDialogs(conversationId); if (dialogs.Any(x => x.MetaData.MessageId == dialog.MessageId && x.Content == dialog.Content)) { return; - } + }*/ if (dialog.Role == AgentRole.Function) { diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs index 6ad2ae75..86839b67 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs @@ -5,6 +5,8 @@ namespace BotSharp.Core.Routing; public partial class RoutingService { private List _functionCallStack = new List(); + public List FunctionCallStack => _functionCallStack; + public async Task InvokeFunction(string name, RoleDialogModel message) { var function = _services.GetServices().FirstOrDefault(x => x.Name == name); @@ -16,10 +18,9 @@ public partial class RoutingService return false; } - var originalFunctionName = message.FunctionName; - message.FunctionName = name; - message.Role = AgentRole.Function; - message.FunctionArgs = message.FunctionArgs; + // Clone message + var clonedMessage = RoleDialogModel.From(message); + clonedMessage.FunctionName = name; var hooks = _services.GetServices() .OrderBy(x => x.Priority) @@ -28,21 +29,36 @@ public partial class RoutingService // Before executing functions foreach (var hook in hooks) { - await hook.OnFunctionExecuting(message); + await hook.OnFunctionExecuting(clonedMessage); } bool result = false; try { - result = await function.Execute(message); + result = await function.Execute(clonedMessage); + _functionCallStack.Add(new FunctionCallingResponse { Role = AgentRole.Function, - FunctionName = message.FunctionName, - Args = JsonDocument.Parse(message.FunctionArgs ?? "{}"), - Content = message.Content + FunctionName = clonedMessage.FunctionName, + Args = JsonDocument.Parse(clonedMessage.FunctionArgs ?? "{}"), + Content = clonedMessage.Content }); + + // After functions have been executed + foreach (var hook in hooks) + { + await hook.OnFunctionExecuted(clonedMessage); + } + + // Set result to original message + message.PostbackFunctionName = clonedMessage.PostbackFunctionName; + message.CurrentAgentId = clonedMessage.CurrentAgentId; + message.Content = clonedMessage.Content; + message.StopCompletion = clonedMessage.StopCompletion; + message.RichContent = clonedMessage.RichContent; + message.Data = clonedMessage.Data; } catch (JsonException ex) { @@ -63,25 +79,12 @@ public partial class RoutingService message.Content = JsonSerializer.Serialize(message.Data); } - // After functions have been executed - foreach (var hook in hooks) - { - await hook.OnFunctionExecuted(message); - } - - // restore original function name - if (!message.StopCompletion && - message.FunctionName != originalFunctionName) - { - message.FunctionName = originalFunctionName; - } - // Save to Storage as well - if (!message.StopCompletion && message.FunctionName != "route_to_agent") + /*if (!message.StopCompletion && message.FunctionName != "route_to_agent") { var storage = _services.GetRequiredService(); storage.Append(Context.ConversationId, message); - } + }*/ return result; } diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs index 2e2ed3d6..e1b5fd0f 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs @@ -98,7 +98,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR if (!_convSettings.ShowVerboseLog) return; } - public override async Task OnFunctionExecuted(RoleDialogModel message) + public override async Task OnFunctionExecuting(RoleDialogModel message) { if (message.FunctionName == "route_to_agent") { @@ -109,7 +109,30 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR var agent = await _agentService.LoadAgent(message.CurrentAgentId); message.FunctionArgs = message.FunctionArgs ?? "{}"; var args = JsonSerializer.Serialize(JsonDocument.Parse(message.FunctionArgs), _options.JsonSerializerOptions); - var log = $"*{message.FunctionName}*\r\n```json\r\n{args}\r\n```\r\n=> {message.Content?.Trim()}"; + var log = $"{message.FunctionName} executing\r\n```json\r\n{args}\r\n```"; + + var input = new ContentLogInputModel(conversationId, message) + { + Name = agent?.Name, + AgentId = agent?.Id, + Source = ContentLogSource.FunctionCall, + Log = log + }; + await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input)); + } + + public override async Task OnFunctionExecuted(RoleDialogModel message) + { + if (message.FunctionName == "route_to_agent") + { + return; + } + + var conversationId = _state.GetConversationId(); + var agent = await _agentService.LoadAgent(message.CurrentAgentId); + message.FunctionArgs = message.FunctionArgs ?? "{}"; + // var args = JsonSerializer.Serialize(JsonDocument.Parse(message.FunctionArgs), _options.JsonSerializerOptions); + var log = $"{message.FunctionName} =>\r\n*{message.Content?.Trim()}*"; var input = new ContentLogInputModel(conversationId, message) { From 491452ece191293c1947b0e8c16321cb1a7ba7a3 Mon Sep 17 00:00:00 2001 From: Haiping Chen <101423@smsassist.com> Date: Sat, 6 Apr 2024 19:00:58 -0500 Subject: [PATCH 016/201] Restore Append --- .../Conversations/Services/ConversationStorage.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs index 532f56d4..e523ce48 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs @@ -28,11 +28,11 @@ public class ConversationStorage : IConversationStorage var dialogElements = new List(); // Prevent duplicate record to be inserted - /*var dialogs = db.GetConversationDialogs(conversationId); + var dialogs = db.GetConversationDialogs(conversationId); if (dialogs.Any(x => x.MetaData.MessageId == dialog.MessageId && x.Content == dialog.Content)) { return; - }*/ + } if (dialog.Role == AgentRole.Function) { From 7c061af7f5d5ee89dd43c31ca64f1090826ae397 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Sat, 6 Apr 2024 22:24:55 -0500 Subject: [PATCH 017/201] ConversationStorage.Append --- .../Conversations/Services/ConversationStorage.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs index e523ce48..532f56d4 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs @@ -28,11 +28,11 @@ public class ConversationStorage : IConversationStorage var dialogElements = new List(); // Prevent duplicate record to be inserted - var dialogs = db.GetConversationDialogs(conversationId); + /*var dialogs = db.GetConversationDialogs(conversationId); if (dialogs.Any(x => x.MetaData.MessageId == dialog.MessageId && x.Content == dialog.Content)) { return; - } + }*/ if (dialog.Role == AgentRole.Function) { From 8fd6f6a99e4a911b84740f07ddae141f6e66fb44 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Sun, 7 Apr 2024 13:37:16 -0500 Subject: [PATCH 018/201] add hook --- .../Conversations/Models/StateChangeModel.cs | 4 +-- .../Services/ConversationStateService.cs | 32 ++++++++++++++++--- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateChangeModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateChangeModel.cs index 26c10a71..de84692f 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateChangeModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateChangeModel.cs @@ -12,13 +12,13 @@ public class StateChangeModel public string Name { get; set; } [JsonPropertyName("before_value")] - public string BeforeValue { get; set; } + public string? BeforeValue { get; set; } [JsonPropertyName("before_active_rounds")] public int? BeforeActiveRounds { get; set; } [JsonPropertyName("after_value")] - public string AfterValue { get; set; } + public string? AfterValue { get; set; } [JsonPropertyName("after_active_rounds")] public int? AfterActiveRounds { get; set; } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index 5f632bd6..3f2fe11c 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -1,5 +1,8 @@ +using Amazon.Auth.AccessControlPolicy; +using AspectInjector.Broker; using BotSharp.Abstraction.Conversations.Enums; using BotSharp.Abstraction.Users.Enums; +using System; namespace BotSharp.Core.Conversations.Services; @@ -201,9 +204,9 @@ public class ConversationStateService : IConversationStateService, IDisposable var key = pair.Key; var curValue = pair.Value; - if (!_historyStates.TryGetValue(key, out var historyValue) - || historyValue == null - || historyValue.Values.IsNullOrEmpty() + if (!_historyStates.TryGetValue(key, out var historyValue) + || historyValue == null + || historyValue.Values.IsNullOrEmpty() || !curValue.Versioning) { states.Add(curValue); @@ -232,8 +235,9 @@ public class ConversationStateService : IConversationStateService, IDisposable if (!ContainsState(name)) return false; var routingCtx = _services.GetRequiredService(); - var leafNode = _curStates[name].Values?.LastOrDefault(); - if (leafNode == null) return false; + var value = _curStates[name]; + var leafNode = value?.Values?.LastOrDefault(); + if (value == null || !value.Versioning || leafNode == null) return false; _curStates[name].Values.Add(new StateValue { @@ -246,6 +250,24 @@ public class ConversationStateService : IConversationStateService, IDisposable UpdateTime = DateTime.UtcNow }); + var hooks = _services.GetServices(); + foreach (var hook in hooks) + { + hook.OnStateChanged(new StateChangeModel + { + ConversationId = _conversationId, + MessageId = routingCtx.MessageId, + Name = name, + BeforeValue = leafNode.Data, + BeforeActiveRounds = leafNode.ActiveRounds, + AfterValue = null, + AfterActiveRounds = leafNode.ActiveRounds, + DataType = leafNode.DataType, + Source = leafNode.Source, + Readonly = _curStates[name].Readonly + }).Wait(); + } + return true; } From 76de65e8f55c4cfadd51a0147e22c04b4fe67608 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Sun, 7 Apr 2024 13:38:56 -0500 Subject: [PATCH 019/201] clean code --- .../Conversations/Services/ConversationStateService.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index 3f2fe11c..61bcfbfd 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -1,8 +1,5 @@ -using Amazon.Auth.AccessControlPolicy; -using AspectInjector.Broker; using BotSharp.Abstraction.Conversations.Enums; using BotSharp.Abstraction.Users.Enums; -using System; namespace BotSharp.Core.Conversations.Services; From ca262e7ec397689fbe867059ea84790ded704134 Mon Sep 17 00:00:00 2001 From: Haiping Chen <101423@smsassist.com> Date: Sun, 7 Apr 2024 22:15:51 -0500 Subject: [PATCH 020/201] ConversationBreakpoint.Reason --- .../Conversations/ConversationHookBase.cs | 3 + .../Conversations/IConversationHook.cs | 7 ++ .../Conversations/IConversationService.cs | 3 +- .../Models/ConversationBreakpoint.cs | 3 + .../Functions/Models/FunctionCallFromLlm.cs | 1 - .../Functions/Models/ParameterPropertyDef.cs | 3 +- .../Infrastructures/Enums/StateConst.cs | 1 + .../Repositories/IBotSharpRepository.cs | 4 +- .../Routing/Models/RoutingArgs.cs | 6 ++ .../ConversationService.SendMessage.cs | 24 ++++--- .../ConversationService.UpdateBreakpoint.cs | 10 ++- .../Services/ConversationService.cs | 9 ++- .../Repository/BotSharpDbContext.cs | 4 +- .../FileRepository.Conversation.cs | 15 +++-- .../Routing/Functions/RouteToAgentFn.cs | 3 +- .../Handlers/ConversationEndRoutingHandler.cs | 49 -------------- .../Handlers/ResponseToUserRoutingHandler.cs | 3 +- .../Handlers/RouteToAgentRoutingHandler.cs | 45 +++++++------ .../Handlers/TaskCompletedRoutingHandler.cs | 64 ------------------- .../Models/BreakpointMongoElement.cs | 1 + .../MongoRepository.Conversation.cs | 34 +++++----- 21 files changed, 112 insertions(+), 180 deletions(-) delete mode 100644 src/Infrastructure/BotSharp.Core/Routing/Handlers/ConversationEndRoutingHandler.cs delete mode 100644 src/Infrastructure/BotSharp.Core/Routing/Handlers/TaskCompletedRoutingHandler.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs index 36f065fd..06a97174 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs @@ -44,6 +44,9 @@ public abstract class ConversationHookBase : IConversationHook public virtual Task OnConversationEnding(RoleDialogModel message) => Task.CompletedTask; + public virtual Task OnNewTaskDetected(RoleDialogModel message, string reason) + => Task.CompletedTask; + public virtual Task OnTaskCompleted(RoleDialogModel message) => Task.CompletedTask; diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs index b9f0f3b8..9ed47c61 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs @@ -64,6 +64,13 @@ public interface IConversationHook Task OnResponseGenerated(RoleDialogModel message); + /// + /// LLM detected user requested a new task different from previous topic. + /// + /// + /// + Task OnNewTaskDetected(RoleDialogModel message, string reason); + /// /// LLM detected the current task is completed. /// It's useful for the situation of multiple tasks in the same conversation. diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs index effb8a94..d0a95ef8 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs @@ -42,6 +42,7 @@ public interface IConversationService /// Use this feature when you want to hide some context from LLM. /// /// Whether to reset all states + /// Append user init words /// - Task UpdateBreakpoint(bool resetStates = false); + Task UpdateBreakpoint(bool resetStates = false, string? reason = null); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationBreakpoint.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationBreakpoint.cs index 69a88d13..0d819353 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationBreakpoint.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationBreakpoint.cs @@ -10,4 +10,7 @@ public class ConversationBreakpoint [JsonPropertyName("created_time")] public DateTime CreatedTime { get; set; } = DateTime.UtcNow; + + [JsonPropertyName("reason")] + public string? Reason { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallFromLlm.cs b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallFromLlm.cs index fefade10..661a1437 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallFromLlm.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallFromLlm.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Routing.Models; using System.Text.Json; namespace BotSharp.Abstraction.Functions.Models; diff --git a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/ParameterPropertyDef.cs b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/ParameterPropertyDef.cs index fd927bbc..4f948aa1 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/ParameterPropertyDef.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/ParameterPropertyDef.cs @@ -2,10 +2,11 @@ namespace BotSharp.Abstraction.Functions.Models; public class ParameterPropertyDef : NameDesc { - public ParameterPropertyDef(string name, string description, string type = "string") + public ParameterPropertyDef(string name, string description, string type = "string", bool required = false) : base(name, description) { Type = type; + Required = required; } [JsonPropertyName("required")] diff --git a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/StateConst.cs b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/StateConst.cs index a4039bf7..8b7fc37c 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/StateConst.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/StateConst.cs @@ -5,5 +5,6 @@ public class StateConst public const string EXPECTED_ACTION_AGENT = "expected_next_action_agent"; public const string EXPECTED_GOAL_AGENT = "expected_user_goal_agent"; public const string NEXT_ACTION_AGENT = "next_action_agent"; + public const string NEXT_ACTION_REASON = "next_action_reason"; public const string USER_GOAL_AGENT = "user_goal_agent"; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index 64e92a66..e177b354 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -58,8 +58,8 @@ public interface IBotSharpRepository Conversation GetConversation(string conversationId); PagedItems GetConversations(ConversationFilter filter); void UpdateConversationTitle(string conversationId, string title); - void UpdateConversationBreakpoint(string conversationId, string messageId, DateTime breakpoint); - DateTime GetConversationBreakpoint(string conversationId); + void UpdateConversationBreakpoint(string conversationId, ConversationBreakpoint breakpoint); + ConversationBreakpoint? GetConversationBreakpoint(string conversationId); List GetLastConversations(); List GetIdleConversations(int batchSize, int messageLimit, int bufferHours); bool TruncateConversation(string conversationId, string messageId, bool cleanLog = false); diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs index 82de1a81..31311b57 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs @@ -19,6 +19,12 @@ public class RoutingArgs [JsonPropertyName("conversation_end")] public bool ConversationEnd { get; set; } + [JsonPropertyName("task_completed")] + public bool TaskCompleted { get; set; } + + [JsonPropertyName("is_new_task")] + public bool IsNewTask { get; set; } + /// /// The content of replying to user /// diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs index eaa24fa3..c4dd9302 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -156,27 +156,31 @@ public partial class ConversationService response.FunctionName = response.PostbackFunctionName; } - var hooks = _services.GetServices().ToList(); - if (response.Instruction != null) { var conversation = _services.GetRequiredService(); var updatedConversation = await conversation.UpdateConversationTitle(_conversationId, response.Instruction.NextActionReason); + // Emit conversation task completed hook + if (response.Instruction.TaskCompleted) + { + await HookEmitter.Emit(_services, async hook => + await hook.OnTaskCompleted(response) + ); + } + // Emit conversation ending hook if (response.Instruction.ConversationEnd) { - foreach (var hook in hooks) - { - await hook.OnConversationEnding(response); - } + await HookEmitter.Emit(_services, async hook => + await hook.OnConversationEnding(response) + ); } } - foreach (var hook in hooks) - { - await hook.OnResponseGenerated(response); - } + await HookEmitter.Emit(_services, async hook => + await hook.OnResponseGenerated(response) + ); await onResponseReceived(response); diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.UpdateBreakpoint.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.UpdateBreakpoint.cs index ea10dac0..6a3397fc 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.UpdateBreakpoint.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.UpdateBreakpoint.cs @@ -2,12 +2,18 @@ namespace BotSharp.Core.Conversations.Services; public partial class ConversationService : IConversationService { - public async Task UpdateBreakpoint(bool resetStates = false) + public async Task UpdateBreakpoint(bool resetStates = false, string? reason = null) { var db = _services.GetRequiredService(); var routingCtx = _services.GetRequiredService(); var messageId = routingCtx.MessageId; - db.UpdateConversationBreakpoint(_conversationId, messageId, DateTime.UtcNow); + + db.UpdateConversationBreakpoint(_conversationId, new ConversationBreakpoint + { + MessageId = messageId, + Breakpoint = DateTime.UtcNow, + Reason = reason + }); // Reset states if (resetStates) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs index 188715d4..72b6cde0 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs @@ -115,7 +115,14 @@ public partial class ConversationService : IConversationService { var db = _services.GetRequiredService(); var breakpoint = db.GetConversationBreakpoint(_conversationId); - dialogs = dialogs.Where(x => x.CreatedAt >= breakpoint).ToList(); + if (breakpoint != null) + { + dialogs = dialogs.Where(x => x.CreatedAt >= breakpoint.Breakpoint).ToList(); + if (!string.IsNullOrEmpty(breakpoint.Reason)) + { + dialogs.Insert(0, new RoleDialogModel(AgentRole.User, breakpoint.Reason)); + } + } } return dialogs diff --git a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs index 904048a8..d1361d02 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs @@ -189,10 +189,10 @@ public class BotSharpDbContext : Database, IBotSharpRepository public void UpdateConversationTitle(string conversationId, string title) => new NotImplementedException(); - public void UpdateConversationBreakpoint(string conversationId, string messageId, DateTime breakpoint) + public void UpdateConversationBreakpoint(string conversationId, ConversationBreakpoint breakpoint) => new NotImplementedException(); - public DateTime GetConversationBreakpoint(string conversationId) + public ConversationBreakpoint? GetConversationBreakpoint(string conversationId) => throw new NotImplementedException(); public void UpdateConversationStates(string conversationId, List states) diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs index bb477391..e5c193c1 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs @@ -160,7 +160,7 @@ namespace BotSharp.Core.Repository } } - public void UpdateConversationBreakpoint(string conversationId, string messageId, DateTime breakpoint) + public void UpdateConversationBreakpoint(string conversationId, ConversationBreakpoint breakpoint) { var convDir = FindConversationDirectory(conversationId); if (!string.IsNullOrEmpty(convDir)) @@ -178,9 +178,10 @@ namespace BotSharp.Core.Repository { new ConversationBreakpoint { - MessageId = messageId, - Breakpoint = breakpoint, - CreatedTime = DateTime.UtcNow + MessageId = breakpoint.MessageId, + Breakpoint = breakpoint.Breakpoint, + CreatedTime = DateTime.UtcNow, + Reason = breakpoint.Reason, } }; @@ -197,12 +198,12 @@ namespace BotSharp.Core.Repository } } - public DateTime GetConversationBreakpoint(string conversationId) + public ConversationBreakpoint? GetConversationBreakpoint(string conversationId) { var convDir = FindConversationDirectory(conversationId); if (string.IsNullOrEmpty(convDir)) { - return default; + return null; } var breakpointFile = Path.Combine(convDir, BREAKPOINT_FILE); @@ -214,7 +215,7 @@ namespace BotSharp.Core.Repository var content = File.ReadAllText(breakpointFile); var records = JsonSerializer.Deserialize>(content, _options); - return records?.LastOrDefault()?.Breakpoint ?? default; + return records?.LastOrDefault(); } public ConversationState GetConversationStates(string conversationId) diff --git a/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs b/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs index 6eaaef3a..8221cfb5 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Functions; +using BotSharp.Abstraction.Infrastructures.Enums; using BotSharp.Abstraction.Routing.Models; namespace BotSharp.Core.Routing; @@ -58,7 +59,7 @@ public partial class RouteToAgentFn : IFunctionCallback if (!string.IsNullOrEmpty(args.AgentName) && args.AgentName.Length < 32) { _context.Push(args.AgentName, args.NextActionReason); - states.SetState("next_action_agent", args.AgentName, isNeedVersion: true); + states.SetState(StateConst.NEXT_ACTION_AGENT, args.AgentName, isNeedVersion: true); } if (string.IsNullOrEmpty(args.AgentName)) diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ConversationEndRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ConversationEndRoutingHandler.cs deleted file mode 100644 index bbd4fbb1..00000000 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ConversationEndRoutingHandler.cs +++ /dev/null @@ -1,49 +0,0 @@ -using BotSharp.Abstraction.Routing.Settings; - -namespace BotSharp.Core.Routing.Handlers; - -public class ConversationEndRoutingHandler : RoutingHandlerBase, IRoutingHandler -{ - public string Name => "conversation_end"; - - public string Description => "User completed his task and wants to end the conversation."; - - public List Parameters => new List - { - new ParameterPropertyDef("reason", "why end conversation"), - new ParameterPropertyDef("response", "response content to user") - }; - - public List Planers => new List - { - }; - - public ConversationEndRoutingHandler(IServiceProvider services, ILogger logger, RoutingSettings settings) - : base(services, logger, settings) - { - } - - public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message) - { - var response = new RoleDialogModel(AgentRole.Assistant, inst.Response) - { - CurrentAgentId = message.CurrentAgentId, - MessageId = message.MessageId, - StopCompletion = true, - FunctionName = inst.Function - }; - - _dialogs.Add(response); - - var hooks = _services.GetServices() - .OrderBy(x => x.Priority) - .ToList(); - - foreach (var hook in hooks) - { - await hook.OnConversationEnding(response); - } - - return true; - } -} diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs index ad6eaf8d..2b010183 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs @@ -12,7 +12,8 @@ public class ResponseToUserRoutingHandler : RoutingHandlerBase, IRoutingHandler { new ParameterPropertyDef("reason", "why response to user directly without go to other agents"), new ParameterPropertyDef("response", "response content to user in courteous words. If the user wants to end the conversation, you must set conversation_end to true and response politely."), - new ParameterPropertyDef("conversation_end", "whether to end this conversation, true or false", type: "boolean") + new ParameterPropertyDef("conversation_end", "whether to end this conversation", type: "boolean"), + new ParameterPropertyDef("task_completed ", "whether the user's task request has been completed.", type: "boolean") }; public ResponseToUserRoutingHandler(IServiceProvider services, ILogger logger, RoutingSettings settings) diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs index cfbfe31e..b5c6a59e 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs @@ -11,26 +11,24 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler public List Parameters => new List { - new ParameterPropertyDef("next_action_reason", "the reason why route to this virtual agent") - { - Required = true - }, - new ParameterPropertyDef("next_action_agent", "agent for next action based on user latest response, if user is replying last agent's question, you must route to this agent") - { - Required = true - }, - new ParameterPropertyDef("user_goal_description", "user goal based on user initial task.") - { - Required = true - }, - new ParameterPropertyDef("user_goal_agent", "agent who can acheive user initial task, must align with user_goal_description ") - { - Required = true - }, - new ParameterPropertyDef("args", "useful parameters of next action agent, format: { }") - { - Type = "object" - } + new ParameterPropertyDef("next_action_reason", + "the reason why route to this virtual agent", + required: true), + new ParameterPropertyDef("next_action_agent", + "agent for next action based on user latest response, if user is replying last agent's question, you must route to this agent", + required: true), + new ParameterPropertyDef("user_goal_description", + "user goal based on user initial task.", + required: true), + new ParameterPropertyDef("user_goal_agent", + "agent who can acheive user initial task, must align with user_goal_description.", + required: true), + new ParameterPropertyDef("args", + "useful parameters of next action agent, format: { }", + type: "object"), + new ParameterPropertyDef("is_new_task", + "whether the user is requesting a new task that is different from the previous topic.", + type: "boolean") }; public RouteToAgentRoutingHandler(IServiceProvider services, ILogger logger, RoutingSettings settings) @@ -51,6 +49,13 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler ); } + if (inst.IsNewTask) + { + await HookEmitter.Emit(_services, async hook => + await hook.OnNewTaskDetected(message, inst.NextActionReason) + ); + } + message.FunctionArgs = JsonSerializer.Serialize(inst); var ret = await routing.InvokeFunction(message.FunctionName, message); diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/TaskCompletedRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/TaskCompletedRoutingHandler.cs deleted file mode 100644 index 21b7ea13..00000000 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/TaskCompletedRoutingHandler.cs +++ /dev/null @@ -1,64 +0,0 @@ -using BotSharp.Abstraction.Routing.Settings; -using BotSharp.Core.Routing.Planning; - -namespace BotSharp.Core.Routing.Handlers; - -public class TaskCompletedRoutingHandler : RoutingHandlerBase, IRoutingHandler -{ - public string Name => "task_completed"; - - public string Description => "User task is completed."; - - public List Parameters => new List - { - new ParameterPropertyDef("reason", "why the task is completed") - { - Required = true - }, - new ParameterPropertyDef("response", "polite response when the task is completed") - { - Required = true - }, - new ParameterPropertyDef("conversation_end", "whether to end this conversation, true or false") - { - Required = true, - Type = "boolean" - }, - new ParameterPropertyDef("abandoned_arguments", "the arguments next task can't reuse") - }; - - public List Planers => new List - { - nameof(HFPlanner) - }; - - public TaskCompletedRoutingHandler(IServiceProvider services, ILogger logger, RoutingSettings settings) - : base(services, logger, settings) - { - } - - public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message) - { - var response = new RoleDialogModel(AgentRole.Assistant, inst.Response) - { - CurrentAgentId = message.CurrentAgentId, - MessageId = message.MessageId, - StopCompletion = true, - FunctionName = inst.Function, - Instruction = inst, - }; - - _dialogs.Add(response); - - var hooks = _services.GetServices() - .OrderBy(x => x.Priority) - .ToList(); - - foreach (var hook in hooks) - { - await hook.OnTaskCompleted(response); - } - - return true; - } -} diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/BreakpointMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/BreakpointMongoElement.cs index 96c1fac2..db17d353 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/BreakpointMongoElement.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/BreakpointMongoElement.cs @@ -5,4 +5,5 @@ public class BreakpointMongoElement public string? MessageId { get; set; } public DateTime Breakpoint { get; set; } public DateTime CreatedTime { get; set; } + public string? Reason { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs index 7d9eaea7..4c5af6bc 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs @@ -44,21 +44,12 @@ public partial class MongoRepository } }).ToList(); - var initialBreakpoints = new List() - { - new BreakpointMongoElement - { - Breakpoint = utcNow.AddMilliseconds(-100), - CreatedTime = utcNow - } - }; - var stateDoc = new ConversationStateDocument { Id = Guid.NewGuid().ToString(), ConversationId = convDoc.Id, States = initialStates, - Breakpoints = initialBreakpoints + Breakpoints = new List() }; _dc.Conversations.InsertOne(convDoc); @@ -152,15 +143,16 @@ public partial class MongoRepository _dc.Conversations.UpdateOne(filterConv, updateConv); } - public void UpdateConversationBreakpoint(string conversationId, string messageId, DateTime breakpoint) + public void UpdateConversationBreakpoint(string conversationId, ConversationBreakpoint breakpoint) { if (string.IsNullOrEmpty(conversationId)) return; var newBreakpoint = new BreakpointMongoElement() { - MessageId = messageId, - Breakpoint = breakpoint, - CreatedTime = DateTime.UtcNow + MessageId = breakpoint.MessageId, + Breakpoint = breakpoint.Breakpoint, + CreatedTime = DateTime.UtcNow, + Reason = breakpoint.Reason }; var filterState = Builders.Filter.Eq(x => x.ConversationId, conversationId); var updateState = Builders.Update.Push(x => x.Breakpoints, newBreakpoint); @@ -168,11 +160,11 @@ public partial class MongoRepository _dc.ConversationStates.UpdateOne(filterState, updateState); } - public DateTime GetConversationBreakpoint(string conversationId) + public ConversationBreakpoint? GetConversationBreakpoint(string conversationId) { if (string.IsNullOrEmpty(conversationId)) { - return default; + return null; } var filter = Builders.Filter.Eq(x => x.ConversationId, conversationId); @@ -180,10 +172,16 @@ public partial class MongoRepository if (state == null || state.Breakpoints.IsNullOrEmpty()) { - return default; + return null; } - return state.Breakpoints.LastOrDefault()?.Breakpoint ?? default; + return state.Breakpoints.Select(x => new ConversationBreakpoint + { + Breakpoint = x.Breakpoint, + CreatedTime = x.CreatedTime, + MessageId = x.MessageId, + Reason = x.Reason, + }).LastOrDefault(); } public ConversationState GetConversationStates(string conversationId) From 4723c64e3c06941971dafc45bcfd180f3e407cc9 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 8 Apr 2024 09:58:25 -0500 Subject: [PATCH 021/201] clean code --- .../Services/ConversationStateService.cs | 10 +++---- .../FileRepository.Conversation.cs | 24 ++-------------- .../Collections/ConversationStateDocument.cs | 4 +-- .../MongoRepository.Conversation.cs | 28 ++++++------------- 4 files changed, 19 insertions(+), 47 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index 61bcfbfd..36b2ddeb 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -58,9 +58,9 @@ public class ConversationStateService : IConversationStateService, IDisposable if (ContainsState(name) && _curStates.TryGetValue(name, out var pair)) { - var lastNode = pair?.Values?.LastOrDefault(); - preActiveRounds = lastNode?.ActiveRounds; - preValue = lastNode?.Data ?? string.Empty; + var leafNode = pair?.Values?.LastOrDefault(); + preActiveRounds = leafNode?.ActiveRounds; + preValue = leafNode?.Data ?? string.Empty; } _logger.LogInformation($"[STATE] {name} = {value}"); @@ -139,7 +139,7 @@ public class ConversationStateService : IConversationStateService, IDisposable { var key = state.Key; var value = state.Value; - var leafNode = value.Values.LastOrDefault(); + var leafNode = value?.Values?.LastOrDefault(); if (leafNode == null) continue; _curStates[key] = new StateKeyValue @@ -261,7 +261,7 @@ public class ConversationStateService : IConversationStateService, IDisposable AfterActiveRounds = leafNode.ActiveRounds, DataType = leafNode.DataType, Source = leafNode.Source, - Readonly = _curStates[name].Readonly + Readonly = value.Readonly }).Wait(); } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs index e5c193c1..f55ac4e1 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs @@ -1,5 +1,4 @@ using BotSharp.Abstraction.Loggers.Models; -using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Repositories.Models; using System.Globalization; using System.IO; @@ -35,30 +34,13 @@ namespace BotSharp.Core.Repository var stateFile = Path.Combine(dir, STATE_FILE); if (!File.Exists(stateFile)) { - var states = conversation.States ?? new Dictionary(); - var initialStates = states.Select(x => new StateKeyValue - { - Key = x.Key, - Values = new List - { - new StateValue { Data = x.Value, UpdateTime = DateTime.UtcNow } - } - }).ToList(); - File.WriteAllText(stateFile, JsonSerializer.Serialize(initialStates, _options)); + File.WriteAllText(stateFile, JsonSerializer.Serialize(new List(), _options)); } var breakpointFile = Path.Combine(dir, BREAKPOINT_FILE); if (!File.Exists(breakpointFile)) { - var initialBreakpoints = new List - { - new ConversationBreakpoint() - { - Breakpoint = utcNow.AddMilliseconds(-100), - CreatedTime = DateTime.UtcNow - } - }; - File.WriteAllText(breakpointFile, JsonSerializer.Serialize(initialBreakpoints, _options)); + File.WriteAllText(breakpointFile, JsonSerializer.Serialize(new List(), _options)); } } @@ -180,8 +162,8 @@ namespace BotSharp.Core.Repository { MessageId = breakpoint.MessageId, Breakpoint = breakpoint.Breakpoint, - CreatedTime = DateTime.UtcNow, Reason = breakpoint.Reason, + CreatedTime = DateTime.UtcNow, } }; diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationStateDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationStateDocument.cs index 42331616..1f8f0e90 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationStateDocument.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationStateDocument.cs @@ -5,6 +5,6 @@ namespace BotSharp.Plugin.MongoStorage.Collections; public class ConversationStateDocument : MongoBase { public string ConversationId { get; set; } - public List States { get; set; } - public List Breakpoints { get; set; } + public List States { get; set; } = new List(); + public List Breakpoints { get; set; } = new List(); } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs index 4c5af6bc..ac5a47d5 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs @@ -3,7 +3,6 @@ using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Repositories.Models; using BotSharp.Plugin.MongoStorage.Collections; using BotSharp.Plugin.MongoStorage.Models; -using System.Text.RegularExpressions; namespace BotSharp.Plugin.MongoStorage.Repository; @@ -34,21 +33,11 @@ public partial class MongoRepository Dialogs = new List() }; - var states = conversation.States ?? new Dictionary(); - var initialStates = states.Select(x => new StateMongoElement - { - Key = x.Key, - Values = new List - { - new StateValueMongoElement { Data = x.Value, UpdateTime = DateTime.UtcNow } - } - }).ToList(); - var stateDoc = new ConversationStateDocument { Id = Guid.NewGuid().ToString(), ConversationId = convDoc.Id, - States = initialStates, + States = new List(), Breakpoints = new List() }; @@ -169,19 +158,20 @@ public partial class MongoRepository var filter = Builders.Filter.Eq(x => x.ConversationId, conversationId); var state = _dc.ConversationStates.Find(filter).FirstOrDefault(); + var leafNode = state?.Breakpoints?.LastOrDefault(); - if (state == null || state.Breakpoints.IsNullOrEmpty()) + if (leafNode == null) { return null; } - return state.Breakpoints.Select(x => new ConversationBreakpoint + return new ConversationBreakpoint { - Breakpoint = x.Breakpoint, - CreatedTime = x.CreatedTime, - MessageId = x.MessageId, - Reason = x.Reason, - }).LastOrDefault(); + Breakpoint = leafNode.Breakpoint, + MessageId = leafNode.MessageId, + Reason = leafNode.Reason, + CreatedTime = leafNode.CreatedTime, + }; } public ConversationState GetConversationStates(string conversationId) From 8e9afae5f508420aa64ec4d26a2e7f066831ad81 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Mon, 8 Apr 2024 10:09:08 -0500 Subject: [PATCH 022/201] Add Highlight to ElementLocatingArgs --- .../Browsing/Enums/BroswerActionEnum.cs | 7 +++++ .../Browsing/Models/BrowserActionResult.cs | 5 ++-- .../Browsing/Models/ElementActionArgs.cs | 17 +++++++++--- .../Browsing/Models/ElementLocatingArgs.cs | 5 ++++ .../PlaywrightWebDriver.ChangeCheckbox.cs | 16 +++++------ .../PlaywrightWebDriver.ChangeListValue.cs | 2 +- .../PlaywrightWebDriver.CheckRadioButton.cs | 8 +++--- .../PlaywrightWebDriver.ClickButton.cs | 4 +-- .../PlaywrightWebDriver.ClickElement.cs | 8 +++--- .../PlaywrightWebDriver.DoAction.cs | 8 +++++- .../PlaywrightWebDriver.GoToPage.cs | 4 +-- .../PlaywrightWebDriver.HttpRequest.cs | 2 +- .../PlaywrightWebDriver.InputUserPassword.cs | 6 ++--- .../PlaywrightWebDriver.InputUserText.cs | 2 +- .../PlaywrightWebDriver.LaunchBrowser.cs | 2 +- .../PlaywrightWebDriver.LocateElement.cs | 27 ++++++++++++++++--- .../Functions/ChangeCheckboxFn.cs | 2 +- .../Functions/ChangeListValueFn.cs | 2 +- .../Functions/CheckRadioButtonFn.cs | 2 +- .../Functions/ClickButtonFn.cs | 2 +- .../Functions/ClickElementFn.cs | 2 +- .../Functions/GoToPageFn.cs | 2 +- .../Functions/HttpRequestFn.cs | 2 +- .../Functions/InputUserTextFn.cs | 2 +- .../Functions/OpenBrowserFn.cs | 2 +- 25 files changed, 95 insertions(+), 46 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Browsing/Enums/BroswerActionEnum.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Enums/BroswerActionEnum.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Enums/BroswerActionEnum.cs new file mode 100644 index 00000000..6dd0fb63 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Enums/BroswerActionEnum.cs @@ -0,0 +1,7 @@ +namespace BotSharp.Abstraction.Browsing.Enums; + +public enum BroswerActionEnum +{ + Click = 1, + InputText = 2, +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionResult.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionResult.cs index 9e45cf97..a634cccd 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionResult.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionResult.cs @@ -3,8 +3,9 @@ namespace BotSharp.Abstraction.Browsing.Models; public class BrowserActionResult { public bool IsSuccess { get; set; } - public string ErrorMessage { get; set; } - public string StackTrace { get; set; } + public string? Message { get; set; } + public string? StackTrace { get; set; } public string Selector { get; set; } public string Body { get; set; } + public bool IsHighlighted { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs index 32d13c5c..dcda7436 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs @@ -1,12 +1,23 @@ +using BotSharp.Abstraction.Browsing.Enums; + namespace BotSharp.Abstraction.Browsing.Models; public class ElementActionArgs { - private string _action; - public string Action => _action; + private BroswerActionEnum _action; + public BroswerActionEnum Action => _action; - public ElementActionArgs(string action) + private string _content; + public string Content => _content; + + public ElementActionArgs(BroswerActionEnum action) { _action = action; } + + public ElementActionArgs(BroswerActionEnum action, string content) + { + _action = action; + _content = content; + } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs index d043ca54..6d6411d8 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs @@ -21,4 +21,9 @@ public class ElementLocatingArgs public string? Selector { get; set; } public bool FailIfMultiple { get; set; } + + /// + /// Draw outline around the element + /// + public bool Highlight { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeCheckbox.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeCheckbox.cs index 7611f1d1..2bac92dd 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeCheckbox.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeCheckbox.cs @@ -25,13 +25,13 @@ public partial class PlaywrightWebDriver var errorMessage = $"Can't locate element by keyword {actionParams.Context.ElementText}"; if (count == 0) { - result.ErrorMessage = errorMessage; + result.Message = errorMessage; return result; } else if (count > 1) { - result.ErrorMessage = $"Located multiple elements by {actionParams.Context.ElementText}"; - _logger.LogError(result.ErrorMessage); + result.Message = $"Located multiple elements by {actionParams.Context.ElementText}"; + _logger.LogError(result.Message); var allElements = await elements.AllAsync(); foreach (var element in allElements) { @@ -44,7 +44,7 @@ public partial class PlaywrightWebDriver count = await parentElement.CountAsync(); if (count == 0) { - result.ErrorMessage = errorMessage; + result.Message = errorMessage; return result; } @@ -61,13 +61,13 @@ public partial class PlaywrightWebDriver if (count == 0) { - result.ErrorMessage = errorMessage; + result.Message = errorMessage; return result; } else if (count > 1) { - result.ErrorMessage = $"Located multiple elements by {actionParams.Context.ElementText}"; - _logger.LogError(result.ErrorMessage); + result.Message = $"Located multiple elements by {actionParams.Context.ElementText}"; + _logger.LogError(result.Message); return result; } @@ -87,7 +87,7 @@ public partial class PlaywrightWebDriver } catch (Exception ex) { - result.ErrorMessage = ex.Message; + result.Message = ex.Message; result.StackTrace = ex.StackTrace; _logger.LogError(ex.Message); } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeListValue.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeListValue.cs index d8b81393..119064a9 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeListValue.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeListValue.cs @@ -106,7 +106,7 @@ public partial class PlaywrightWebDriver } catch (Exception ex) { - result.ErrorMessage = ex.Message; + result.Message = ex.Message; result.StackTrace = ex.StackTrace; _logger.LogError(ex.Message); } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.CheckRadioButton.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.CheckRadioButton.cs index 6a5f8663..9d28f8c4 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.CheckRadioButton.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.CheckRadioButton.cs @@ -22,7 +22,7 @@ public partial class PlaywrightWebDriver var errorMessage = $"Can't locate element by keyword {actionParams.Context.ElementText}"; if (count == 0) { - result.ErrorMessage = errorMessage; + result.Message = errorMessage; return result; } @@ -30,7 +30,7 @@ public partial class PlaywrightWebDriver count = await parentElement.CountAsync(); if (count == 0) { - result.ErrorMessage = errorMessage; + result.Message = errorMessage; return result; } @@ -40,7 +40,7 @@ public partial class PlaywrightWebDriver if (count == 0) { - result.ErrorMessage = errorMessage; + result.Message = errorMessage; return result; } @@ -53,7 +53,7 @@ public partial class PlaywrightWebDriver } catch (Exception ex) { - result.ErrorMessage = ex.Message; + result.Message = ex.Message; result.StackTrace = ex.StackTrace; _logger.LogError(ex.Message); } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ClickButton.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ClickButton.cs index 1f2e3ec3..f1678db5 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ClickButton.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ClickButton.cs @@ -46,7 +46,7 @@ public partial class PlaywrightWebDriver if (elements == null) { var errorMessage = $"Can't locate element by keyword {actionParams.Context.ElementName}"; - result.ErrorMessage = errorMessage; + result.Message = errorMessage; return result; } } @@ -60,7 +60,7 @@ public partial class PlaywrightWebDriver } catch (Exception ex) { - result.ErrorMessage = ex.Message; + result.Message = ex.Message; result.StackTrace = ex.StackTrace; _logger.LogError(ex.Message); } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ClickElement.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ClickElement.cs index 0ee8b24e..3d12ed2c 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ClickElement.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ClickElement.cs @@ -42,8 +42,8 @@ public partial class PlaywrightWebDriver if (count == 0) { - result.ErrorMessage = $"Can't locate element by keyword {actionParams.Context.ElementText}"; - _logger.LogError(result.ErrorMessage); + result.Message = $"Can't locate element by keyword {actionParams.Context.ElementText}"; + _logger.LogError(result.Message); } else if (count == 1) { @@ -57,8 +57,8 @@ public partial class PlaywrightWebDriver } else if (count > 1) { - result.ErrorMessage = $"Multiple elements are found by keyword {actionParams.Context.ElementText}"; - _logger.LogWarning(result.ErrorMessage); + result.Message = $"Multiple elements are found by keyword {actionParams.Context.ElementText}"; + _logger.LogWarning(result.Message); var all = await locator.AllAsync(); foreach (var element in all) { diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs index d3311eee..5e0bcf8b 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs @@ -1,3 +1,5 @@ +using BotSharp.Abstraction.Browsing.Enums; + namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver @@ -7,9 +9,13 @@ public partial class PlaywrightWebDriver var page = _instance.GetPage(message.ConversationId); ILocator locator = page.Locator(result.Selector); - if (action.Action == "click") + if (action.Action == BroswerActionEnum.Click) { await locator.ClickAsync(); } + else if (action.Action == BroswerActionEnum.InputText) + { + await locator.FillAsync(action.Content); + } } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs index ffcec537..e3c9a52d 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs @@ -19,12 +19,12 @@ public partial class PlaywrightWebDriver } else { - result.ErrorMessage = response.StatusText; + result.Message = response.StatusText; } } catch (Exception ex) { - result.ErrorMessage = ex.Message; + result.Message = ex.Message; result.StackTrace = ex.StackTrace; _logger.LogError(ex.Message); } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs index 8d54a430..6f03ca8d 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs @@ -34,7 +34,7 @@ public partial class PlaywrightWebDriver } catch (Exception ex) { - result.ErrorMessage = ex.Message; + result.Message = ex.Message; result.StackTrace = ex.StackTrace; _logger.LogError(ex.Message); } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserPassword.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserPassword.cs index c4e8c2fc..3d0f2419 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserPassword.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserPassword.cs @@ -16,8 +16,8 @@ public partial class PlaywrightWebDriver if (password == null) { - result.ErrorMessage = $"Can't locate the password element by '{actionParams.Context.ElementName}'"; - _logger.LogError(result.ErrorMessage); + result.Message = $"Can't locate the password element by '{actionParams.Context.ElementName}'"; + _logger.LogError(result.Message); return result; } @@ -29,7 +29,7 @@ public partial class PlaywrightWebDriver } catch (Exception ex) { - result.ErrorMessage = ex.Message; + result.Message = ex.Message; result.StackTrace = ex.StackTrace; _logger.LogError(ex.Message); } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserText.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserText.cs index 49850499..01eb107f 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserText.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserText.cs @@ -61,7 +61,7 @@ public partial class PlaywrightWebDriver } catch (Exception ex) { - result.ErrorMessage = ex.Message; + result.Message = ex.Message; result.StackTrace = ex.StackTrace; _logger.LogError(ex.Message); } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs index 533bce08..323dd89c 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs @@ -27,7 +27,7 @@ public partial class PlaywrightWebDriver } catch(Exception ex) { - result.ErrorMessage = ex.Message; + result.Message = ex.Message; result.StackTrace = ex.StackTrace; _logger.LogError(ex.Message); } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs index 9ec68d20..3edba4bd 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs @@ -2,6 +2,12 @@ namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver { + /// + /// Using attributes or text to locate element and return the selector + /// + /// + /// + /// public async Task LocateElement(MessageInfo message, ElementLocatingArgs location) { var result = new BrowserActionResult(); @@ -53,8 +59,8 @@ public partial class PlaywrightWebDriver if (count == 0) { - result.ErrorMessage = $"Can't locate element by keyword {location.Text}"; - _logger.LogError(result.ErrorMessage); + result.Message = $"Can't locate element by keyword {location.Text}"; + _logger.LogError(result.Message); } else if (count == 1) { @@ -67,8 +73,8 @@ public partial class PlaywrightWebDriver { if (location.FailIfMultiple) { - result.ErrorMessage = $"Multiple elements are found by {locator}"; - _logger.LogError(result.ErrorMessage); + result.Message = $"Multiple elements are found by {locator}"; + _logger.LogError(result.Message); foreach (var element in await locator.AllAsync()) { @@ -83,6 +89,19 @@ public partial class PlaywrightWebDriver } } + // Hightlight the element + if (result.IsSuccess && location.Highlight) + { + var handle = await page.QuerySelectorAsync(result.Selector); + + await page.EvaluateAsync($@" + (element) => {{ + element.style.outline = '2px solid red'; + }}", handle); + + result.IsHighlighted = true; + } + return result; } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ChangeCheckboxFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ChangeCheckboxFn.cs index 0776b107..57d0851a 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ChangeCheckboxFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ChangeCheckboxFn.cs @@ -26,7 +26,7 @@ public class ChangeCheckboxFn : IFunctionCallback var content = $"{(args.UpdateValue == "check" ? "Check" : "Uncheck")} checkbox of '{args.ElementText}'"; message.Content = result.IsSuccess ? $"{content} successfully" : - $"{content} failed. {result.ErrorMessage}"; + $"{content} failed. {result.Message}"; var webDriverService = _services.GetRequiredService(); var path = webDriverService.GetScreenshotFilePath(message.MessageId); diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ChangeListValueFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ChangeListValueFn.cs index f1c0c134..8d0ec144 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ChangeListValueFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ChangeListValueFn.cs @@ -26,7 +26,7 @@ public class ChangeListValueFn : IFunctionCallback var content = $"Change value to '{args.UpdateValue}' for {args.ElementName}"; message.Content = result.IsSuccess ? $"{content} successfully" : - $"{content} failed. {result.ErrorMessage}"; + $"{content} failed. {result.Message}"; var webDriverService = _services.GetRequiredService(); var path = webDriverService.GetScreenshotFilePath(message.MessageId); diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/CheckRadioButtonFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/CheckRadioButtonFn.cs index 8c3027d9..c12430df 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/CheckRadioButtonFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/CheckRadioButtonFn.cs @@ -26,7 +26,7 @@ public class CheckRadioButtonFn : IFunctionCallback var content = $"Check value of '{args.UpdateValue}' for radio button '{args.ElementName}'"; message.Content = result.IsSuccess ? $"{content} successfully" : - $"{content} failed. {result.ErrorMessage}"; + $"{content} failed. {result.Message}"; var webDriverService = _services.GetRequiredService(); var path = webDriverService.GetScreenshotFilePath(message.MessageId); diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ClickButtonFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ClickButtonFn.cs index 67215a1b..060e5055 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ClickButtonFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ClickButtonFn.cs @@ -26,7 +26,7 @@ public class ClickButtonFn : IFunctionCallback var content = $"Click button of '{args.ElementName}'"; message.Content = result.IsSuccess ? $"{content} successfully" : - $"{content} failed. {result.ErrorMessage}"; + $"{content} failed. {result.Message}"; var webDriverService = _services.GetRequiredService(); var path = webDriverService.GetScreenshotFilePath(message.MessageId); diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ClickElementFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ClickElementFn.cs index d87a2c0b..612416a3 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ClickElementFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ClickElementFn.cs @@ -26,7 +26,7 @@ public class ClickElementFn : IFunctionCallback var content = $"Click element {args.MatchRule} text '{args.ElementText}'"; message.Content = result.IsSuccess ? $"{content} successfully" : - $"{content} failed. {result.ErrorMessage}"; + $"{content} failed. {result.Message}"; var webDriverService = _services.GetRequiredService(); var path = webDriverService.GetScreenshotFilePath(message.MessageId); diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/GoToPageFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/GoToPageFn.cs index 8a3c850b..7ffe4026 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/GoToPageFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/GoToPageFn.cs @@ -27,7 +27,7 @@ public class GoToPageFn : IFunctionCallback url = url.Replace("https://https://", "https://"); var result = await _browser.GoToPage(convService.ConversationId, url); - message.Content = result.IsSuccess ? $"Page {url} is open." : $"Page {url} open failed. {result.ErrorMessage}"; + message.Content = result.IsSuccess ? $"Page {url} is open." : $"Page {url} open failed. {result.Message}"; var path = webDriverService.GetScreenshotFilePath(message.MessageId); diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/HttpRequestFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/HttpRequestFn.cs index 1e42df1f..76b9a926 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/HttpRequestFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/HttpRequestFn.cs @@ -25,7 +25,7 @@ public class HttpRequestFn : IFunctionCallback message.Content = result.IsSuccess ? result.Body : - $"Http request failed. {result.ErrorMessage}"; + $"Http request failed. {result.Message}"; return true; } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserTextFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserTextFn.cs index d0ac76b8..028d369d 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserTextFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserTextFn.cs @@ -31,7 +31,7 @@ public class InputUserTextFn : IFunctionCallback message.Content = result.IsSuccess ? content + " successfully" : - content + $" failed. {result.ErrorMessage}"; + content + $" failed. {result.Message}"; var webDriverService = _services.GetRequiredService(); var path = webDriverService.GetScreenshotFilePath(message.MessageId); diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/OpenBrowserFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/OpenBrowserFn.cs index 1a9c5e13..8d0cff8e 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/OpenBrowserFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/OpenBrowserFn.cs @@ -31,7 +31,7 @@ public class OpenBrowserFn : IFunctionCallback } else { - message.Content = $"Launch browser failed. {result.ErrorMessage}"; + message.Content = $"Launch browser failed. {result.Message}"; } var path = webDriverService.GetScreenshotFilePath(message.MessageId); From 9fd3ea98a4b592ab8372f3ee9d73b95b29ef0541 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Mon, 8 Apr 2024 13:50:06 -0500 Subject: [PATCH 023/201] IsPopup --- .../Models/RichContent/Template/GenericTemplateMessage.cs | 3 +++ 1 file changed, 3 insertions(+) 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 c86bca59..6578b363 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/GenericTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/GenericTemplateMessage.cs @@ -19,6 +19,9 @@ public class GenericTemplateMessage : IRichMessage, ITemplateMessage [JsonPropertyName("is_horizontal")] public bool IsHorizontal { get; set; } + [JsonPropertyName("is_popup")] + public bool IsPopup { get; set; } + [JsonPropertyName("element_type")] public string ElementType => typeof(T).Name; } From d59c99c712c60b8f2d23e5635aea2cad7d0cbcf1 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 8 Apr 2024 18:06:50 -0500 Subject: [PATCH 024/201] add file editor --- .../BotSharp.Abstraction/Messaging/Enums/EditorTypeEnum.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/EditorTypeEnum.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/EditorTypeEnum.cs index f190e5e5..9516bb13 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/EditorTypeEnum.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/EditorTypeEnum.cs @@ -12,6 +12,7 @@ public static class EditorTypeEnum public const string DateTimePicker = "datetime-picker"; public const string DateTimeRangePicker = "datetime-range-picker"; public const string Email = "email"; + public const string File = "file"; /// /// Regex, set the expression in editor_attributes From e334c74704cd0c0314947d7c16d56f31878342ab Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Mon, 8 Apr 2024 22:38:56 -0500 Subject: [PATCH 025/201] Optimize WebDriver with context id. --- .../Browsing/IWebBrowser.cs | 13 +++++----- .../Browsing/Models/BrowserActionParams.cs | 6 ++--- .../Browsing/Models/MessageInfo.cs | 2 +- .../PlaywrightDriver/PlaywrightInstance.cs | 25 +++++++++++++------ .../PlaywrightWebDriver.ActionOnElement.cs | 2 +- .../PlaywrightWebDriver.ChangeCheckbox.cs | 8 +++--- .../PlaywrightWebDriver.ChangeListValue.cs | 14 +++++------ .../PlaywrightWebDriver.CheckRadioButton.cs | 4 +-- .../PlaywrightWebDriver.ClickButton.cs | 18 ++++++------- .../PlaywrightWebDriver.ClickElement.cs | 6 ++--- .../PlaywrightWebDriver.CloseBrowser.cs | 4 +-- .../PlaywrightWebDriver.CloseCurrentPage.cs | 9 +++++++ .../PlaywrightWebDriver.DoAction.cs | 4 +-- .../PlaywrightWebDriver.EvaluateScript.cs | 6 ++--- .../PlaywrightWebDriver.ExtractData.cs | 4 +-- .../PlaywrightWebDriver.GetAttributeValue.cs | 2 +- .../PlaywrightWebDriver.GoToPage.cs | 11 ++++---- .../PlaywrightWebDriver.HttpRequest.cs | 5 ++-- .../PlaywrightWebDriver.InputUserPassword.cs | 4 +-- .../PlaywrightWebDriver.InputUserText.cs | 10 ++++---- .../PlaywrightWebDriver.LaunchBrowser.cs | 16 +++++++++--- .../PlaywrightWebDriver.LocateElement.cs | 2 +- .../PlaywrightWebDriver.Screenshot.cs | 6 ++--- .../PlaywrightWebDriver.ScrollPage.cs | 4 +-- .../PlaywrightDriver/PlaywrightWebDriver.cs | 8 +++--- .../Functions/HttpRequestFn.cs | 2 +- .../BotSharp.Plugin.WebDriver/Using.cs | 1 + 27 files changed, 111 insertions(+), 85 deletions(-) create mode 100644 src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.CloseCurrentPage.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs index f732b801..850833cd 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs @@ -4,8 +4,8 @@ namespace BotSharp.Abstraction.Browsing; public interface IWebBrowser { - Task LaunchBrowser(string conversationId, string? url); - Task ScreenshotAsync(string conversationId, string path); + Task LaunchBrowser(string contextId, string? url, bool openIfNotExist = true); + Task ScreenshotAsync(string contextId, string path); Task ScrollPageAsync(BrowserActionParams actionParams); Task ActionOnElement(MessageInfo message, ElementLocatingArgs location, ElementActionArgs action); @@ -19,10 +19,11 @@ public interface IWebBrowser Task ChangeListValue(BrowserActionParams actionParams); Task CheckRadioButton(BrowserActionParams actionParams); Task ChangeCheckbox(BrowserActionParams actionParams); - Task GoToPage(string conversationId, string url); + Task GoToPage(string contextId, string url, bool openNewTab = false); Task ExtractData(BrowserActionParams actionParams); - Task EvaluateScript(string conversationId, string script); - Task CloseBrowser(string conversationId); - Task SendHttpRequest(HttpRequestParams actionParams); + Task EvaluateScript(string contextId, string script); + Task CloseBrowser(string contextId); + Task CloseCurrentPage(string contextId); + Task SendHttpRequest(string contextId, HttpRequestParams actionParams); Task GetAttributeValue(MessageInfo message, ElementLocatingArgs location, BrowserActionResult result); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionParams.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionParams.cs index 37c820a2..04ba82b2 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionParams.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionParams.cs @@ -4,14 +4,14 @@ public class BrowserActionParams { public Agent Agent { get; set; } public BrowsingContextIn Context { get; set; } - public string ConversationId { get; set; } + public string ContextId { get; set; } public string MessageId { get; set; } - public BrowserActionParams(Agent agent, BrowsingContextIn context, string conversationId, string messageId) + public BrowserActionParams(Agent agent, BrowsingContextIn context, string contextId, string messageId) { Agent = agent; Context = context; - ConversationId = conversationId; + ContextId = contextId; MessageId = messageId; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/MessageInfo.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/MessageInfo.cs index dc530e6d..336d255c 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/MessageInfo.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/MessageInfo.cs @@ -3,6 +3,6 @@ namespace BotSharp.Abstraction.Browsing.Models; public class MessageInfo { public string AgentId { get; set; } - public string ConversationId { get; set; } + public string ContextId { get; set; } public string MessageId { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs index fdbc29cb..9786ab93 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs @@ -6,6 +6,7 @@ public class PlaywrightInstance : IDisposable { IPlaywright _playwright; Dictionary _contexts = new Dictionary(); + public Dictionary Contexts => _contexts; public IPage GetPage(string id) { @@ -13,24 +14,22 @@ public class PlaywrightInstance : IDisposable return _contexts[id].Pages.LastOrDefault(); } - public async Task InitInstance(string id) + public async Task InitInstance(string id) { if (_playwright == null) { _playwright = await Playwright.CreateAsync(); } - await InitContext(id); + return await InitContext(id); } - public async Task InitContext(string id) + public async Task InitContext(string id) { if (_contexts.ContainsKey(id)) - return; -#if DEBUG - string tempFolderPath = $"{Path.GetTempPath()}\\playwright"; -#else + return _contexts[id]; + string tempFolderPath = $"{Path.GetTempPath()}\\playwright\\{id}"; -#endif + _contexts[id] = await _playwright.Chromium.LaunchPersistentContextAsync(tempFolderPath, new BrowserTypeLaunchPersistentContextOptions { #if DEBUG @@ -65,6 +64,8 @@ public class PlaywrightInstance : IDisposable Serilog.Log.Warning($"Playwright browser context is closed"); _contexts.Remove(id); }; + + return _contexts[id]; } public async Task NewPage(string id) @@ -92,6 +93,14 @@ public class PlaywrightInstance : IDisposable } } + public async Task CloseCurrentPage(string id) + { + if (_contexts.ContainsKey(id)) + { + await GetPage(id).CloseAsync(); + } + } + public void Dispose() { _contexts.Clear(); diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ActionOnElement.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ActionOnElement.cs index f8d4d1d9..67a46f03 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ActionOnElement.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ActionOnElement.cs @@ -4,7 +4,7 @@ public partial class PlaywrightWebDriver { public async Task ActionOnElement(MessageInfo message, ElementLocatingArgs location, ElementActionArgs action) { - await _instance.Wait(message.ConversationId); + await _instance.Wait(message.ContextId); var result = await LocateElement(message, location); if (result.IsSuccess) { diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeCheckbox.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeCheckbox.cs index 2bac92dd..d15597bc 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeCheckbox.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeCheckbox.cs @@ -1,5 +1,3 @@ -using System.Text.RegularExpressions; - namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver @@ -8,7 +6,7 @@ public partial class PlaywrightWebDriver { var result = new BrowserActionResult(); - await _instance.Wait(actionParams.ConversationId); + await _instance.Wait(actionParams.ContextId); // Retrieve the page raw html and infer the element path var regexExpression = actionParams.Context.MatchRule.ToLower() switch @@ -19,7 +17,7 @@ public partial class PlaywrightWebDriver _ => $"^{actionParams.Context.ElementText}$" }; var regex = new Regex(regexExpression, RegexOptions.IgnoreCase); - var elements = _instance.GetPage(actionParams.ConversationId).GetByText(regex); + var elements = _instance.GetPage(actionParams.ContextId).GetByText(regex); var count = await elements.CountAsync(); var errorMessage = $"Can't locate element by keyword {actionParams.Context.ElementText}"; @@ -55,7 +53,7 @@ public partial class PlaywrightWebDriver } else { - elements = _instance.GetPage(actionParams.ConversationId).Locator($"#{id}"); + elements = _instance.GetPage(actionParams.ContextId).Locator($"#{id}"); } count = await elements.CountAsync(); diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeListValue.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeListValue.cs index 119064a9..f3d58f97 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeListValue.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeListValue.cs @@ -5,10 +5,10 @@ public partial class PlaywrightWebDriver public async Task ChangeListValue(BrowserActionParams actionParams) { var result = new BrowserActionResult(); - await _instance.Wait(actionParams.ConversationId); + await _instance.Wait(actionParams.ContextId); // Retrieve the page raw html and infer the element path - var body = await _instance.GetPage(actionParams.ConversationId).QuerySelectorAsync("body"); + var body = await _instance.GetPage(actionParams.ContextId).QuerySelectorAsync("body"); var str = new List(); var inputs = await body.QuerySelectorAllAsync("select"); @@ -61,7 +61,7 @@ public partial class PlaywrightWebDriver string.Join("", str), actionParams.Context.ElementName, actionParams.MessageId); - ILocator element = Locator(actionParams.ConversationId, htmlElementContextOut); + ILocator? element = Locator(actionParams.ContextId, htmlElementContextOut); try { @@ -70,11 +70,11 @@ public partial class PlaywrightWebDriver if (!isVisible) { // Select the element you want to make visible (replace with your own selector) - var control = await _instance.GetPage(actionParams.ConversationId) + var control = await _instance.GetPage(actionParams.ContextId) .QuerySelectorAsync($"#{htmlElementContextOut.ElementId}"); // Show the element by modifying its CSS styles - await _instance.GetPage(actionParams.ConversationId) + await _instance.GetPage(actionParams.ContextId) .EvaluateAsync(@"(element) => { element.style.display = 'block'; element.style.visibility = 'visible'; @@ -92,11 +92,11 @@ public partial class PlaywrightWebDriver if (!isVisible) { // Select the element you want to make visible (replace with your own selector) - var control = await _instance.GetPage(actionParams.ConversationId) + var control = await _instance.GetPage(actionParams.ContextId) .QuerySelectorAsync($"#{htmlElementContextOut.ElementId}"); // Show the element by modifying its CSS styles - await _instance.GetPage(actionParams.ConversationId).EvaluateAsync(@"(element) => { + await _instance.GetPage(actionParams.ContextId).EvaluateAsync(@"(element) => { element.style.display = 'none'; element.style.visibility = 'hidden'; }", control); diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.CheckRadioButton.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.CheckRadioButton.cs index 9d28f8c4..9df8d3bc 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.CheckRadioButton.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.CheckRadioButton.cs @@ -5,7 +5,7 @@ public partial class PlaywrightWebDriver public async Task CheckRadioButton(BrowserActionParams actionParams) { var result = new BrowserActionResult(); - await _instance.Wait(actionParams.ConversationId); + await _instance.Wait(actionParams.ContextId); // Retrieve the page raw html and infer the element path var regexExpression = actionParams.Context.MatchRule.ToLower() switch @@ -16,7 +16,7 @@ public partial class PlaywrightWebDriver _ => $"^{actionParams.Context.ElementText}$" }; var regex = new Regex(regexExpression, RegexOptions.IgnoreCase); - var elements = _instance.GetPage(actionParams.ConversationId).GetByText(regex); + var elements = _instance.GetPage(actionParams.ContextId).GetByText(regex); var count = await elements.CountAsync(); var errorMessage = $"Can't locate element by keyword {actionParams.Context.ElementText}"; diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ClickButton.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ClickButton.cs index f1678db5..db0aad60 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ClickButton.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ClickButton.cs @@ -5,10 +5,10 @@ public partial class PlaywrightWebDriver public async Task ClickButton(BrowserActionParams actionParams) { var result = new BrowserActionResult(); - await _instance.Wait(actionParams.ConversationId); + await _instance.Wait(actionParams.ContextId); // Find by text exactly match - var elements = _instance.GetPage(actionParams.ConversationId) + var elements = _instance.GetPage(actionParams.ContextId) .GetByRole(AriaRole.Button, new PageGetByRoleOptions { Name = actionParams.Context.ElementName @@ -17,7 +17,7 @@ public partial class PlaywrightWebDriver if (count == 0) { - elements = _instance.GetPage(actionParams.ConversationId) + elements = _instance.GetPage(actionParams.ContextId) .GetByRole(AriaRole.Link, new PageGetByRoleOptions { Name = actionParams.Context.ElementName @@ -27,7 +27,7 @@ public partial class PlaywrightWebDriver if (count == 0) { - elements = _instance.GetPage(actionParams.ConversationId) + elements = _instance.GetPage(actionParams.ContextId) .GetByText(actionParams.Context.ElementName); count = await elements.CountAsync(); } @@ -36,12 +36,12 @@ public partial class PlaywrightWebDriver { // Infer element if not found var driverService = _services.GetRequiredService(); - var html = await FilteredButtonHtml(actionParams.ConversationId); + var html = await FilteredButtonHtml(actionParams.ContextId); var htmlElementContextOut = await driverService.InferElement(actionParams.Agent, html, actionParams.Context.ElementName, actionParams.MessageId); - elements = Locator(actionParams.ConversationId, htmlElementContextOut); + elements = Locator(actionParams.ContextId, htmlElementContextOut); if (elements == null) { @@ -54,7 +54,7 @@ public partial class PlaywrightWebDriver try { await elements.ClickAsync(); - await _instance.Wait(actionParams.ConversationId); + await _instance.Wait(actionParams.ContextId); result.IsSuccess = true; } @@ -67,12 +67,12 @@ public partial class PlaywrightWebDriver return result; } - private async Task FilteredButtonHtml(string conversationId) + private async Task FilteredButtonHtml(string contextId) { var driverService = _services.GetRequiredService(); // Retrieve the page raw html and infer the element path - var body = await _instance.GetPage(conversationId).QuerySelectorAsync("body"); + var body = await _instance.GetPage(contextId).QuerySelectorAsync("body"); var str = new List(); /*var anchors = await body.QuerySelectorAllAsync("a"); diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ClickElement.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ClickElement.cs index 3d12ed2c..c477f805 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ClickElement.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ClickElement.cs @@ -5,9 +5,9 @@ public partial class PlaywrightWebDriver public async Task ClickElement(BrowserActionParams actionParams) { var result = new BrowserActionResult(); - await _instance.Wait(actionParams.ConversationId); + await _instance.Wait(actionParams.ContextId); - var page = _instance.GetPage(actionParams.ConversationId); + var page = _instance.GetPage(actionParams.ContextId); ILocator locator = default; int count = 0; @@ -51,7 +51,7 @@ public partial class PlaywrightWebDriver await locator.ClickAsync(); // Triggered ajax - await _instance.Wait(actionParams.ConversationId); + await _instance.Wait(actionParams.ContextId); result.IsSuccess = true; } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.CloseBrowser.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.CloseBrowser.cs index 8ab94880..1e3240ea 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.CloseBrowser.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.CloseBrowser.cs @@ -2,8 +2,8 @@ namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver { - public async Task CloseBrowser(string conversationId) + public async Task CloseBrowser(string contextId) { - await _instance.Close(conversationId); + await _instance.Close(contextId); } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.CloseCurrentPage.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.CloseCurrentPage.cs new file mode 100644 index 00000000..c5ed1700 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.CloseCurrentPage.cs @@ -0,0 +1,9 @@ +namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; + +public partial class PlaywrightWebDriver +{ + public async Task CloseCurrentPage(string contextId) + { + await _instance.CloseCurrentPage(contextId); + } +} diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs index 5e0bcf8b..93f714d2 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs @@ -1,12 +1,10 @@ -using BotSharp.Abstraction.Browsing.Enums; - namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver { public async Task DoAction(MessageInfo message, ElementActionArgs action, BrowserActionResult result) { - var page = _instance.GetPage(message.ConversationId); + var page = _instance.GetPage(message.ContextId); ILocator locator = page.Locator(result.Selector); if (action.Action == BroswerActionEnum.Click) diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.EvaluateScript.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.EvaluateScript.cs index 8a72c311..a8a7eda4 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.EvaluateScript.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.EvaluateScript.cs @@ -2,10 +2,10 @@ namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver { - public async Task EvaluateScript(string conversationId, string script) + public async Task EvaluateScript(string contextId, string script) { - await _instance.Wait(conversationId); + await _instance.Wait(contextId); - return await _instance.GetPage(conversationId).EvaluateAsync(script); + return await _instance.GetPage(contextId).EvaluateAsync(script); } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ExtractData.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ExtractData.cs index 86fae9e4..b716b7ba 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ExtractData.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ExtractData.cs @@ -4,12 +4,12 @@ public partial class PlaywrightWebDriver { public async Task ExtractData(BrowserActionParams actionParams) { - await _instance.Wait(actionParams.ConversationId); + await _instance.Wait(actionParams.ContextId); await Task.Delay(3000); // Retrieve the page raw html and infer the element path - var body = await _instance.GetPage(actionParams.ConversationId).QuerySelectorAsync("body"); + var body = await _instance.GetPage(actionParams.ContextId).QuerySelectorAsync("body"); var content = await body.InnerTextAsync(); var driverService = _services.GetRequiredService(); diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GetAttributeValue.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GetAttributeValue.cs index 8b87c2c8..cf172d31 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GetAttributeValue.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GetAttributeValue.cs @@ -4,7 +4,7 @@ public partial class PlaywrightWebDriver { public async Task GetAttributeValue(MessageInfo message, ElementLocatingArgs location, BrowserActionResult result) { - var page = _instance.GetPage(message.ConversationId); + var page = _instance.GetPage(message.ContextId); ILocator locator = page.Locator(result.Selector); var value = string.Empty; diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs index e3c9a52d..5513e89f 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs @@ -2,18 +2,19 @@ namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver { - public async Task GoToPage(string conversationId, string url) + public async Task GoToPage(string contextId, string url, bool openNewTab = false) { var result = new BrowserActionResult(); try { - var response = await _instance.GetPage(conversationId).GotoAsync(url); - await _instance.GetPage(conversationId).WaitForLoadStateAsync(LoadState.DOMContentLoaded); - await _instance.GetPage(conversationId).WaitForLoadStateAsync(LoadState.NetworkIdle); + var page = openNewTab ? await _instance.NewPage(contextId) : + _instance.GetPage(contextId); + var response = await page.GotoAsync(url); + await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded); + await page.WaitForLoadStateAsync(LoadState.NetworkIdle); if (response.Status == 200) { - var page = _instance.GetPage(conversationId); result.Body = await page.ContentAsync(); result.IsSuccess = true; } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs index 6f03ca8d..a5a59334 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs @@ -4,7 +4,7 @@ namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver { - public async Task SendHttpRequest(HttpRequestParams args) + public async Task SendHttpRequest(string contextId, HttpRequestParams args) { var result = new BrowserActionResult(); @@ -25,10 +25,9 @@ public partial class PlaywrightWebDriver }})(); "; - var conv = _services.GetRequiredService(); try { - var response = await EvaluateScript(conv.ConversationId, script); + var response = await EvaluateScript(contextId, script); result.IsSuccess = true; result.Body = JsonSerializer.Serialize(response); } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserPassword.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserPassword.cs index 3d0f2419..9f302701 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserPassword.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserPassword.cs @@ -5,10 +5,10 @@ public partial class PlaywrightWebDriver public async Task InputUserPassword(BrowserActionParams actionParams) { var result = new BrowserActionResult(); - await _instance.Wait(actionParams.ConversationId); + await _instance.Wait(actionParams.ContextId); // Retrieve the page raw html and infer the element path - var body = await _instance.GetPage(actionParams.ConversationId) + var body = await _instance.GetPage(actionParams.ContextId) .QuerySelectorAsync("body"); var inputs = await body.QuerySelectorAllAsync("input"); diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserText.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserText.cs index 01eb107f..dded5f9a 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserText.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserText.cs @@ -5,9 +5,9 @@ public partial class PlaywrightWebDriver public async Task InputUserText(BrowserActionParams actionParams) { var result = new BrowserActionResult(); - await _instance.Wait(actionParams.ConversationId); + await _instance.Wait(actionParams.ContextId); - var page = _instance.GetPage(actionParams.ConversationId); + var page = _instance.GetPage(actionParams.ContextId); ILocator locator = default; int count = 0; @@ -37,12 +37,12 @@ public partial class PlaywrightWebDriver if (count == 0) { var driverService = _services.GetRequiredService(); - var html = await FilteredInputHtml(actionParams.ConversationId); + var html = await FilteredInputHtml(actionParams.ContextId); var htmlElementContextOut = await driverService.InferElement(actionParams.Agent, html, actionParams.Context.ElementText, actionParams.MessageId); - locator = Locator(actionParams.ConversationId, htmlElementContextOut); + locator = Locator(actionParams.ContextId, htmlElementContextOut); count = await locator.CountAsync(); } else if (count > 0) @@ -56,7 +56,7 @@ public partial class PlaywrightWebDriver } // Triggered ajax - await _instance.Wait(actionParams.ConversationId); + await _instance.Wait(actionParams.ContextId); result.IsSuccess = true; } catch (Exception ex) diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs index 323dd89c..e415df8c 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs @@ -2,17 +2,27 @@ namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver { - public async Task LaunchBrowser(string conversationId, string? url) + public async Task LaunchBrowser(string contextId, string? url, bool openIfNotExist = true) { var result = new BrowserActionResult() { IsSuccess = true }; - await _instance.InitInstance(conversationId); + var context = await _instance.InitInstance(contextId); if (!string.IsNullOrEmpty(url)) { - var page = await _instance.NewPage(conversationId); + // Check if the page is already open + foreach (var p in context.Pages) + { + if (p.Url == url) + { + await p.BringToFrontAsync(); + return result; + } + } + + var page = await _instance.NewPage(contextId); if (!string.IsNullOrEmpty(url)) { diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs index 3edba4bd..78fa00a3 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs @@ -11,7 +11,7 @@ public partial class PlaywrightWebDriver public async Task LocateElement(MessageInfo message, ElementLocatingArgs location) { var result = new BrowserActionResult(); - var page = _instance.GetPage(message.ConversationId); + var page = _instance.GetPage(message.ContextId); ILocator locator = page.Locator("body"); int count = 0; diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.Screenshot.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.Screenshot.cs index f0c1fffa..07fea145 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.Screenshot.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.Screenshot.cs @@ -2,12 +2,12 @@ namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver { - public async Task ScreenshotAsync(string conversationId, string path) + public async Task ScreenshotAsync(string contextId, string path) { var result = new BrowserActionResult(); - await _instance.Wait(conversationId); - var page = _instance.GetPage(conversationId); + await _instance.Wait(contextId); + var page = _instance.GetPage(contextId); await Task.Delay(500); var bytes = await page.ScreenshotAsync(new PageScreenshotOptions diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ScrollPage.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ScrollPage.cs index 2d9cabdf..5c76c6e7 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ScrollPage.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ScrollPage.cs @@ -5,9 +5,9 @@ public partial class PlaywrightWebDriver public async Task ScrollPageAsync(BrowserActionParams actionParams) { var result = new BrowserActionResult(); - await _instance.Wait(actionParams.ConversationId); + await _instance.Wait(actionParams.ContextId); - var page = _instance.GetPage(actionParams.ConversationId); + var page = _instance.GetPage(actionParams.ContextId); if(actionParams.Context.Direction == "down") await page.EvaluateAsync("window.scrollBy(0, window.innerHeight - 200)"); diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.cs index 779bbb4c..dab45548 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.cs @@ -22,12 +22,12 @@ public partial class PlaywrightWebDriver : IWebBrowser _agent = agent; } - private ILocator? Locator(string conversationId, HtmlElementContextOut context) + private ILocator? Locator(string contextId, HtmlElementContextOut context) { ILocator element = default; if (!string.IsNullOrEmpty(context.ElementId)) { - element = _instance.GetPage(conversationId).Locator($"#{context.ElementId}"); + element = _instance.GetPage(contextId).Locator($"#{context.ElementId}"); } else if (!string.IsNullOrEmpty(context.ElementName)) { @@ -38,7 +38,7 @@ public partial class PlaywrightWebDriver : IWebBrowser "button" => AriaRole.Button, _ => AriaRole.Generic }; - element = _instance.GetPage(conversationId).Locator($"[name='{context.ElementName}']"); + element = _instance.GetPage(contextId).Locator($"[name='{context.ElementName}']"); var count = element.CountAsync().Result; if (count == 0) { @@ -58,7 +58,7 @@ public partial class PlaywrightWebDriver : IWebBrowser _logger.LogError($"Can't locate the web element {context.Index}."); return null; } - element = _instance.GetPage(conversationId).Locator(context.TagName).Nth(context.Index); + element = _instance.GetPage(contextId).Locator(context.TagName).Nth(context.Index); } return element; diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/HttpRequestFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/HttpRequestFn.cs index 76b9a926..c16da29c 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/HttpRequestFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/HttpRequestFn.cs @@ -21,7 +21,7 @@ public class HttpRequestFn : IFunctionCallback var agentService = _services.GetRequiredService(); var agent = await agentService.LoadAgent(message.CurrentAgentId); - var result = await _browser.SendHttpRequest(args); + var result = await _browser.SendHttpRequest(convService.ConversationId, args); message.Content = result.IsSuccess ? result.Body : diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Using.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Using.cs index 3c8c8c94..161e150a 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Using.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Using.cs @@ -9,6 +9,7 @@ global using Microsoft.Playwright; global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.Logging; +global using BotSharp.Abstraction.Browsing.Enums; global using BotSharp.Abstraction.Conversations; global using BotSharp.Abstraction.Plugins; global using BotSharp.Abstraction.Conversations.Models; From 63ad880b1c789ae6fe54e6f7ad5f51da0609d255 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Tue, 9 Apr 2024 07:37:30 -0500 Subject: [PATCH 026/201] Release v1.3.1 --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 56e1a108..99a8ba4c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,8 +2,8 @@ net8.0 10.0 - 1.2.1 - true + 1.3.1 + false false \ No newline at end of file From f6cf392267a6fb0cd2038ff7f6a3e9330b82c00e Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 9 Apr 2024 15:52:21 -0500 Subject: [PATCH 027/201] add visible property --- .../Agents/IAgentService.cs | 2 + .../Agents/Services/AgentService.Rendering.cs | 59 +++++++++++++++++++ .../Providers/ChatCompletionProvider.cs | 3 +- .../Providers/ChatCompletionProvider.cs | 2 +- 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs index c8aee271..e67cd6b8 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs @@ -26,6 +26,8 @@ public interface IAgentService bool RenderFunction(Agent agent, FunctionDef def); + FunctionParametersDef? RenderFunctionProperty(Agent agent, FunctionDef def); + /// /// Get agent detail without trigger any hook. /// diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs index 9ce7a7a8..810e101a 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs @@ -1,5 +1,6 @@ using BotSharp.Abstraction.Loggers; using BotSharp.Abstraction.Templating; +using Newtonsoft.Json.Linq; namespace BotSharp.Core.Agents.Services; @@ -32,6 +33,64 @@ public partial class AgentService return true; } + public FunctionParametersDef? RenderFunctionProperty(Agent agent, FunctionDef def) + { + var parameterDef = def?.Parameters; + var propertyDef = parameterDef?.Properties; + if (propertyDef == null) return null; + + var visibleExpress = "visibility_expression"; + var root = propertyDef.RootElement; + var iterator = root.EnumerateObject(); + var list = new List(); + while (iterator.MoveNext()) + { + var prop = iterator.Current; + var name = prop.Name; + var node = prop.Value; + var matched = true; + if (node.TryGetProperty(visibleExpress, out var element)) + { + var expression = element.GetString(); + var render = _services.GetRequiredService(); + var result = render.Render(expression, new Dictionary + { + { "states", agent.TemplateDict } + }); + matched = result == "visible"; + } + + if (matched) + { + list.Add(name); + } + } + + var rootObject = JObject.Parse(root.GetRawText()); + var clonedRoot = rootObject.DeepClone() as JObject; + var required = parameterDef?.Required ?? new List(); + foreach (var property in rootObject.Properties()) + { + if (list.Contains(property.Name)) + { + var value = clonedRoot.GetValue(property.Name) as JObject; + if (value != null && value.ContainsKey(visibleExpress)) + { + value.Remove(visibleExpress); + } + } + else + { + clonedRoot.Remove(property.Name); + required.Remove(property.Name); + } + } + + parameterDef.Properties = JsonSerializer.Deserialize(clonedRoot.ToString()); + parameterDef.Required = required; + return parameterDef; ; + } + public string RenderedTemplate(Agent agent, string templateName) { // render liquid template diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs index e3f6afca..0e5cc0ab 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs @@ -221,11 +221,12 @@ public class ChatCompletionProvider : IChatCompletion { if (agentService.RenderFunction(agent, function)) { + var property = agentService.RenderFunctionProperty(agent, function); chatCompletionsOptions.Functions.Add(new FunctionDefinition { Name = function.Name, Description = function.Description, - Parameters = BinaryData.FromObjectAsJson(function.Parameters) + Parameters = BinaryData.FromObjectAsJson(property) }); } } diff --git a/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs index 3423347e..7d529df1 100644 --- a/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs @@ -222,7 +222,7 @@ public class ChatCompletionProvider : IChatCompletion return (prompt, messages.ToArray(), functions.ToArray()); } - private string GetPrompt(List messages,List functions) + private string GetPrompt(List messages, List functions) { var prompt = string.Empty; From ed729bf18dccd53581ec964b5de820742f6965b7 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 9 Apr 2024 16:00:53 -0500 Subject: [PATCH 028/201] minor change --- .../BotSharp.Core/Agents/Services/AgentService.Rendering.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs index 810e101a..a0b2f130 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs @@ -42,7 +42,7 @@ public partial class AgentService var visibleExpress = "visibility_expression"; var root = propertyDef.RootElement; var iterator = root.EnumerateObject(); - var list = new List(); + var visibleProps = new List(); while (iterator.MoveNext()) { var prop = iterator.Current; @@ -62,7 +62,7 @@ public partial class AgentService if (matched) { - list.Add(name); + visibleProps.Add(name); } } @@ -71,7 +71,7 @@ public partial class AgentService var required = parameterDef?.Required ?? new List(); foreach (var property in rootObject.Properties()) { - if (list.Contains(property.Name)) + if (visibleProps.Contains(property.Name)) { var value = clonedRoot.GetValue(property.Name) as JObject; if (value != null && value.ContainsKey(visibleExpress)) From 90c0ef014de8191972b9cd41ffe560404d318235 Mon Sep 17 00:00:00 2001 From: jli238 <40345639+jli238@users.noreply.github.com> Date: Tue, 9 Apr 2024 16:17:55 -0500 Subject: [PATCH 029/201] Update args definition for router agent Update args definition for router agent, to provide a more stable args response output from agent. Tested solid on GPT Playground. --- .../01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid index 9caf041d..93960782 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid @@ -4,7 +4,7 @@ You're {{router.name}} ({{router.description}}). Follow these steps to handle us 3. Determine which agent is suitable to handle this conversation. 4. Re-think on whether the function you chose matches the reason. 5. For agent required arguments, think carefully, leave it as blank object if user doesn't provide specific arguments. -6. Please do not make up any parameters when there is no exact value provided, you must set the parameter value as null. +6. You must include all required args when using selected FUNCTIONS, but you must not make up any parameters when there is no exact value provided, those parameters must set value as null if not declared. 7. Response must be in JSON format. {% if routing_requirements and routing_requirements != empty %} From 45fd30aaf84a64c1806bbffd872489419b79531f Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 10 Apr 2024 12:45:35 -0500 Subject: [PATCH 030/201] add readonly load state --- .../Conversations/IConversationStateService.cs | 2 +- .../Conversations/Services/ConversationStateService.cs | 6 +++--- .../BotSharp.OpenAPI/Controllers/ConversationController.cs | 2 +- .../Repository/MongoRepository.Conversation.cs | 5 +---- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs index 655e657a..fe3f9193 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs @@ -9,7 +9,7 @@ namespace BotSharp.Abstraction.Conversations; public interface IConversationStateService { string GetConversationId(); - Dictionary Load(string conversationId); + Dictionary Load(string conversationId, bool isReadOnly = false); string GetState(string name, string defaultValue = ""); bool ContainsState(string name); Dictionary GetStates(); diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index 36b2ddeb..7a08b10b 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -117,9 +117,9 @@ public class ConversationStateService : IConversationStateService, IDisposable return this; } - public Dictionary Load(string conversationId) + public Dictionary Load(string conversationId, bool isReadOnly = false) { - _conversationId = conversationId; + _conversationId = !isReadOnly ? conversationId : null; var routingCtx = _services.GetRequiredService(); var curMsgId = routingCtx.MessageId; @@ -320,7 +320,7 @@ public class ConversationStateService : IConversationStateService, IDisposable public void Dispose() { - Save(); + } public bool ContainsState(string name) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index e01a0f46..8bf3b757 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -125,7 +125,7 @@ public class ConversationController : ControllerBase var result = ConversationViewModel.FromSession(conversations.Items.First()); var state = _services.GetRequiredService(); - result.States = state.Load(conversationId); + result.States = state.Load(conversationId, isReadOnly: true); var user = await userService.GetUser(result.User.Id); result.User = UserViewModel.FromUser(user); diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs index ac5a47d5..ed15803e 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs @@ -191,14 +191,11 @@ public partial class MongoRepository { if (string.IsNullOrEmpty(conversationId) || states == null) return; - var filterConv = Builders.Filter.Eq(x => x.Id, conversationId); var filterStates = Builders.Filter.Eq(x => x.ConversationId, conversationId); var saveStates = states.Select(x => StateMongoElement.ToMongoElement(x)).ToList(); var updateStates = Builders.Update.Set(x => x.States, saveStates); - var updateConv = Builders.Update.Set(x => x.UpdatedTime, DateTime.UtcNow); _dc.ConversationStates.UpdateOne(filterStates, updateStates); - _dc.Conversations.UpdateOne(filterConv, updateConv); } public void UpdateConversationStatus(string conversationId, string status) @@ -391,7 +388,7 @@ public partial class MongoRepository { var skip = (page - 1) * batchSize; var candidates = _dc.Conversations.AsQueryable() - .Where(x => (x.DialogCount <= messageLimit) && x.UpdatedTime <= utcNow.AddHours(-bufferHours)) + .Where(x => x.DialogCount <= messageLimit && x.UpdatedTime <= utcNow.AddHours(-bufferHours)) .Skip(skip) .Take(batchSize) .Select(x => x.Id) From cb812171e657596d9702027f4fdd3eea160da305 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 10 Apr 2024 12:47:51 -0500 Subject: [PATCH 031/201] revert code --- .../Conversations/Services/ConversationStateService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index 7a08b10b..de54dbb9 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -320,7 +320,7 @@ public class ConversationStateService : IConversationStateService, IDisposable public void Dispose() { - + Save(); } public bool ContainsState(string name) From 84220a8a71bf9509f563bad21a1954074d1afde6 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 10 Apr 2024 12:49:12 -0500 Subject: [PATCH 032/201] minor change --- .../Conversations/Services/ConversationStateService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index de54dbb9..e86f4610 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -124,8 +124,8 @@ public class ConversationStateService : IConversationStateService, IDisposable var routingCtx = _services.GetRequiredService(); var curMsgId = routingCtx.MessageId; - _historyStates = _db.GetConversationStates(_conversationId); - var dialogs = _db.GetConversationDialogs(_conversationId); + _historyStates = _db.GetConversationStates(conversationId); + var dialogs = _db.GetConversationDialogs(conversationId); var userDialogs = dialogs.Where(x => x.MetaData?.Role == AgentRole.User || x.MetaData?.Role == UserRole.Client) .OrderBy(x => x.MetaData?.CreateTime) .ToList(); @@ -177,7 +177,7 @@ public class ConversationStateService : IConversationStateService, IDisposable _logger.LogInformation($"[STATE] {key} : {data}"); } - _logger.LogInformation($"Loaded conversation states: {_conversationId}"); + _logger.LogInformation($"Loaded conversation states: {conversationId}"); var hooks = _services.GetServices(); foreach (var hook in hooks) { From 5564287871efe44226eef406898162d98b402c80 Mon Sep 17 00:00:00 2001 From: "C. Oceania" Date: Wed, 10 Apr 2024 15:02:19 -0500 Subject: [PATCH 033/201] Agent Name is contaminated. --- .../Routing/Planning/NaivePlanner.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Infrastructure/BotSharp.Core/Routing/Planning/NaivePlanner.cs b/src/Infrastructure/BotSharp.Core/Routing/Planning/NaivePlanner.cs index eefb88f7..aeed1a78 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Planning/NaivePlanner.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Planning/NaivePlanner.cs @@ -165,6 +165,22 @@ public class NaivePlanner : IPlaner malformed = true; } + // Agent Name is contaminated. + if (args.Function == "route_to_agent") + { + // Action agent name + if (!agents.Any(x => x.Name == args.AgentName)) + { + args.AgentName = agents.FirstOrDefault(x => args.AgentName.Contains(x.Name))?.Name ?? args.AgentName; + } + + // Goal agent name + if (!agents.Any(x => x.Name == args.OriginalAgent)) + { + args.OriginalAgent = agents.FirstOrDefault(x => args.OriginalAgent.Contains(x.Name))?.Name ?? args.OriginalAgent; + } + } + if (malformed) { _logger.LogWarning($"Captured LLM malformed response"); From 32a084289e46b21b0cbc4c9942c2b0435be122d6 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Thu, 11 Apr 2024 02:00:17 -0500 Subject: [PATCH 034/201] refine agent refresh --- .../Agents/IAgentService.cs | 4 +- .../Repositories/IBotSharpRepository.cs | 3 +- .../Services/AgentService.RefreshAgents.cs | 86 ++++++++++++------- .../Services/AgentService.UpdateAgent.cs | 27 ++++-- .../Repository/BotSharpDbContext.cs | 69 +++++---------- .../FileRepository/FileRepository.Agent.cs | 10 +-- .../FileRepository.AgentTask.cs | 16 ++-- .../Tasks/Services/AgentTaskService.cs | 2 +- .../Controllers/AgentController.cs | 8 +- .../Repository/MongoRepository.Agent.cs | 20 +++++ .../Repository/MongoRepository.AgentTask.cs | 12 ++- 11 files changed, 145 insertions(+), 112 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs index e67cd6b8..5ab249b8 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs @@ -10,7 +10,7 @@ namespace BotSharp.Abstraction.Agents; public interface IAgentService { Task CreateAgent(Agent agent); - Task RefreshAgents(); + Task RefreshAgents(); Task> GetAgents(AgentFilter filter); /// @@ -37,7 +37,7 @@ public interface IAgentService Task DeleteAgent(string id); Task UpdateAgent(Agent agent, AgentField updateField); - Task UpdateAgentFromFile(string id); + Task UpdateAgentFromFile(string id); string GetDataDir(); string GetAgentDataDir(string agentId); diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index e177b354..a1124975 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -32,6 +32,7 @@ public interface IBotSharpRepository void BulkInsertAgents(List agents); void BulkInsertUserAgents(List userAgents); bool DeleteAgents(); + bool DeleteAgent(string agentId); List GetAgentResponses(string agentId, string prefix, string intent); string GetAgentTemplate(string agentId, string templateName); #endregion @@ -42,7 +43,7 @@ public interface IBotSharpRepository void InsertAgentTask(AgentTask task); void BulkInsertAgentTasks(List tasks); void UpdateAgentTask(AgentTask task, AgentTaskField field); - bool DeleteAgentTask(string agentId, string taskId); + bool DeleteAgentTask(string agentId, List taskIds); bool DeleteAgentTasks(); #endregion diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs index 9c3b34f9..3077e929 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs @@ -1,55 +1,79 @@ -using BotSharp.Abstraction.Tasks.Models; using System.IO; namespace BotSharp.Core.Agents.Services; public partial class AgentService { - public async Task RefreshAgents() + public async Task RefreshAgents() { - var isAgentDeleted = _db.DeleteAgents(); - var isTaskDeleted = _db.DeleteAgentTasks(); - if (!isAgentDeleted) return; - var dbSettings = _services.GetRequiredService(); var agentDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, dbSettings.FileRepository, _agentSettings.DataDir); + string refreshResult; + if (!Directory.Exists(agentDir)) + { + refreshResult = $"Cannot find the directory: {agentDir}"; + return refreshResult; + } + var user = _db.GetUserById(_user.Id); - var agents = new List(); - var userAgents = new List(); - var agentTasks = new List(); + var refreshedAgents = new List(); foreach (var dir in Directory.GetDirectories(agentDir)) { - var agentJson = File.ReadAllText(Path.Combine(dir, "agent.json")); - var agent = JsonSerializer.Deserialize(agentJson, _options); - if (agent == null) continue; + try + { + var agentJson = File.ReadAllText(Path.Combine(dir, "agent.json")); + var agent = JsonSerializer.Deserialize(agentJson, _options); + + if (agent == null) + { + _logger.LogError($"Cannot find agent in file directory: {dir}"); + continue; + } - var functions = FetchFunctionsFromFile(dir); - var instruction = FetchInstructionFromFile(dir); - var responses = FetchResponsesFromFile(dir); - var templates = FetchTemplatesFromFile(dir); - var samples = FetchSamplesFromFile(dir); - agent.SetInstruction(instruction) - .SetTemplates(templates) - .SetFunctions(functions) - .SetResponses(responses) - .SetSamples(samples); - agents.Add(agent); + var functions = FetchFunctionsFromFile(dir); + var instruction = FetchInstructionFromFile(dir); + var responses = FetchResponsesFromFile(dir); + var templates = FetchTemplatesFromFile(dir); + var samples = FetchSamplesFromFile(dir); + agent.SetInstruction(instruction) + .SetTemplates(templates) + .SetFunctions(functions) + .SetResponses(responses) + .SetSamples(samples); - var userAgent = BuildUserAgent(agent.Id, user.Id); - userAgents.Add(userAgent); + var userAgent = BuildUserAgent(agent.Id, user.Id); + var tasks = FetchTasksFromFile(dir); - var tasks = FetchTasksFromFile(dir); - agentTasks.AddRange(tasks); + var isAgentDeleted = _db.DeleteAgent(agent.Id); + if (isAgentDeleted) + { + _db.BulkInsertAgents(new List { agent }); + _db.BulkInsertUserAgents(new List { userAgent }); + _db.BulkInsertAgentTasks(tasks); + refreshedAgents.Add(agent.Name); + } + } + catch (Exception ex) + { + _logger.LogError($"Failed to migrate agent in file directory: {dir}\r\nError: {ex.Message}"); + } } - _db.BulkInsertAgents(agents); - _db.BulkInsertUserAgents(userAgents); - _db.BulkInsertAgentTasks(agentTasks); + if (!refreshedAgents.IsNullOrEmpty()) + { + Utilities.ClearCache(); + refreshResult = $"Agents are migrated! {string.Join("\r\n", refreshedAgents)}"; + } + else + { + refreshResult = "No agent gets refreshed!"; + } - Utilities.ClearCache(); + _logger.LogInformation(refreshResult); + return refreshResult; } } diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs index 4577af6d..5eca3f8c 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs @@ -39,11 +39,13 @@ public partial class AgentService await Task.CompletedTask; } - public async Task UpdateAgentFromFile(string id) + public async Task UpdateAgentFromFile(string id) { var agent = _db.GetAgent(id); - - if (agent == null) return; + if (agent == null) + { + return $"Cannot find agent ${id}"; + } var dbSettings = _services.GetRequiredService(); var agentSettings = _services.GetRequiredService(); @@ -53,7 +55,12 @@ public partial class AgentService var clonedAgent = Agent.Clone(agent); var foundAgent = FetchAgentFileById(agent.Id, filePath); - if (foundAgent != null) + if (foundAgent == null) + { + return $"Cannot find agent {agent.Name} in file directory: {filePath}"; + } + + try { clonedAgent.SetId(foundAgent.Id) .SetName(foundAgent.Name) @@ -71,15 +78,19 @@ public partial class AgentService .SetLlmConfig(foundAgent.LlmConfig); _db.UpdateAgent(clonedAgent, AgentField.All); - Utilities.ClearCache(); + return $"Agent {agent.Name} has been migrated!"; + } + catch (Exception ex) + { + return $"Failed to migrate agent {agent.Name} in file directory {filePath}.\r\nError: {ex.Message}"; } - - await Task.CompletedTask; } - private Agent FetchAgentFileById(string agentId, string filePath) + private Agent? FetchAgentFileById(string agentId, string filePath) { + if (!Directory.Exists(filePath)) return null; + foreach (var dir in Directory.GetDirectories(filePath)) { var agentJson = File.ReadAllText(Path.Combine(dir, "agent.json")); diff --git a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs index d1361d02..0605c525 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs @@ -73,86 +73,57 @@ public class BotSharpDbContext : Database, IBotSharpRepository #region Agent public Agent GetAgent(string agentId) - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public List GetAgents(AgentFilter filter) - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public List GetAgentsByUser(string userId) - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public void UpdateAgent(Agent agent, AgentField field) - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public string GetAgentTemplate(string agentId, string templateName) - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public List GetAgentResponses(string agentId, string prefix, string intent) - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public void BulkInsertAgents(List agents) - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public void BulkInsertUserAgents(List userAgents) - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public bool DeleteAgents() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); + + public bool DeleteAgent(string agentId) + => throw new NotImplementedException(); #endregion #region Agent Task public PagedItems GetAgentTasks(AgentTaskFilter filter) - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public AgentTask? GetAgentTask(string agentId, string taskId) - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public void InsertAgentTask(AgentTask task) - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public void BulkInsertAgentTasks(List tasks) - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public void UpdateAgentTask(AgentTask task, AgentTaskField field) - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); - public bool DeleteAgentTask(string agentId, string taskId) - { - throw new NotImplementedException(); - } + public bool DeleteAgentTask(string agentId, List taskIds) + => throw new NotImplementedException(); public bool DeleteAgentTasks() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); #endregion #region Conversation diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs index 1fbca99c..20f8bcdd 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs @@ -1,9 +1,4 @@ -using BotSharp.Abstraction.Agents.Models; -using BotSharp.Abstraction.Functions.Models; -using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Routing.Models; -using BotSharp.Abstraction.Tasks.Models; -using Microsoft.Extensions.Logging; using System.IO; namespace BotSharp.Core.Repository @@ -419,5 +414,10 @@ namespace BotSharp.Core.Repository { return false; } + + public bool DeleteAgent(string agentId) + { + return false; + } } } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs index 73a8e3ed..df80bc8b 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs @@ -1,7 +1,5 @@ -using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Tasks.Models; using System.IO; -using System.Threading.Tasks; namespace BotSharp.Core.Repository; @@ -192,18 +190,22 @@ public partial class FileRepository File.WriteAllText(taskFile, fileContent); } - public bool DeleteAgentTask(string agentId, string taskId) + public bool DeleteAgentTask(string agentId, List taskIds) { var agentDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId); - if (!Directory.Exists(agentDir)) return false; + if (!Directory.Exists(agentDir) || taskIds.IsNullOrEmpty()) return false; var taskDir = Path.Combine(agentDir, "tasks"); if (!Directory.Exists(taskDir)) return false; - var taskFile = FindTaskFileById(taskDir, taskId); - if (string.IsNullOrWhiteSpace(taskFile)) return false; + foreach (var taskId in taskIds) + { + var taskFile = FindTaskFileById(taskDir, taskId); + if (string.IsNullOrWhiteSpace(taskFile)) return false; - File.Delete(taskFile); + File.Delete(taskFile); + } + return true; } diff --git a/src/Infrastructure/BotSharp.Core/Tasks/Services/AgentTaskService.cs b/src/Infrastructure/BotSharp.Core/Tasks/Services/AgentTaskService.cs index 1e7cdfdf..13ffc646 100644 --- a/src/Infrastructure/BotSharp.Core/Tasks/Services/AgentTaskService.cs +++ b/src/Infrastructure/BotSharp.Core/Tasks/Services/AgentTaskService.cs @@ -72,7 +72,7 @@ public class AgentTaskService : IAgentTaskService public async Task DeleteTask(string agentId, string taskId) { var db = _services.GetRequiredService(); - var isDeleted = db.DeleteAgentTask(agentId, taskId); + var isDeleted = db.DeleteAgentTask(agentId, new List { taskId }); return await Task.FromResult(isDeleted); } } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs index 5d7f7b56..77f73061 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs @@ -84,15 +84,15 @@ public class AgentController : ControllerBase } [HttpPost("/refresh-agents")] - public async Task RefreshAgents() + public async Task RefreshAgents() { - await _agentService.RefreshAgents(); + return await _agentService.RefreshAgents(); } [HttpPut("/agent/file/{agentId}")] - public async Task UpdateAgentFromFile([FromRoute] string agentId) + public async Task UpdateAgentFromFile([FromRoute] string agentId) { - await _agentService.UpdateAgentFromFile(agentId); + return await _agentService.UpdateAgentFromFile(agentId); } [HttpPut("/agent/{agentId}")] diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs index 3272930b..07d30984 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs @@ -398,7 +398,27 @@ public partial class MongoRepository { return false; } + } + public bool DeleteAgent(string agentId) + { + try + { + if (string.IsNullOrEmpty(agentId)) return false; + + var agentFilter = Builders.Filter.Eq(x => x.Id, agentId); + var agentUserFilter = Builders.Filter.Eq(x => x.AgentId, agentId); + var agentTaskFilter = Builders.Filter.Eq(x => x.AgentId, agentId); + + _dc.Agents.DeleteOne(agentFilter); + _dc.UserAgents.DeleteMany(agentUserFilter); + _dc.AgentTasks.DeleteMany(agentTaskFilter); + return true; + } + catch + { + return false; + } } private Agent TransformAgentDocument(AgentDocument? agentDoc) diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentTask.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentTask.cs index 125f3cd8..a3df61da 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentTask.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentTask.cs @@ -155,12 +155,16 @@ public partial class MongoRepository _dc.AgentTasks.ReplaceOne(filter, taskDoc); } - public bool DeleteAgentTask(string agentId, string taskId) + public bool DeleteAgentTask(string agentId, List taskIds) { - if (string.IsNullOrEmpty(taskId)) return false; + if (taskIds.IsNullOrEmpty()) return false; - var filter = Builders.Filter.Eq(x => x.Id, taskId); - var taskDeleted = _dc.AgentTasks.DeleteOne(filter); + var builder = Builders.Filter; + var filters = new List> + { + builder.In(x => x.Id, taskIds) + }; + var taskDeleted = _dc.AgentTasks.DeleteMany(builder.And(filters)); return taskDeleted.DeletedCount > 0; } From e4e3ee56682af77ace0f0bd11c2479bee966d401 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Thu, 11 Apr 2024 02:04:54 -0500 Subject: [PATCH 035/201] minor change --- .../Repository/FileRepository/FileRepository.AgentTask.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs index df80bc8b..d542b28b 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs @@ -198,15 +198,17 @@ public partial class FileRepository var taskDir = Path.Combine(agentDir, "tasks"); if (!Directory.Exists(taskDir)) return false; + var deletedTasks = new List(); foreach (var taskId in taskIds) { var taskFile = FindTaskFileById(taskDir, taskId); - if (string.IsNullOrWhiteSpace(taskFile)) return false; + if (string.IsNullOrWhiteSpace(taskFile)) continue; File.Delete(taskFile); + deletedTasks.Add(taskId); } - return true; + return deletedTasks.Any(); } public bool DeleteAgentTasks() From 2af4102b65ebff7ef0e80faa2172277bbde018a7 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 11 Apr 2024 10:19:57 -0500 Subject: [PATCH 036/201] add log in agent refresh --- .../Services/AgentService.RefreshAgents.cs | 4 +++- .../Services/AgentService.UpdateAgent.cs | 18 ++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs index 3077e929..356707a8 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs @@ -51,10 +51,12 @@ public partial class AgentService var isAgentDeleted = _db.DeleteAgent(agent.Id); if (isAgentDeleted) { + await Task.Delay(100); _db.BulkInsertAgents(new List { agent }); _db.BulkInsertUserAgents(new List { userAgent }); _db.BulkInsertAgentTasks(tasks); refreshedAgents.Add(agent.Name); + _logger.LogInformation($"Agent {agent.Name} has been migrated."); } } catch (Exception ex) @@ -66,7 +68,7 @@ public partial class AgentService if (!refreshedAgents.IsNullOrEmpty()) { Utilities.ClearCache(); - refreshResult = $"Agents are migrated! {string.Join("\r\n", refreshedAgents)}"; + refreshResult = $"Agents are migrated!\r\n{string.Join("\r\n", refreshedAgents)}"; } else { diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs index 5eca3f8c..747bd11c 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs @@ -41,10 +41,13 @@ public partial class AgentService public async Task UpdateAgentFromFile(string id) { + string updateResult; var agent = _db.GetAgent(id); if (agent == null) { - return $"Cannot find agent ${id}"; + updateResult = $"Cannot find agent ${id}"; + _logger.LogError(updateResult); + return updateResult; } var dbSettings = _services.GetRequiredService(); @@ -57,7 +60,9 @@ public partial class AgentService var foundAgent = FetchAgentFileById(agent.Id, filePath); if (foundAgent == null) { - return $"Cannot find agent {agent.Name} in file directory: {filePath}"; + updateResult = $"Cannot find agent {agent.Name} in file directory: {filePath}"; + _logger.LogError(updateResult); + return updateResult; } try @@ -79,11 +84,16 @@ public partial class AgentService _db.UpdateAgent(clonedAgent, AgentField.All); Utilities.ClearCache(); - return $"Agent {agent.Name} has been migrated!"; + + updateResult = $"Agent {agent.Name} has been migrated!"; + _logger.LogInformation(updateResult); + return updateResult; } catch (Exception ex) { - return $"Failed to migrate agent {agent.Name} in file directory {filePath}.\r\nError: {ex.Message}"; + updateResult = $"Failed to migrate agent {agent.Name} in file directory {filePath}.\r\nError: {ex.Message}"; + _logger.LogError(updateResult); + return updateResult; } } From 4aa5b92b926c9a1bad24e876d8c06fb6cb73c0ea Mon Sep 17 00:00:00 2001 From: sylviachency <33144082+sylviachency@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:41:48 -0500 Subject: [PATCH 037/201] Update HumanInterventionNeededHandler.cs address transfer to person issue --- .../Routing/Handlers/HumanInterventionNeededHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs index f0de3a9c..4aca2c3a 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs @@ -6,7 +6,7 @@ public class HumanInterventionNeededHandler : RoutingHandlerBase, IRoutingHandle { public string Name => "human_intervention_needed"; - public string Description => "Reach out to human being, customer service or customer representative."; + public string Description => "Reach out to human customer service."; public List Parameters => new List { From d7524162d8804bc8ef3e285f766b62d0b2602210 Mon Sep 17 00:00:00 2001 From: sylviachency <33144082+sylviachency@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:43:43 -0500 Subject: [PATCH 038/201] Update planner_prompt.naive.liquid address transfer to person issue --- .../templates/planner_prompt.naive.liquid | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 2b42efaa..3a332350 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,5 @@ 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 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. \ No newline at end of file +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. From e319992206bcf801b63b160f8c34e26954556db5 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Fri, 12 Apr 2024 10:00:55 -0500 Subject: [PATCH 039/201] add repository enum --- .../Repositories/Enums/RepositoryEnum.cs | 7 +++++++ .../Agents/Services/AgentService.RefreshAgents.cs | 10 +++++++++- .../Agents/Services/AgentService.UpdateAgent.cs | 13 +++++++++++-- .../BotSharp.Core/Repository/RepositoryPlugin.cs | 3 ++- .../MongoStoragePlugin.cs | 3 ++- 5 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Repositories/Enums/RepositoryEnum.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/Enums/RepositoryEnum.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/Enums/RepositoryEnum.cs new file mode 100644 index 00000000..f71845a5 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/Enums/RepositoryEnum.cs @@ -0,0 +1,7 @@ +namespace BotSharp.Abstraction.Repositories.Enums; + +public static class RepositoryEnum +{ + public const string FileRepository = nameof(FileRepository); + public const string MongoRepository = nameof(MongoRepository); +} diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs index 356707a8..0b61977e 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.Repositories.Enums; using System.IO; namespace BotSharp.Core.Agents.Services; @@ -6,12 +7,19 @@ public partial class AgentService { public async Task RefreshAgents() { + string refreshResult; var dbSettings = _services.GetRequiredService(); + if (dbSettings.Default == RepositoryEnum.FileRepository) + { + refreshResult = $"Invalid database repository setting: {dbSettings.Default}"; + _logger.LogWarning(refreshResult); + return refreshResult; + } + var agentDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, dbSettings.FileRepository, _agentSettings.DataDir); - string refreshResult; if (!Directory.Exists(agentDir)) { refreshResult = $"Cannot find the directory: {agentDir}"; diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs index 747bd11c..0cce4209 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs @@ -1,6 +1,7 @@ using BotSharp.Abstraction.Agents.Models; using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Repositories; +using BotSharp.Abstraction.Repositories.Enums; using BotSharp.Abstraction.Routing.Models; using System.IO; @@ -42,6 +43,16 @@ public partial class AgentService public async Task UpdateAgentFromFile(string id) { string updateResult; + var dbSettings = _services.GetRequiredService(); + var agentSettings = _services.GetRequiredService(); + + if (dbSettings.Default == RepositoryEnum.FileRepository) + { + updateResult = $"Invalid database repository setting: {dbSettings.Default}"; + _logger.LogWarning(updateResult); + return updateResult; + } + var agent = _db.GetAgent(id); if (agent == null) { @@ -50,8 +61,6 @@ public partial class AgentService return updateResult; } - var dbSettings = _services.GetRequiredService(); - var agentSettings = _services.GetRequiredService(); var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, dbSettings.FileRepository, agentSettings.DataDir); diff --git a/src/Infrastructure/BotSharp.Core/Repository/RepositoryPlugin.cs b/src/Infrastructure/BotSharp.Core/Repository/RepositoryPlugin.cs index ee397e38..3160597a 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/RepositoryPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/RepositoryPlugin.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.Repositories.Enums; using BotSharp.Abstraction.Settings; using Microsoft.Extensions.Configuration; @@ -32,7 +33,7 @@ public class RepositoryPlugin : IBotSharpPlugin var myDatabaseSettings = new BotSharpDatabaseSettings(); config.Bind("Database", myDatabaseSettings); - if (myDatabaseSettings.Default == "FileRepository") + if (myDatabaseSettings.Default == RepositoryEnum.FileRepository) { services.AddScoped(); } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoStoragePlugin.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoStoragePlugin.cs index a7a574fa..8c4c43b9 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoStoragePlugin.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoStoragePlugin.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Plugins.Models; +using BotSharp.Abstraction.Repositories.Enums; using BotSharp.Plugin.MongoStorage.Repository; namespace BotSharp.Plugin.MongoStorage; @@ -18,7 +19,7 @@ public class MongoStoragePlugin : IBotSharpPlugin var dbSettings = new BotSharpDatabaseSettings(); config.Bind("Database", dbSettings); - if (dbSettings.Default == "MongoRepository") + if (dbSettings.Default == RepositoryEnum.MongoRepository) { services.AddScoped((IServiceProvider x) => { From 8df609ad2c176a8ed24a25f287e465bfd6772ae2 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Fri, 12 Apr 2024 11:37:38 -0500 Subject: [PATCH 040/201] add post action disclaimer --- .../Messaging/Models/RichContent/ElementButton.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs index bc13660a..294b1835 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs @@ -20,4 +20,7 @@ public class ElementButton [JsonPropertyName("is_secondary")] public bool IsSecondary { get; set; } + + [JsonPropertyName("post_action_disclaimer")] + public string? PostActionDisclaimer { get; set; } } From 668df3e1baa943ba74e6028fadcd5d803be0bba6 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Sat, 13 Apr 2024 10:23:20 -0500 Subject: [PATCH 041/201] fix file serialization --- .../Services/ConversationStorage.cs | 25 +++---------------- .../FileRepository.Conversation.cs | 23 +++++++++++++++-- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs index 532f56d4..ba0d9198 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs @@ -8,8 +8,8 @@ namespace BotSharp.Core.Conversations.Services; public class ConversationStorage : IConversationStorage { private readonly BotSharpDatabaseSettings _dbSettings; + private readonly BotSharpOptions _options; private readonly IServiceProvider _services; - private readonly JsonSerializerOptions _jsonOptions; public ConversationStorage( BotSharpDatabaseSettings dbSettings, @@ -18,7 +18,7 @@ public class ConversationStorage : IConversationStorage { _dbSettings = dbSettings; _services = services; - _jsonOptions = InitJsonSerilizerOptions(options); + _options = options; } public void Append(string conversationId, RoleDialogModel dialog) @@ -70,7 +70,7 @@ public class ConversationStorage : IConversationStorage return; } - var richContent = dialog.RichContent != null ? JsonSerializer.Serialize(dialog.RichContent, _jsonOptions) : null; + var richContent = dialog.RichContent != null ? JsonSerializer.Serialize(dialog.RichContent, _options.JsonSerializerOptions) : null; dialogElements.Add(new DialogElement(meta, content, richContent)); } @@ -95,7 +95,7 @@ public class ConversationStorage : IConversationStorage var senderId = role == AgentRole.Function ? currentAgentId : meta.SenderId; var createdAt = meta.CreateTime; var richContent = !string.IsNullOrEmpty(dialog.RichContent) ? - JsonSerializer.Deserialize>(dialog.RichContent, _jsonOptions) : null; + JsonSerializer.Deserialize>(dialog.RichContent, _options.JsonSerializerOptions) : null; var record = new RoleDialogModel(role, content) { @@ -140,21 +140,4 @@ public class ConversationStorage : IConversationStorage } return Path.Combine(dir, "dialogs.txt"); } - - private JsonSerializerOptions InitJsonSerilizerOptions(BotSharpOptions botSharOptions) - { - var options = botSharOptions.JsonSerializerOptions; - var jsonOptions = new JsonSerializerOptions - { - PropertyNameCaseInsensitive = options.PropertyNameCaseInsensitive, - PropertyNamingPolicy = options.PropertyNamingPolicy ?? JsonNamingPolicy.CamelCase, - AllowTrailingCommas = options.AllowTrailingCommas, - }; - - foreach (var converter in options.Converters) - { - jsonOptions.Converters.Add(converter); - } - return jsonOptions; - } } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs index f55ac4e1..70f28bbe 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs @@ -521,7 +521,7 @@ namespace BotSharp.Core.Repository CreateTime = DateTime.Parse(blocks[0]) }; - var richContent = blocks.Count() > 6 ? blocks[6] : null; + var richContent = blocks.Count() > 6 ? DecodeRichContent(blocks[6]) : null; dialogs.Add(new DialogElement(meta, trimmed, richContent)); } } @@ -537,7 +537,8 @@ namespace BotSharp.Core.Repository { var meta = element.MetaData; var createTime = meta.CreateTime.ToString("MM/dd/yyyy hh:mm:ss.ffffff tt", CultureInfo.InvariantCulture); - var metaStr = $"{createTime}|{meta.Role}|{meta.AgentId}|{meta.MessageId}|{meta.SenderId}|{meta.FunctionName}|{element.RichContent}"; + var encodedRichContent = EncodeRichContent(element.RichContent); + var metaStr = $"{createTime}|{meta.Role}|{meta.AgentId}|{meta.MessageId}|{meta.SenderId}|{meta.FunctionName}|{encodedRichContent}"; dialogTexts.Add(metaStr); var content = $" - {element.Content}"; dialogTexts.Add(content); @@ -686,6 +687,24 @@ namespace BotSharp.Core.Repository File.WriteAllText(breakpointDir, breakpointStr); return true; } + + private string? EncodeRichContent(string? content) + { + if (string.IsNullOrEmpty(content)) return content; + + var bytes = Encoding.UTF8.GetBytes(content); + var encoded = Convert.ToBase64String(bytes); + return encoded; + } + + private string? DecodeRichContent(string? content) + { + if (string.IsNullOrEmpty(content)) return content; + + var decoded = Convert.FromBase64String(content); + var origin = Encoding.UTF8.GetString(decoded); + return origin; + } #endregion } } From 70b3e96c0aeba9c874b56748a51951e47a514581 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Mon, 15 Apr 2024 23:48:22 -0500 Subject: [PATCH 042/201] Click element by position. --- .../Browsing/Models/ElementActionArgs.cs | 10 +++++-- .../Browsing/Models/ElementPosition.cs | 8 +++++ .../BotSharp.Core/BotSharp.Core.csproj | 4 +-- .../BotSharp.Plugin.SqlDriver.csproj | 3 +- .../PlaywrightDriver/PlaywrightInstance.cs | 10 +++---- .../PlaywrightWebDriver.DoAction.cs | 16 +++++++++- .../PlaywrightWebDriver.LaunchBrowser.cs | 29 +++++++++---------- 7 files changed, 51 insertions(+), 29 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementPosition.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs index dcda7436..48eb6a26 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs @@ -7,12 +7,16 @@ public class ElementActionArgs private BroswerActionEnum _action; public BroswerActionEnum Action => _action; - private string _content; - public string Content => _content; + private string? _content; + public string? Content => _content; - public ElementActionArgs(BroswerActionEnum action) + private ElementPosition? _position; + public ElementPosition? Position => _position; + + public ElementActionArgs(BroswerActionEnum action, ElementPosition? position = null) { _action = action; + _position = position; } public ElementActionArgs(BroswerActionEnum action, string content) diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementPosition.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementPosition.cs new file mode 100644 index 00000000..fd95bd66 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementPosition.cs @@ -0,0 +1,8 @@ +namespace BotSharp.Abstraction.Browsing.Models; + +public class ElementPosition +{ + public float X { get; set; } = default!; + + public float Y { get; set; } = default!; +} diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index ece7daa7..4b7bfff6 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -133,8 +133,8 @@ - - + + diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj b/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj index 171ba4cd..383a9f4e 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 @@ -33,7 +33,6 @@ - diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs index 9786ab93..a801400b 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs @@ -40,13 +40,13 @@ public class PlaywrightInstance : IDisposable Channel = "chrome", IgnoreDefaultArgs = new[] { - "--disable-infobars" - }, + "--disable-infobars" + }, Args = new[] { - "--disable-infobars", - // "--start-maximized" - } + "--disable-infobars", + // "--start-maximized" + } }); _contexts[id].Page += async (sender, e) => diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs index 93f714d2..dd4600c9 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs @@ -9,7 +9,21 @@ public partial class PlaywrightWebDriver if (action.Action == BroswerActionEnum.Click) { - await locator.ClickAsync(); + if (action.Position == null) + { + await locator.ClickAsync(); + } + else + { + await locator.ClickAsync(new LocatorClickOptions + { + Position = new Position + { + X = action.Position.X, + Y = action.Position.Y + } + }); + } } else if (action.Action == BroswerActionEnum.InputText) { diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs index e415df8c..b3afd940 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs @@ -23,24 +23,21 @@ public partial class PlaywrightWebDriver } var page = await _instance.NewPage(contextId); - - if (!string.IsNullOrEmpty(url)) + + try { - try + var response = await page.GotoAsync(url, new PageGotoOptions { - var response = await page.GotoAsync(url, new PageGotoOptions - { - Timeout = 15 * 1000 - }); - await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded); - result.IsSuccess = response.Status == 200; - } - catch(Exception ex) - { - result.Message = ex.Message; - result.StackTrace = ex.StackTrace; - _logger.LogError(ex.Message); - } + Timeout = 15 * 1000 + }); + await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded); + result.IsSuccess = response.Status == 200; + } + catch (Exception ex) + { + result.Message = ex.Message; + result.StackTrace = ex.StackTrace; + _logger.LogError(ex.Message); } } From 46fdf455eee52415989fddcce445340ae8f0349e Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 16 Apr 2024 14:31:51 -0500 Subject: [PATCH 043/201] refine save state by args --- .../Services/ConversationStateService.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index e86f4610..ca731824 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,15 @@ public class ConversationStateService : IConversationStateService, IDisposable { foreach (JsonProperty property in root.EnumerateObject()) { - if (!string.IsNullOrEmpty(property.Value.ToString())) + var stateValue = property.Value.ToString(); + if (!string.IsNullOrEmpty(stateValue)) { - SetState(property.Name, property.Value, source: StateSource.Application); + if (bool.TryParse(stateValue, out _)) + { + stateValue = stateValue?.ToLower(); + } + + SetState(property.Name, stateValue, source: StateSource.Application); } } } From 8af7466ecf5223b713a67a2b3f9027bfa4a6b07b Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 16 Apr 2024 14:48:55 -0500 Subject: [PATCH 044/201] use value kind --- .../Conversations/Services/ConversationStateService.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index ca731824..636d622e 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -344,10 +344,12 @@ public class ConversationStateService : IConversationStateService, IDisposable { foreach (JsonProperty property in root.EnumerateObject()) { - var stateValue = property.Value.ToString(); + var propertyValue = property.Value; + var stateValue = propertyValue.ToString(); if (!string.IsNullOrEmpty(stateValue)) { - if (bool.TryParse(stateValue, out _)) + if (propertyValue.ValueKind == JsonValueKind.True || + propertyValue.ValueKind == JsonValueKind.False) { stateValue = stateValue?.ToLower(); } From 59958bcf8bb45f12ada02301f348fb6cb7134df8 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 18 Apr 2024 13:54:43 -0500 Subject: [PATCH 045/201] refine json --- .../BotSharp.Abstraction/Messaging/IRichMessage.cs | 3 +++ .../Messaging/ITemplateMessage.cs | 3 +++ .../Messaging/Models/RichContent/ElementAction.cs | 4 ++++ .../Messaging/Models/RichContent/ElementButton.cs | 6 ++++++ .../Models/RichContent/QuickReplyElement.cs | 4 ++++ .../Models/RichContent/QuickReplyMessage.cs | 3 +++ .../RichContent/Template/ButtonTemplateMessage.cs | 6 ++++++ .../RichContent/Template/CouponTemplateMessage.cs | 11 +++++++++++ .../RichContent/Template/GenericTemplateMessage.cs | 12 ++++++++++++ .../Template/MultiSelectTemplateMessage.cs | 7 +++++++ .../RichContent/Template/ProductTemplateMessage.cs | 4 ++++ .../Messaging/Models/RichContent/TextMessage.cs | 2 ++ .../BotSharp.Core/Plugins/PluginLoader.cs | 1 - 13 files changed, 65 insertions(+), 1 deletion(-) 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.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; From 970bc4d0280e07993f5641560a16515114f52325 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 18 Apr 2024 16:34:18 -0500 Subject: [PATCH 046/201] add secondary content --- .../Conversations/Models/Conversation.cs | 9 ++++++-- .../Conversations/Models/RoleDialogModel.cs | 5 ++++ .../Services/ConversationStorage.cs | 12 +++++++--- .../FileRepository.Conversation.cs | 23 ++++++++++++++----- .../Controllers/ConversationController.cs | 5 +++- .../Conversations/ChatResponseModel.cs | 7 ++++++ .../Models/DialogMongoElement.cs | 10 ++++++-- 7 files changed, 57 insertions(+), 14 deletions(-) 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.Core/Conversations/Services/ConversationStorage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs index ba0d9198..3dcb6ce8 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.RichContent != 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/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.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 }; } } From 72c0f06d931dc9533a4b31cf1cbf4891097f29dc Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 18 Apr 2024 16:40:50 -0500 Subject: [PATCH 047/201] minor change --- .../BotSharp.Core/Conversations/Services/ConversationStorage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs index 3dcb6ce8..4ab447b8 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs @@ -71,7 +71,7 @@ public class ConversationStorage : IConversationStorage } var richContent = dialog.RichContent != null ? JsonSerializer.Serialize(dialog.RichContent, _options.JsonSerializerOptions) : null; - var secondaryRichContent = dialog.RichContent != null ? JsonSerializer.Serialize(dialog.SecondaryRichContent, _options.JsonSerializerOptions) : null; + var secondaryRichContent = dialog.SecondaryRichContent != null ? JsonSerializer.Serialize(dialog.SecondaryRichContent, _options.JsonSerializerOptions) : null; dialogElements.Add(new DialogElement(meta, content, richContent, dialog.SecondaryContent, secondaryRichContent)); } From 27e6bd4d8a58e9ab1f0f911d4ac3d4f660e31ccf Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Fri, 19 Apr 2024 13:40:30 -0500 Subject: [PATCH 048/201] SeleniumWebDriver --- .../BotSharp.Plugin.WebDriver.csproj | 1 + .../SeleniumDriver/SeleniumInstance.cs | 88 +++++++++++++ .../SeleniumWebDriver.DoAction.cs | 43 +++++++ .../SeleniumWebDriver.GetAttributeValue.cs | 18 +++ .../SeleniumWebDriver.GoToPage.cs | 27 ++++ .../SeleniumWebDriver.LaunchBrowser.cs | 33 +++++ .../SeleniumWebDriver.LocateElement.cs | 119 ++++++++++++++++++ .../SeleniumDriver/SeleniumWebDriver.cs | 94 ++++++++++++++ .../BotSharp.Plugin.WebDriver/Using.cs | 1 + .../WebDriverPlugin.cs | 9 +- 10 files changed, 431 insertions(+), 2 deletions(-) create mode 100644 src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumInstance.cs create mode 100644 src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.DoAction.cs create mode 100644 src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.GetAttributeValue.cs create mode 100644 src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.GoToPage.cs create mode 100644 src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.LaunchBrowser.cs create mode 100644 src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.LocateElement.cs create mode 100644 src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.cs diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj b/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj index 7c4d6a76..234e898b 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj +++ b/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj @@ -12,6 +12,7 @@ + diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumInstance.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumInstance.cs new file mode 100644 index 00000000..43586be4 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumInstance.cs @@ -0,0 +1,88 @@ +using OpenQA.Selenium.Chrome; +using System.IO; + +namespace BotSharp.Plugin.WebDriver.Drivers.SeleniumDriver; + +public class SeleniumInstance : IDisposable +{ + Dictionary _contexts = new Dictionary(); + + public Dictionary Contexts => _contexts; + + public INavigation GetPage(string id) + { + InitInstance(id).Wait(); + return _contexts[id].Navigate(); + } + + public string GetPageContent(string id) + { + InitInstance(id).Wait(); + return _contexts[id].PageSource; + } + + public async Task InitInstance(string id) + { + return await InitContext(id); + } + + public async Task InitContext(string id) + { + if (_contexts.ContainsKey(id)) + return _contexts[id]; + + string tempFolderPath = $"{Path.GetTempPath()}\\_selenium\\{id}"; + + var options = new ChromeOptions(); + options.AddArgument("disable-infobars"); + options.AddArgument($"--user-data-dir={tempFolderPath}"); + var selenium = new ChromeDriver(options); + selenium.Manage().Window.Maximize(); + selenium.Navigate().GoToUrl("about:blank"); + _contexts[id] = selenium; + + return _contexts[id]; + } + + public async Task NewPage(string id) + { + await InitContext(id); + var selenium = _contexts[id]; + selenium.Navigate().GoToUrl("about:blank"); + return _contexts[id].Navigate(); + } + + public async Task Wait(string id) + { + if (_contexts.ContainsKey(id)) + { + _contexts[id].Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10); + } + await Task.Delay(100); + } + + public async Task Close(string id) + { + if (_contexts.ContainsKey(id)) + { + _contexts[id].Quit(); + _contexts.Remove(id); + } + } + + public async Task CloseCurrentPage(string id) + { + if (_contexts.ContainsKey(id)) + { + } + } + + public void Dispose() + { + foreach(var context in _contexts) + { + context.Value.Quit(); + } + _contexts.Clear(); + } +} diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.DoAction.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.DoAction.cs new file mode 100644 index 00000000..bb9afd87 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.DoAction.cs @@ -0,0 +1,43 @@ +using OpenQA.Selenium; +using OpenQA.Selenium.Interactions; + +namespace BotSharp.Plugin.WebDriver.Drivers.SeleniumDriver; + +public partial class SeleniumWebDriver +{ + public async Task DoAction(MessageInfo message, ElementActionArgs action, BrowserActionResult result) + { + var driver = await _instance.InitInstance(message.ContextId); + IWebElement element = default; + if (result.Selector.StartsWith("//")) + { + element = driver.FindElement(By.XPath(result.Selector)); + } + else + { + element = driver.FindElement(By.CssSelector(result.Selector)); + } + + + if (action.Action == BroswerActionEnum.Click) + { + if (action.Position == null) + { + element.Click(); + } + else + { + var size = element.Size; + var actions = new Actions(driver); + actions.MoveToElement(element) + .MoveByOffset((int)action.Position.X - size.Width / 2, (int)action.Position.Y - size.Height / 2) + .Click() + .Perform(); + } + } + else if (action.Action == BroswerActionEnum.InputText) + { + element.SendKeys(action.Content); + } + } +} diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.GetAttributeValue.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.GetAttributeValue.cs new file mode 100644 index 00000000..ee5d47e7 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.GetAttributeValue.cs @@ -0,0 +1,18 @@ +namespace BotSharp.Plugin.WebDriver.Drivers.SeleniumDriver; + +public partial class SeleniumWebDriver +{ + public async Task GetAttributeValue(MessageInfo message, ElementLocatingArgs location, BrowserActionResult result) + { + var driver = await _instance.InitInstance(message.ContextId); + var locator = driver.FindElement(By.CssSelector(result.Selector)); + var value = string.Empty; + + if (!string.IsNullOrEmpty(location?.AttributeName)) + { + value = locator.GetAttribute(location.AttributeName); + } + + return value ?? string.Empty; + } +} diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.GoToPage.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.GoToPage.cs new file mode 100644 index 00000000..abe51210 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.GoToPage.cs @@ -0,0 +1,27 @@ +namespace BotSharp.Plugin.WebDriver.Drivers.SeleniumDriver; + +public partial class SeleniumWebDriver +{ + public async Task GoToPage(string contextId, string url, bool openNewTab = false) + { + var result = new BrowserActionResult(); + try + { + var page = openNewTab ? await _instance.NewPage(contextId) : + _instance.GetPage(contextId); + page.GoToUrl(url); + await _instance.Wait(contextId); + + result.Body = _instance.GetPageContent(contextId); + result.IsSuccess = true; + } + catch (Exception ex) + { + result.Message = ex.Message; + result.StackTrace = ex.StackTrace; + _logger.LogError(ex.Message); + } + + return result; + } +} diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.LaunchBrowser.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.LaunchBrowser.cs new file mode 100644 index 00000000..3203b617 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.LaunchBrowser.cs @@ -0,0 +1,33 @@ +namespace BotSharp.Plugin.WebDriver.Drivers.SeleniumDriver; + +public partial class SeleniumWebDriver +{ + public async Task LaunchBrowser(string contextId, string? url, bool openIfNotExist = true) + { + var result = new BrowserActionResult() + { + IsSuccess = true + }; + var context = await _instance.InitInstance(contextId); + + if (!string.IsNullOrEmpty(url)) + { + // Check if the page is already open + var page = await _instance.NewPage(contextId); + + try + { + page.GoToUrl(url); + result.IsSuccess = true; + } + catch (Exception ex) + { + result.Message = ex.Message; + result.StackTrace = ex.StackTrace; + _logger.LogError(ex.Message); + } + } + + return result; + } +} diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.LocateElement.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.LocateElement.cs new file mode 100644 index 00000000..4d96b9a6 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.LocateElement.cs @@ -0,0 +1,119 @@ +using OpenQA.Selenium; +using System.Collections.ObjectModel; + +namespace BotSharp.Plugin.WebDriver.Drivers.SeleniumDriver; + +public partial class SeleniumWebDriver +{ + /// + /// Using attributes or text to locate element and return the selector + /// + /// + /// + /// + public async Task LocateElement(MessageInfo message, ElementLocatingArgs location) + { + var result = new BrowserActionResult(); + var driver = await _instance.InitInstance(message.ContextId); + + IWebElement locator = driver.FindElement(By.TagName("body")); + ReadOnlyCollection elements = default; + string selector = string.Empty; + int count = 0; + + // check if selector is specified + if (location.Selector != null) + { + selector = location.Selector; + elements = driver.FindElements(By.CssSelector(location.Selector)); + count = elements.Count; + } + + // try attribute + if (count == 0 && !string.IsNullOrEmpty(location.AttributeName)) + { + selector = $"[{location.AttributeName}='{location.AttributeValue}']"; + elements = driver.FindElements(By.CssSelector(selector)); + count = elements.Count; + } + + // Retrieve the page raw html and infer the element path + if (!string.IsNullOrEmpty(location.Text)) + { + var regexExpression = location.MatchRule.ToLower() switch + { + "startwith" => $"^{location.Text}", + "endwith" => $"{location.Text}$", + "contains" => $"{location.Text}", + _ => $"^{location.Text}$" + }; + var regex = new Regex(regexExpression, RegexOptions.IgnoreCase); + + selector = $"//*[text() = '{location.Text}']"; + elements = driver.FindElements(By.XPath(selector)); + count = elements.Count; + + // try placeholder + if (count == 0) + { + selector = $"[placeholder='{location.Text}']"; + elements = driver.FindElements(By.CssSelector(selector)); + count = elements.Count; + } + } + + if (location.Index >= 0) + { + locator = elements[location.Index]; + count = 1; + } + + if (count == 0) + { + result.Message = $"Can't locate element by keyword {location.Text}"; + _logger.LogError(result.Message); + } + else if (count == 1) + { + locator = elements[0]; + result.Selector = selector; + var text = locator.Text; + result.Body = text; + result.IsSuccess = true; + } + else if (count > 1) + { + if (location.FailIfMultiple) + { + result.Message = $"Multiple elements are found by {locator}"; + _logger.LogError(result.Message); + + /*foreach (var element in await locator.AllAsync()) + { + var content = await element.InnerHTMLAsync(); + _logger.LogError(content); + }*/ + } + else + { + result.Selector = locator.ToString(); + result.IsSuccess = true; + } + } + + // Hightlight the element + if (result.IsSuccess && location.Highlight) + { + /*var handle = await page.QuerySelectorAsync(result.Selector); + + await page.EvaluateAsync($@" + (element) => {{ + element.style.outline = '2px solid red'; + }}", handle); + + result.IsHighlighted = true;*/ + } + + return result; + } +} diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.cs new file mode 100644 index 00000000..26ce8ea7 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.cs @@ -0,0 +1,94 @@ +namespace BotSharp.Plugin.WebDriver.Drivers.SeleniumDriver; + +public partial class SeleniumWebDriver : IWebBrowser +{ + private readonly IServiceProvider _services; + private readonly SeleniumInstance _instance; + private readonly ILogger _logger; + public SeleniumInstance Instance => _instance; + + public Agent Agent => _agent; + private Agent _agent; + + public SeleniumWebDriver(IServiceProvider services, SeleniumInstance instance, ILogger logger) + { + _services = services; + _instance = instance; + _logger = logger; + } + + public Task ActionOnElement(MessageInfo message, ElementLocatingArgs location, ElementActionArgs action) + { + throw new NotImplementedException(); + } + + public Task ChangeCheckbox(BrowserActionParams actionParams) + { + throw new NotImplementedException(); + } + + public Task ChangeListValue(BrowserActionParams actionParams) + { + throw new NotImplementedException(); + } + + public Task CheckRadioButton(BrowserActionParams actionParams) + { + throw new NotImplementedException(); + } + + public Task ClickButton(BrowserActionParams actionParams) + { + throw new NotImplementedException(); + } + + public Task ClickElement(BrowserActionParams actionParams) + { + throw new NotImplementedException(); + } + + public Task CloseBrowser(string contextId) + { + throw new NotImplementedException(); + } + + public Task CloseCurrentPage(string contextId) + { + throw new NotImplementedException(); + } + + public Task EvaluateScript(string contextId, string script) + { + throw new NotImplementedException(); + } + + public Task ExtractData(BrowserActionParams actionParams) + { + throw new NotImplementedException(); + } + + public Task InputUserPassword(BrowserActionParams actionParams) + { + throw new NotImplementedException(); + } + + public Task InputUserText(BrowserActionParams actionParams) + { + throw new NotImplementedException(); + } + + public Task ScreenshotAsync(string contextId, string path) + { + throw new NotImplementedException(); + } + + public Task ScrollPageAsync(BrowserActionParams actionParams) + { + throw new NotImplementedException(); + } + + public Task SendHttpRequest(string contextId, HttpRequestParams actionParams) + { + throw new NotImplementedException(); + } +} diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Using.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Using.cs index 161e150a..619f0e23 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Using.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Using.cs @@ -9,6 +9,7 @@ global using Microsoft.Playwright; global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.Logging; +global using OpenQA.Selenium; global using BotSharp.Abstraction.Browsing.Enums; global using BotSharp.Abstraction.Conversations; global using BotSharp.Abstraction.Plugins; diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/WebDriverPlugin.cs b/src/Plugins/BotSharp.Plugin.WebDriver/WebDriverPlugin.cs index 2c7a4133..e767104b 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/WebDriverPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/WebDriverPlugin.cs @@ -1,4 +1,5 @@ using BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; +using BotSharp.Plugin.WebDriver.Drivers.SeleniumDriver; using BotSharp.Plugin.WebDriver.Hooks; namespace BotSharp.Plugin.Playwrights; @@ -13,8 +14,12 @@ public class WebDriverPlugin : IBotSharpPlugin public void RegisterDI(IServiceCollection services, IConfiguration config) { - services.AddScoped(); - services.AddSingleton(); + // services.AddScoped(); + // services.AddSingleton(); + + services.AddScoped(); + services.AddSingleton(); + services.AddScoped(); services.AddScoped(); } From f09c22b90ce0da64a17a9fe5eeafd41b09871ec8 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 22 Apr 2024 14:29:21 -0500 Subject: [PATCH 049/201] temp save --- .../Attributes/TranslationAttribute.cs | 7 ++++++ .../Translation/ITranslationService.cs | 11 +++++++++ .../Conversations/ConversationPlugin.cs | 2 ++ .../Translation/TranslationService.cs | 24 +++++++++++++++++++ src/Infrastructure/BotSharp.Core/Using.cs | 1 + 5 files changed, 45 insertions(+) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Translation/Attributes/TranslationAttribute.cs create mode 100644 src/Infrastructure/BotSharp.Abstraction/Translation/ITranslationService.cs create mode 100644 src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Translation/Attributes/TranslationAttribute.cs b/src/Infrastructure/BotSharp.Abstraction/Translation/Attributes/TranslationAttribute.cs new file mode 100644 index 00000000..2df5c539 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Translation/Attributes/TranslationAttribute.cs @@ -0,0 +1,7 @@ +namespace BotSharp.Abstraction.Translation.Attributes; + +public class TranslationAttribute : Attribute +{ + + +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Translation/ITranslationService.cs b/src/Infrastructure/BotSharp.Abstraction/Translation/ITranslationService.cs new file mode 100644 index 00000000..fdd58b1e --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Translation/ITranslationService.cs @@ -0,0 +1,11 @@ + +using BotSharp.Abstraction.Messaging.Models.RichContent; +using BotSharp.Abstraction.Messaging; + +namespace BotSharp.Abstraction.Translation; + +public interface ITranslationService +{ + + T Translate(T data, string language) where T : RichContent; +} diff --git a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs index 5c215884..8fb6d9a5 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs @@ -8,6 +8,7 @@ using BotSharp.Core.Instructs; using BotSharp.Core.Messaging; using BotSharp.Core.Routing.Planning; using BotSharp.Core.Templating; +using BotSharp.Core.Translation; using Microsoft.Extensions.Configuration; namespace BotSharp.Core.Conversations; @@ -35,6 +36,7 @@ public class ConversationPlugin : IBotSharpPlugin services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); // Rich content messaging services.AddScoped(); diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs new file mode 100644 index 00000000..47a7e70a --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -0,0 +1,24 @@ +using BotSharp.Abstraction.Messaging; +using BotSharp.Abstraction.Messaging.Models.RichContent; + +namespace BotSharp.Core.Translation; + +public class TranslationService : ITranslationService +{ + private readonly IServiceProvider _services; + private readonly ILogger _logger; + + public TranslationService(IServiceProvider services, + ILogger logger) + { + _services = services; + _logger = logger; + } + + public T Translate(T data, string language) where T : RichContent + { + + + return data; + } +} diff --git a/src/Infrastructure/BotSharp.Core/Using.cs b/src/Infrastructure/BotSharp.Core/Using.cs index fbb1c8df..769f318f 100644 --- a/src/Infrastructure/BotSharp.Core/Using.cs +++ b/src/Infrastructure/BotSharp.Core/Using.cs @@ -22,6 +22,7 @@ global using BotSharp.Abstraction.Agents.Models; global using BotSharp.Abstraction.Functions.Models; global using BotSharp.Abstraction.Repositories; global using BotSharp.Abstraction.Repositories.Filters; +global using BotSharp.Abstraction.Translation; global using BotSharp.Core.Repository; global using BotSharp.Core.Routing; global using BotSharp.Core.Agents.Services; From 4307d7711d2b0e5e2e63c9ae1727b2777e902c87 Mon Sep 17 00:00:00 2001 From: Haiping Chen <101423@smsassist.com> Date: Mon, 22 Apr 2024 15:46:47 -0500 Subject: [PATCH 050/201] Refactor human intervention needed. --- .../Routing/IRoutingContext.cs | 4 +- .../BotSharp.Core/Agents/AgentPlugin.cs | 7 +++ .../BotSharp.Core/BotSharp.Core.csproj | 12 ++++++ .../Functions/HumanInterventionNeededFn.cs | 29 +++++++++++++ .../Routing/Functions/RouteToAgentFn.cs | 2 +- .../HumanInterventionNeededHandler.cs | 43 ------------------- .../BotSharp.Core/Routing/RoutingContext.cs | 19 +++++++- .../agent.json | 11 +++++ .../functions.json | 20 +++++++++ .../instruction.liquid | 2 + .../templates/planner_prompt.naive.liquid | 2 - 11 files changed, 103 insertions(+), 48 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Core/Routing/Functions/HumanInterventionNeededFn.cs delete mode 100644 src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs create mode 100644 src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/agent.json create mode 100644 src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/functions.json create mode 100644 src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/instruction.liquid 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 ece7daa7..873fa93a 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/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. From d54eb271b3cb06e2438d513146ed045f95682588 Mon Sep 17 00:00:00 2001 From: sylviachency <33144082+sylviachency@users.noreply.github.com> Date: Tue, 23 Apr 2024 00:16:43 -0500 Subject: [PATCH 051/201] Update instruction.liquid --- .../01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/instruction.liquid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 3b1aab42..58795499 100644 --- 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 @@ -1,2 +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 +When other AI customer service agents cannot solve user problems, you know how to call the API to transfer users to human customer service for answers. From b8baf14d63b41da5a1c0b7e08dda5de356dca4a2 Mon Sep 17 00:00:00 2001 From: sylviachency <33144082+sylviachency@users.noreply.github.com> Date: Tue, 23 Apr 2024 00:17:06 -0500 Subject: [PATCH 052/201] Update instruction.liquid --- .../dfd9b46d-d00c-40af-8a75-3fbdc2b89869/instruction.liquid | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/data/agents/dfd9b46d-d00c-40af-8a75-3fbdc2b89869/instruction.liquid b/src/Infrastructure/BotSharp.Core/data/agents/dfd9b46d-d00c-40af-8a75-3fbdc2b89869/instruction.liquid index d0bed0db..bc80b14c 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/dfd9b46d-d00c-40af-8a75-3fbdc2b89869/instruction.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/dfd9b46d-d00c-40af-8a75-3fbdc2b89869/instruction.liquid @@ -1,7 +1,7 @@ -This is a model evaluation program, which interactive with model to complete a certain task based on the background information given to you. +This is a model evaluation program, which interacts with model to complete a certain task based on the background information given to you. {{ task_prompt }} user: Hi! assistant: Hello, How can I help you? -user: \ No newline at end of file +user: From bac6888934ee91f7b9fe572ddc129f00e533cf70 Mon Sep 17 00:00:00 2001 From: sylviachency <33144082+sylviachency@users.noreply.github.com> Date: Tue, 23 Apr 2024 00:19:34 -0500 Subject: [PATCH 053/201] Update agent.json --- .../agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/agent.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index d32e4680..24508fcc 100644 --- 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 @@ -1,11 +1,11 @@ { "id": "01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b", "name": "Human Support", - "description": "Reach out to human customer service representative.", + "description": "Reach out to human customer service.", "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 +} From 9381fa951d5ff6c399a0676583e1e0241cfb3e5a Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 23 Apr 2024 11:25:05 -0500 Subject: [PATCH 054/201] add translate service --- .../Models/RichContent/ElementButton.cs | 1 + .../Attributes/TranslateAttribute.cs | 10 + .../Attributes/TranslationAttribute.cs | 7 - .../Translation/ITranslationService.cs | 7 +- .../BotSharp.Abstraction/Using.cs | 1 + .../Translation/TranslationService.cs | 249 +++++++++++++++++- 6 files changed, 257 insertions(+), 18 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Translation/Attributes/TranslateAttribute.cs delete mode 100644 src/Infrastructure/BotSharp.Abstraction/Translation/Attributes/TranslationAttribute.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs index 47e7cc9a..9932dbda 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs @@ -13,6 +13,7 @@ public class ElementButton [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string Url { get; set; } + [Translate] public string Title { get; set; } = string.Empty; [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] diff --git a/src/Infrastructure/BotSharp.Abstraction/Translation/Attributes/TranslateAttribute.cs b/src/Infrastructure/BotSharp.Abstraction/Translation/Attributes/TranslateAttribute.cs new file mode 100644 index 00000000..1c39c415 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Translation/Attributes/TranslateAttribute.cs @@ -0,0 +1,10 @@ +namespace BotSharp.Abstraction.Translation.Attributes; + +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false)] +public class TranslateAttribute : Attribute +{ + public TranslateAttribute() + { + + } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Translation/Attributes/TranslationAttribute.cs b/src/Infrastructure/BotSharp.Abstraction/Translation/Attributes/TranslationAttribute.cs deleted file mode 100644 index 2df5c539..00000000 --- a/src/Infrastructure/BotSharp.Abstraction/Translation/Attributes/TranslationAttribute.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace BotSharp.Abstraction.Translation.Attributes; - -public class TranslationAttribute : Attribute -{ - - -} diff --git a/src/Infrastructure/BotSharp.Abstraction/Translation/ITranslationService.cs b/src/Infrastructure/BotSharp.Abstraction/Translation/ITranslationService.cs index fdd58b1e..9fe63de0 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Translation/ITranslationService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Translation/ITranslationService.cs @@ -1,11 +1,6 @@ - -using BotSharp.Abstraction.Messaging.Models.RichContent; -using BotSharp.Abstraction.Messaging; - namespace BotSharp.Abstraction.Translation; public interface ITranslationService { - - T Translate(T data, string language) where T : RichContent; + T Translate(T data, string language = "Spanish", bool clone = true) where T : class; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Using.cs b/src/Infrastructure/BotSharp.Abstraction/Using.cs index f9b708dc..91f672b8 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Using.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Using.cs @@ -14,3 +14,4 @@ global using BotSharp.Abstraction.Models; global using BotSharp.Abstraction.Routing.Models; global using BotSharp.Abstraction.Routing.Planning; global using BotSharp.Abstraction.Templating; +global using BotSharp.Abstraction.Translation.Attributes; \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index 47a7e70a..b0dca0eb 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -1,5 +1,7 @@ -using BotSharp.Abstraction.Messaging; -using BotSharp.Abstraction.Messaging.Models.RichContent; +using BotSharp.Abstraction.Options; +using BotSharp.Abstraction.Translation.Attributes; +using Newtonsoft.Json; +using System.Reflection; namespace BotSharp.Core.Translation; @@ -7,18 +9,255 @@ public class TranslationService : ITranslationService { private readonly IServiceProvider _services; private readonly ILogger _logger; + private readonly BotSharpOptions _options; public TranslationService(IServiceProvider services, - ILogger logger) + ILogger logger, + BotSharpOptions options) { _services = services; _logger = logger; + _options = options; } - public T Translate(T data, string language) where T : RichContent + public T Translate(T data, string language = "Spanish", bool clone = true) where T : class { - + var cloned = data; + if (clone) + { + cloned = Clone(data); + } + + var unique = new HashSet(); + Collect(cloned, ref unique); + var map = InnerTranslate(unique, language); + cloned = Assign(cloned, map); + return cloned; + } + + private T Clone(T data) where T : class + { + var str = System.Text.Json.JsonSerializer.Serialize(data, _options.JsonSerializerOptions); + var cloned = System.Text.Json.JsonSerializer.Deserialize(str, _options.JsonSerializerOptions); + return cloned; + } + + /// + /// Collect unique strings in data + /// + /// + /// + /// + private void Collect(T data, ref HashSet res) where T : class + { + if (data == null) return; + + var dataType = data.GetType(); + if (dataType == typeof(string)) + { + res.Add(data.ToString()); + return; + } + + var interfaces = dataType.GetTypeInfo().ImplementedInterfaces; + if (interfaces.Any(x => x.Name == typeof(IDictionary<,>).Name)) + { + return; + } + + var isList = interfaces.Any(x => x.Name == typeof(IEnumerable<>).Name); + if (dataType.IsArray || isList) + { + var elementType = dataType.IsArray ? dataType.GetElementType() : dataType.GetGenericArguments().FirstOrDefault(); + if (elementType == typeof(string)) + { + foreach (var item in (data as IEnumerable)) + { + if (item == null) continue; + res.Add(item); + } + } + else if (elementType != null && (elementType.IsClass || elementType.IsInterface)) + { + foreach (var item in (data as IEnumerable)) + { + if (item == null) continue; + Collect(item, ref res); + } + } + return; + } + + + var props = dataType.GetProperties(); + foreach (var prop in props) + { + var value = prop.GetValue(data, null); + var propType = prop.PropertyType; + var translate = prop.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(TranslateAttribute)); + + if (value == null) continue; + + if (propType == typeof(string)) + { + if (translate != null) + { + Collect(value, ref res); + } + } + else if (propType.IsClass || propType.IsInterface) + { + interfaces = propType.GetTypeInfo().ImplementedInterfaces; + isList = interfaces.Any(x => x.Name == typeof(IEnumerable<>).Name); + if (interfaces.Any(x => x.Name == typeof(IDictionary<,>).Name)) + { + Collect(value, ref res); + } + else if (propType.IsArray || isList) + { + var elementType = propType.IsArray ? propType.GetElementType() : propType.GetGenericArguments().FirstOrDefault(); + if (elementType == typeof(string)) + { + if (translate != null) + { + Collect(value, ref res); + } + } + else if (elementType != null && (elementType.IsClass || elementType.IsInterface)) + { + Collect(value, ref res); + } + } + else + { + Collect(value, ref res); + } + } + } + } + + /// + /// Assign translated values to corresponding attributes + /// + /// + /// + /// + /// + private T Assign(T data, Dictionary map) where T : class + { + if (data == null) return data; + + var dataType = data.GetType(); + if (dataType == typeof(string) && map.TryGetValue(data.ToString(), out var target)) + { + return target as T; + } + + var interfaces = dataType.GetTypeInfo().ImplementedInterfaces; + if (interfaces.Any(x => x.Name == typeof(IDictionary<,>).Name)) + { + return data; + } + + var isList = interfaces.Any(x => x.Name == typeof(IEnumerable<>).Name); + if (dataType.IsArray || isList) + { + var elementType = dataType.IsArray ? dataType.GetElementType() : dataType.GetGenericArguments().FirstOrDefault(); + if (elementType == typeof(string)) + { + var list = new List(); + foreach (var item in (data as IEnumerable)) + { + if (map.TryGetValue(item, out target)) + { + list.Add(target); + } + else + { + list.Add(item?.ToString()); + } + } + + data = dataType.IsArray ? list.ToArray() as T : list as T; + } + else if (elementType != null && (elementType.IsClass || elementType.IsInterface)) + { + foreach (var item in (data as IEnumerable)) + { + if (item == null) continue; + Assign(item, map); + } + } + return data; + } + + + var props = dataType.GetProperties(); + foreach (var prop in props) + { + var value = prop.GetValue(data, null); + var propType = prop.PropertyType; + var translate = prop.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(TranslateAttribute)); + + if (value == null) continue; + + if (propType == typeof(string)) + { + if (translate != null) + { + prop.SetValue(data, Assign(value, map)); + } + } + else if (propType.IsClass || propType.IsInterface) + { + interfaces = propType.GetTypeInfo().ImplementedInterfaces; + isList = interfaces.Any(x => x.Name == typeof(IEnumerable<>).Name); + if (interfaces.Any(x => x.Name == typeof(IDictionary<,>).Name)) + { + Assign(value, map); + } + else if (propType.IsArray || isList) + { + var elementType = propType.IsArray ? propType.GetElementType() : propType.GetGenericArguments().FirstOrDefault(); + if (elementType == typeof(string)) + { + if (translate != null) + { + var targetValue = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(Assign(value, map)), propType); + prop.SetValue(data, targetValue); + } + } + else if (elementType != null && (elementType.IsClass || elementType.IsInterface)) + { + prop.SetValue(data, Assign(value, map)); + } + } + else + { + Assign(value, map); + } + } + } return data; } + + /// + /// Translate + /// + /// + /// + /// + private Dictionary InnerTranslate(HashSet list, string language) + { + var map = new Dictionary(); + if (list == null || !list.Any()) return map; + + foreach (var item in list) + { + map.Add(item, "hello world"); + } + + return map; + } } From ba854c2dec109f3402ac9018aae5011be457fc98 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 23 Apr 2024 11:27:20 -0500 Subject: [PATCH 055/201] minor change --- .../BotSharp.Core/Translation/TranslationService.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index b0dca0eb..7ca79892 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -37,6 +37,8 @@ public class TranslationService : ITranslationService private T Clone(T data) where T : class { + if (data == null) return data; + var str = System.Text.Json.JsonSerializer.Serialize(data, _options.JsonSerializerOptions); var cloned = System.Text.Json.JsonSerializer.Deserialize(str, _options.JsonSerializerOptions); return cloned; From 06974977e7d79e4bcbe592c542a7c91421425ec7 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Tue, 23 Apr 2024 14:02:00 -0500 Subject: [PATCH 056/201] translation prompt. --- .../Infrastructures/Enums/LanguageType.cs | 9 +++ .../Models/RichContent/ElementButton.cs | 1 + .../Template/GenericTemplateMessage.cs | 2 + .../Routing/Models/RoutingArgs.cs | 6 ++ .../Translation/ITranslationService.cs | 2 +- .../BotSharp.Core/BotSharp.Core.csproj | 4 + .../ConversationService.SendMessage.cs | 9 --- .../Handlers/ResponseToUserRoutingHandler.cs | 19 ++++- .../Handlers/RouteToAgentRoutingHandler.cs | 7 +- .../BotSharp.Core/Routing/RoutingService.cs | 81 ++++++++++++++----- .../Translation/TranslationService.cs | 50 ++++++++++-- .../instruction.liquid | 4 +- .../templates/translation_prompt.liquid | 5 ++ 13 files changed, 155 insertions(+), 44 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/LanguageType.cs create mode 100644 src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid diff --git a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/LanguageType.cs b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/LanguageType.cs new file mode 100644 index 00000000..64351e45 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/LanguageType.cs @@ -0,0 +1,9 @@ +namespace BotSharp.Abstraction.Infrastructures.Enums; + +public class LanguageType +{ + public const string UNKNOWN = "Unknown"; + public const string ENGLISH = "English"; + public const string SPANISH = "Spanish"; + public const string CHINESE = "Chinese"; +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs index 9932dbda..91422eae 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs @@ -29,5 +29,6 @@ public class ElementButton [JsonPropertyName("post_action_disclaimer")] [JsonProperty("post_action_disclaimer")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? PostActionDisclaimer { get; set; } } 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 c846e4ea..3d3e5eef 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/GenericTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/GenericTemplateMessage.cs @@ -11,6 +11,7 @@ public class GenericTemplateMessage : IRichMessage, ITemplateMessage [JsonPropertyName("text")] [JsonProperty("text")] + [Translate] public string Text { get; set; } = string.Empty; [JsonPropertyName("template_type")] @@ -36,6 +37,7 @@ public class GenericTemplateMessage : IRichMessage, ITemplateMessage public class GenericElement { + [Translate] public string Title { get; set; } public string Subtitle { get; set; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs index 31311b57..0f8ffd34 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs @@ -50,6 +50,12 @@ public class RoutingArgs [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string UserGoal { get; set; } + [JsonPropertyName("user_message_in_english")] + public string UserMessageInEnglish { get; set; } + + [JsonPropertyName("language")] + public string Language { get; set; } = LanguageType.UNKNOWN; + public override string ToString() { var route = string.IsNullOrEmpty(AgentName) ? "" : $""; diff --git a/src/Infrastructure/BotSharp.Abstraction/Translation/ITranslationService.cs b/src/Infrastructure/BotSharp.Abstraction/Translation/ITranslationService.cs index 9fe63de0..e69e35b1 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Translation/ITranslationService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Translation/ITranslationService.cs @@ -2,5 +2,5 @@ namespace BotSharp.Abstraction.Translation; public interface ITranslationService { - T Translate(T data, string language = "Spanish", bool clone = true) where T : class; + Task Translate(Agent router, string messageId, T data, string language = "Spanish", bool clone = true) where T : class; } diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index 873fa93a..b1c473b4 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -62,6 +62,7 @@ + @@ -118,6 +119,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs index c4dd9302..015ca81a 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -71,15 +71,6 @@ public partial class ConversationService } } - // Persist to storage - if (!message.StopCompletion) - { - _storage.Append(_conversationId, message); - - // Add to thread - dialogs.Add(RoleDialogModel.From(message)); - } - if (!stopCompletion) { // Routing with reasoning diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs index 2b010183..e3244fc7 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs @@ -10,10 +10,21 @@ public class ResponseToUserRoutingHandler : RoutingHandlerBase, IRoutingHandler public List Parameters => new List { - new ParameterPropertyDef("reason", "why response to user directly without go to other agents"), - new ParameterPropertyDef("response", "response content to user in courteous words. If the user wants to end the conversation, you must set conversation_end to true and response politely."), - new ParameterPropertyDef("conversation_end", "whether to end this conversation", type: "boolean"), - new ParameterPropertyDef("task_completed ", "whether the user's task request has been completed.", type: "boolean") + new ParameterPropertyDef("reason", + "why response to user directly without go to other agents"), + new ParameterPropertyDef("response", + "response content to user in courteous words with language English. If the user wants to end the conversation, you must set conversation_end to true and response politely."), + new ParameterPropertyDef("conversation_end", + "whether to end this conversation", + type: "boolean"), + new ParameterPropertyDef("task_completed ", + "whether the user's task request has been completed.", + type: "boolean"), + new ParameterPropertyDef("user_message_in_english", + "Translate user message from non-English to English"), + new ParameterPropertyDef("language", + "Language name of the message user sent, the name may be English, Spanish or Chinese.", + required: true), }; public ResponseToUserRoutingHandler(IServiceProvider services, ILogger logger, RoutingSettings settings) diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs index b5c6a59e..444396f6 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs @@ -28,7 +28,12 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler type: "object"), new ParameterPropertyDef("is_new_task", "whether the user is requesting a new task that is different from the previous topic.", - type: "boolean") + type: "boolean"), + new ParameterPropertyDef("user_message_in_english", + "Translate user message from non-English to English"), + new ParameterPropertyDef("language", + "Language name of the message user sent, the name may be English, Spanish or Chinese.", + required: true), }; public RouteToAgentRoutingHandler(IServiceProvider services, ILogger logger, RoutingSettings settings) diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index 4a17f67a..c2a1eb2d 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -1,7 +1,19 @@ +using BotSharp.Abstraction.Agents.Models; +using BotSharp.Abstraction.Infrastructures.Enums; using BotSharp.Abstraction.Routing.Models; using BotSharp.Abstraction.Routing.Planning; using BotSharp.Abstraction.Routing.Settings; +using BotSharp.Abstraction.Templating; +using BotSharp.Core.Routing.Planning; +using Fluid.Ast; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Diagnostics.Metrics; using System.Drawing; +using System.Runtime.InteropServices; +using System.Security.Cryptography.X509Certificates; +using ThirdParty.Json.LitJson; +using static System.Net.Mime.MediaTypeNames; namespace BotSharp.Core.Routing; @@ -39,15 +51,10 @@ public partial class RoutingService : IRoutingService var handler = handlers.FirstOrDefault(x => x.Name == "route_to_agent"); var conv = _services.GetRequiredService(); - var dialogs = new List(); - if (conv.States.GetState("hide_context", "false") == "true") - { - dialogs.Add(message); - } - else - { - dialogs = conv.GetDialogHistory(); - } + var storage = _services.GetRequiredService(); + storage.Append(conv.ConversationId, message); + + var dialogs = conv.GetDialogHistory(); handler.SetDialogs(dialogs); var inst = new FunctionCallFromLlm @@ -71,11 +78,14 @@ public partial class RoutingService : IRoutingService public async Task InstructLoop(RoleDialogModel message, List dialogs) { - var agentService = _services.GetRequiredService(); - _router = await agentService.LoadAgent(message.CurrentAgentId); - RoleDialogModel response = default; + var agentService = _services.GetRequiredService(); + var convService = _services.GetRequiredService(); + var storage = _services.GetRequiredService(); + + _router = await agentService.LoadAgent(message.CurrentAgentId); + var states = _services.GetRequiredService(); var executor = _services.GetRequiredService(); @@ -83,17 +93,23 @@ public partial class RoutingService : IRoutingService _context.Push(_router.Id); - int loopCount = 0; - while (loopCount < planner.MaxLoopCount && !_context.IsEmpty) + dialogs.Add(message); + + // Get first instruction + _router.TemplateDict["conversation"] = await GetConversationContent(dialogs); + var inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs); + + // Handle multi-language for input + + if (inst.Language != LanguageType.UNKNOWN && inst.Language != LanguageType.ENGLISH) { - loopCount++; - - var conversation = await GetConversationContent(dialogs); - _router.TemplateDict["conversation"] = conversation; - - // Get instruction from Planner - var inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs); + message.Content = inst.UserMessageInEnglish; + } + storage.Append(convService.ConversationId, message); + int loopCount = 1; + while (true) + { await HookEmitter.Emit(_services, async hook => await hook.OnRoutingInstructionReceived(inst, message) ); @@ -121,6 +137,29 @@ public partial class RoutingService : IRoutingService } await planner.AgentExecuted(_router, inst, response, dialogs); + + if (loopCount >= planner.MaxLoopCount || _context.IsEmpty) + { + break; + } + + // Get next instruction from Planner + _router.TemplateDict["conversation"] = await GetConversationContent(dialogs); + inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs); + loopCount++; + } + + // Handle multi-language for output + if (inst.Language != LanguageType.UNKNOWN && inst.Language != LanguageType.ENGLISH) + { + if (response.RichContent != null) + { + var translator = _services.GetRequiredService(); + response.RichContent.Message = await translator.Translate(_router, + message.MessageId, + response.RichContent.Message, + language: inst.Language); + } } return response; diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index 7ca79892..a461dc1b 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -1,4 +1,6 @@ +using Amazon.Runtime.Internal.Transform; using BotSharp.Abstraction.Options; +using BotSharp.Abstraction.Templating; using BotSharp.Abstraction.Translation.Attributes; using Newtonsoft.Json; using System.Reflection; @@ -10,6 +12,8 @@ public class TranslationService : ITranslationService private readonly IServiceProvider _services; private readonly ILogger _logger; private readonly BotSharpOptions _options; + private Agent _router; + private string _messageId; public TranslationService(IServiceProvider services, ILogger logger, @@ -20,8 +24,10 @@ public class TranslationService : ITranslationService _options = options; } - public T Translate(T data, string language = "Spanish", bool clone = true) where T : class + public async Task Translate(Agent router, string messageId, T data, string language = "Spanish", bool clone = true) where T : class { + _router = router; + _messageId = messageId; var cloned = data; if (clone) { @@ -30,7 +36,7 @@ public class TranslationService : ITranslationService var unique = new HashSet(); Collect(cloned, ref unique); - var map = InnerTranslate(unique, language); + var map = await InnerTranslate(unique, language); cloned = Assign(cloned, map); return cloned; } @@ -250,14 +256,44 @@ public class TranslationService : ITranslationService /// /// /// - private Dictionary InnerTranslate(HashSet list, string language) + private async Task> InnerTranslate(HashSet list, string language) { - var map = new Dictionary(); - if (list == null || !list.Any()) return map; + // chat completion + var completion = CompletionProvider.GetChatCompletion(_services, + provider: _router?.LlmConfig?.Provider, + model: _router?.LlmConfig?.Model); - foreach (var item in list) + var texts = list.ToArray(); + var translator = new Agent { - map.Add(item, "hello world"); + Id = Guid.Empty.ToString(), + Name = "Translator", + TemplateDict = new Dictionary + { + { "text_list", JsonConvert.SerializeObject(texts) }, + { "language", language } + } + }; + + var template = _router.Templates.First(x => x.Name == "translation_prompt").Content; + var render = _services.GetRequiredService(); + var prompt = render.Render(template, translator.TemplateDict); + + var translationDialogs = new List + { + new RoleDialogModel(AgentRole.User, prompt) + { + FunctionName = "translation_prompt", + MessageId = _messageId + } + }; + var translationResponse = await completion.GetChatCompletions(translator, translationDialogs); + var translatedTexts = translationResponse.Content.JsonArrayContent(); + var map = new Dictionary(); + + for (var i = 0; i < list.Count; i++) + { + map.Add(texts[i], translatedTexts[i]); } return map; diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid index 93960782..85cd1ff9 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid @@ -1,4 +1,6 @@ -You're {{router.name}} ({{router.description}}). Follow these steps to handle user request: +You're {{router.name}} ({{router.description}}). +You can understand messages sent by users in different languages. +Follow these steps to handle user request: 1. Read the [CONVERSATION] content. 2. Select a appropriate function from [FUNCTIONS]. 3. Determine which agent is suitable to handle this conversation. diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid new file mode 100644 index 00000000..b044bddd --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid @@ -0,0 +1,5 @@ +{{ text_list }} + +===== +Translate the sentences in the list into {{ language }}, output the translated text list. + From 36e569ea28e1a9e1c853b41778b2f3f5b8a4c712 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Tue, 23 Apr 2024 14:27:14 -0500 Subject: [PATCH 057/201] Update language prompt. --- .../Routing/Models/RoutingArgs.cs | 2 +- .../Handlers/ResponseToUserRoutingHandler.cs | 2 +- .../Handlers/RouteToAgentRoutingHandler.cs | 2 +- .../BotSharp.Core/Routing/RoutingService.cs | 15 +++++++++++---- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs index 0f8ffd34..159b5555 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs @@ -54,7 +54,7 @@ public class RoutingArgs public string UserMessageInEnglish { get; set; } [JsonPropertyName("language")] - public string Language { get; set; } = LanguageType.UNKNOWN; + public string Language { get; set; } = LanguageType.ENGLISH; public override string ToString() { diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs index e3244fc7..0544ee66 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs @@ -23,7 +23,7 @@ public class ResponseToUserRoutingHandler : RoutingHandlerBase, IRoutingHandler new ParameterPropertyDef("user_message_in_english", "Translate user message from non-English to English"), new ParameterPropertyDef("language", - "Language name of the message user sent, the name may be English, Spanish or Chinese.", + "Language name detected based on user last message, the name may be English, Spanish or Chinese.", required: true), }; diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs index 444396f6..75ed6785 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs @@ -32,7 +32,7 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler new ParameterPropertyDef("user_message_in_english", "Translate user message from non-English to English"), new ParameterPropertyDef("language", - "Language name of the message user sent, the name may be English, Spanish or Chinese.", + "Language name detected based on user last message, the name may be English, Spanish or Chinese.", required: true), }; diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index c2a1eb2d..a1f61fb6 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -152,12 +152,19 @@ public partial class RoutingService : IRoutingService // Handle multi-language for output if (inst.Language != LanguageType.UNKNOWN && inst.Language != LanguageType.ENGLISH) { + var translator = _services.GetRequiredService(); if (response.RichContent != null) { - var translator = _services.GetRequiredService(); - response.RichContent.Message = await translator.Translate(_router, - message.MessageId, - response.RichContent.Message, + response.RichContent.Message = await translator.Translate(_router, + message.MessageId, + response.RichContent.Message, + language: inst.Language); + } + else + { + response.Content = await translator.Translate(_router, + message.MessageId, + response.Content, language: inst.Language); } } From 2add4a6ae8e8961397a8b26b216da13cccdc13b0 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Tue, 23 Apr 2024 14:48:30 -0500 Subject: [PATCH 058/201] Fix localization return issue. --- .../Messaging/Models/RichContent/ElementButton.cs | 2 ++ .../Conversations/Services/ConversationService.SendMessage.cs | 4 ++-- src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs index 91422eae..30206aa7 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs @@ -17,6 +17,7 @@ public class ElementButton public string Title { get; set; } = string.Empty; [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [Translate] public string Payload { get; set; } [JsonPropertyName("is_primary")] @@ -30,5 +31,6 @@ public class ElementButton [JsonPropertyName("post_action_disclaimer")] [JsonProperty("post_action_disclaimer")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [Translate] public string? PostActionDisclaimer { 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 015ca81a..0306ccaf 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -130,7 +130,7 @@ public partial class ConversationService response.RichContent is RichContent template && string.IsNullOrEmpty(template.Message.Text)) { - template.Message.Text = response.Content; + template.Message.Text = response.SecondaryContent ?? response.Content; } // Only read content from RichContent for UI rendering. When richContent is null, create a basic text message for richContent. @@ -138,7 +138,7 @@ public partial class ConversationService response.RichContent = response.RichContent ?? new RichContent { Recipient = new Recipient { Id = state.GetConversationId() }, - Message = new TextMessage(response.Content) + Message = new TextMessage(response.SecondaryContent ?? response.Content) }; // Patch return function name diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index a1f61fb6..36fb21be 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -162,7 +162,7 @@ public partial class RoutingService : IRoutingService } else { - response.Content = await translator.Translate(_router, + response.SecondaryContent = await translator.Translate(_router, message.MessageId, response.Content, language: inst.Language); From e93fa66bcc25e86f1b4aef1dae083b3ee04ebfc2 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Tue, 23 Apr 2024 15:30:38 -0500 Subject: [PATCH 059/201] update language. --- .../Routing/Handlers/ResponseToUserRoutingHandler.cs | 2 +- .../Routing/Handlers/RouteToAgentRoutingHandler.cs | 2 +- .../templates/translation_prompt.liquid | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs index 0544ee66..81befde7 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs @@ -23,7 +23,7 @@ public class ResponseToUserRoutingHandler : RoutingHandlerBase, IRoutingHandler new ParameterPropertyDef("user_message_in_english", "Translate user message from non-English to English"), new ParameterPropertyDef("language", - "Language name detected based on user last message, the name may be English, Spanish or Chinese.", + "Language detected based on the latest message that USER sent, could be English, Spanish or Chinese.", required: true), }; diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs index 75ed6785..a852c5f9 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs @@ -32,7 +32,7 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler new ParameterPropertyDef("user_message_in_english", "Translate user message from non-English to English"), new ParameterPropertyDef("language", - "Language name detected based on user last message, the name may be English, Spanish or Chinese.", + "Language detected based on the latest message that USER sent, could be English, Spanish or Chinese.", required: true), }; diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid index b044bddd..729f0f72 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid @@ -1,5 +1,4 @@ {{ text_list }} ===== -Translate the sentences in the list into {{ language }}, output the translated text list. - +Translate the sentences in the list into {{ language }}, only output the translated text in string list [""]. \ No newline at end of file From cc1cdd497e1692c05f2a162b756149cfee2b9b63 Mon Sep 17 00:00:00 2001 From: "C. Oceania" Date: Tue, 23 Apr 2024 16:04:48 -0500 Subject: [PATCH 060/201] Ignore Payload translation. --- .../Messaging/Models/RichContent/ElementButton.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs index 30206aa7..b4baf793 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs @@ -17,7 +17,6 @@ public class ElementButton public string Title { get; set; } = string.Empty; [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [Translate] public string Payload { get; set; } [JsonPropertyName("is_primary")] From e3da6db9d7ca98a8d762a4bef1306d1ec13fa75a Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 23 Apr 2024 16:27:09 -0500 Subject: [PATCH 061/201] refine multilanguage log --- .../Controllers/ConversationController.cs | 13 ++++----- .../Conversations/ChatResponseModel.cs | 7 ----- .../Hooks/ChatHubConversationHook.cs | 6 ++--- .../Hooks/StreamingLogHook.cs | 27 ++++++++++++++----- 4 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index 04e61773..2f00911d 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -83,8 +83,7 @@ public class ConversationController : ControllerBase ConversationId = conversationId, MessageId = message.MessageId, CreatedAt = message.CreatedAt, - Text = message.Content, - SecondaryText = message.SecondaryContent, + Text = !string.IsNullOrEmpty(message.SecondaryContent) ? message.SecondaryContent : message.Content, Data = message.Data, Sender = UserViewModel.FromUser(user) }); @@ -97,8 +96,7 @@ public class ConversationController : ControllerBase ConversationId = conversationId, MessageId = message.MessageId, CreatedAt = message.CreatedAt, - Text = message.Content, - SecondaryText = message.SecondaryContent, + Text = !string.IsNullOrEmpty(message.SecondaryContent) ? message.SecondaryContent : message.Content, Function = message.FunctionName, Data = message.Data, Sender = new UserViewModel @@ -106,8 +104,7 @@ public class ConversationController : ControllerBase FirstName = agent.Name, Role = message.Role, }, - RichContent = message.RichContent, - SecondaryRichContent = message.SecondaryRichContent + RichContent = message.SecondaryRichContent ?? message.RichContent }); } } @@ -180,9 +177,9 @@ public class ConversationController : ControllerBase replyMessage: input.Postback, async msg => { - response.Text = msg.Content; + response.Text = !string.IsNullOrEmpty(msg.SecondaryContent) ? msg.SecondaryContent : msg.Content; response.Function = msg.FunctionName; - response.RichContent = msg.RichContent; + response.RichContent = msg.SecondaryRichContent ?? msg.RichContent; response.Instruction = msg.Instruction; response.Data = msg.Data; }, diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ChatResponseModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ChatResponseModel.cs index 86903800..cf71b8f9 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ChatResponseModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ChatResponseModel.cs @@ -21,9 +21,6 @@ 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 /// @@ -31,10 +28,6 @@ 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.ChatHub/Hooks/ChatHubConversationHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs index 0ebff14c..3b14494b 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs @@ -45,7 +45,7 @@ public class ChatHubConversationHook : ConversationHookBase { ConversationId = conv.ConversationId, MessageId = message.MessageId, - Text = message.Content, + Text = !string.IsNullOrEmpty(message.SecondaryContent) ? message.SecondaryContent : message.Content, Sender = UserViewModel.FromUser(sender) }); @@ -71,9 +71,9 @@ public class ChatHubConversationHook : ConversationHookBase { ConversationId = conv.ConversationId, MessageId = message.MessageId, - Text = message.Content, + Text = !string.IsNullOrEmpty(message.SecondaryContent) ? message.SecondaryContent : message.Content, Function = message.FunctionName, - RichContent = message.RichContent, + RichContent = message.SecondaryRichContent ?? message.RichContent, Data = message.Data, Sender = new UserViewModel() { diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs index e1b5fd0f..26e894c7 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs @@ -7,6 +7,8 @@ using BotSharp.Abstraction.Options; using BotSharp.Abstraction.Repositories; using BotSharp.Abstraction.Routing; using Microsoft.AspNetCore.SignalR; +using System.Text.Encodings.Web; +using System.Text.Unicode; namespace BotSharp.Plugin.ChatHub.Hooks; @@ -45,7 +47,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR public override async Task OnMessageReceived(RoleDialogModel message) { var conversationId = _state.GetConversationId(); - var log = $"{message.Content}"; + var log = $"{GetMessageContent(message)}"; var input = new ContentLogInputModel(conversationId, message) { @@ -59,7 +61,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR public override async Task OnPostbackMessageReceived(RoleDialogModel message, PostbackMessageModel replyMsg) { var conversationId = _state.GetConversationId(); - var log = $"{message.Content}"; + var log = $"{GetMessageContent(message)}"; var replyContent = JsonSerializer.Serialize(replyMsg, _options.JsonSerializerOptions); log += $"\r\n```json\r\n{replyContent}\r\n```"; @@ -183,10 +185,18 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR if (message.Role == AgentRole.Assistant) { var agent = await _agentService.LoadAgent(message.CurrentAgentId); - var log = $"{message.Content}"; - if (message.RichContent != null) + var log = $"{GetMessageContent(message)}"; + if (message.RichContent != null || message.SecondaryRichContent != null) { - var richContent = JsonSerializer.Serialize(message.RichContent, _options.JsonSerializerOptions); + var jsonOptions = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + AllowTrailingCommas = true, + WriteIndented = true, + Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) + }; + var richContent = JsonSerializer.Serialize(message.SecondaryRichContent ?? message.RichContent, jsonOptions); log += $"\r\n```json\r\n{richContent}\r\n```"; } @@ -204,7 +214,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR public override async Task OnTaskCompleted(RoleDialogModel message) { var conversationId = _state.GetConversationId(); - var log = $"{message.Content}"; + var log = $"{GetMessageContent(message)}"; var agent = await _agentService.LoadAgent(message.CurrentAgentId); var input = new ContentLogInputModel(conversationId, message) @@ -479,4 +489,9 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR return JsonSerializer.Serialize(model, _options.JsonSerializerOptions); } + + private string GetMessageContent(RoleDialogModel message) + { + return !string.IsNullOrEmpty(message.SecondaryContent) ? message.SecondaryContent : message.Content; + } } From ab3c6089a69251fa2260318f9165d141ee4cd01f Mon Sep 17 00:00:00 2001 From: Joanna Ren <101223@smsassist.com> Date: Tue, 23 Apr 2024 17:13:38 -0500 Subject: [PATCH 062/201] update language detection --- .../Routing/Handlers/ResponseToUserRoutingHandler.cs | 2 +- .../Routing/Handlers/RouteToAgentRoutingHandler.cs | 2 +- .../BotSharp.Core/Translation/TranslationService.cs | 12 ++++++++++-- .../templates/planner_prompt.naive.liquid | 1 + 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs index 81befde7..cca6615e 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs @@ -23,7 +23,7 @@ public class ResponseToUserRoutingHandler : RoutingHandlerBase, IRoutingHandler new ParameterPropertyDef("user_message_in_english", "Translate user message from non-English to English"), new ParameterPropertyDef("language", - "Language detected based on the latest message that USER sent, could be English, Spanish or Chinese.", + "User prefered language, considering the whole conversation. Language could be English, Spanish or Chinese.", required: true), }; diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs index a852c5f9..6aa12998 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs @@ -32,7 +32,7 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler new ParameterPropertyDef("user_message_in_english", "Translate user message from non-English to English"), new ParameterPropertyDef("language", - "Language detected based on the latest message that USER sent, could be English, Spanish or Chinese.", + "User prefered language, considering the whole conversation. Language could be English, Spanish or Chinese.", required: true), }; diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index a461dc1b..9d441e77 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -1,6 +1,7 @@ using Amazon.Runtime.Internal.Transform; using BotSharp.Abstraction.Options; using BotSharp.Abstraction.Templating; +using BotSharp.Abstraction.Translation; using BotSharp.Abstraction.Translation.Attributes; using Newtonsoft.Json; using System.Reflection; @@ -28,16 +29,23 @@ public class TranslationService : ITranslationService { _router = router; _messageId = messageId; + + var unique = new HashSet(); + Collect(data, ref unique); + if (unique.Count == 0) + { + return data; + } + var cloned = data; if (clone) { cloned = Clone(data); } - var unique = new HashSet(); - Collect(cloned, ref unique); var map = await InnerTranslate(unique, language); cloned = Assign(cloned, map); + return cloned; } 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 f7388be6..5177a002 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,3 +10,4 @@ Expected user goal agent is {{ expected_user_goal_agent }}. {%- else -%} User goal agent is inferred based on user initial request. {%- endif %} +Detect language based on the overall content in [CONVERSATION] and only include user message. \ No newline at end of file From 349cc7e44b423747ebb3748a90cee270302c9abd Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Tue, 23 Apr 2024 23:47:38 -0500 Subject: [PATCH 063/201] minor change --- .../BotSharp.Core/Translation/TranslationService.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index 9d441e77..815b2dc9 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -1,9 +1,8 @@ -using Amazon.Runtime.Internal.Transform; using BotSharp.Abstraction.Options; using BotSharp.Abstraction.Templating; -using BotSharp.Abstraction.Translation; using BotSharp.Abstraction.Translation.Attributes; using Newtonsoft.Json; +using System.Collections; using System.Reflection; namespace BotSharp.Core.Translation; @@ -81,7 +80,7 @@ public class TranslationService : ITranslationService return; } - var isList = interfaces.Any(x => x.Name == typeof(IEnumerable<>).Name); + var isList = interfaces.Any(x => x.Name == typeof(IEnumerable).Name); if (dataType.IsArray || isList) { var elementType = dataType.IsArray ? dataType.GetElementType() : dataType.GetGenericArguments().FirstOrDefault(); @@ -124,7 +123,7 @@ public class TranslationService : ITranslationService else if (propType.IsClass || propType.IsInterface) { interfaces = propType.GetTypeInfo().ImplementedInterfaces; - isList = interfaces.Any(x => x.Name == typeof(IEnumerable<>).Name); + isList = interfaces.Any(x => x.Name == typeof(IEnumerable).Name); if (interfaces.Any(x => x.Name == typeof(IDictionary<,>).Name)) { Collect(value, ref res); @@ -175,7 +174,7 @@ public class TranslationService : ITranslationService return data; } - var isList = interfaces.Any(x => x.Name == typeof(IEnumerable<>).Name); + var isList = interfaces.Any(x => x.Name == typeof(IEnumerable).Name); if (dataType.IsArray || isList) { var elementType = dataType.IsArray ? dataType.GetElementType() : dataType.GetGenericArguments().FirstOrDefault(); @@ -227,7 +226,7 @@ public class TranslationService : ITranslationService else if (propType.IsClass || propType.IsInterface) { interfaces = propType.GetTypeInfo().ImplementedInterfaces; - isList = interfaces.Any(x => x.Name == typeof(IEnumerable<>).Name); + isList = interfaces.Any(x => x.Name == typeof(IEnumerable).Name); if (interfaces.Any(x => x.Name == typeof(IDictionary<,>).Name)) { Assign(value, map); From ce7b4954754c564922e65cd400a0c3dd0a9a074e Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Wed, 24 Apr 2024 16:45:18 -0500 Subject: [PATCH 064/201] Return reason from HasMissingRequiredField. --- .../Routing/IRoutingService.cs | 2 +- .../Routing/Models/RoutingArgs.cs | 6 +++--- .../Routing/Functions/RouteToAgentFn.cs | 4 ++-- .../Handlers/ResponseToUserRoutingHandler.cs | 10 +++++----- .../Handlers/RouteToAgentRoutingHandler.cs | 16 ++++++++-------- .../BotSharp.Core/Routing/RoutingContext.cs | 2 +- .../RoutingService.HasMissingRequiredField.cs | 14 +++++++++----- .../Routing/RoutingService.InvokeFunction.cs | 11 ----------- .../Providers/ChatCompletionProvider.cs | 3 ++- 9 files changed, 31 insertions(+), 37 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs index da84ffa3..1577d103 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs @@ -44,5 +44,5 @@ public interface IRoutingService Task GetConversationContent(List dialogs, int maxDialogCount = 50); - bool HasMissingRequiredField(RoleDialogModel message, out string agentId); + (bool, string) HasMissingRequiredField(RoleDialogModel message, out string agentId); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs index 159b5555..105c0c57 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs @@ -50,12 +50,12 @@ public class RoutingArgs [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string UserGoal { get; set; } - [JsonPropertyName("user_message_in_english")] - public string UserMessageInEnglish { get; set; } - [JsonPropertyName("language")] public string Language { get; set; } = LanguageType.ENGLISH; + [JsonPropertyName("lastest_message_translated_to_english")] + public string UserMessageInEnglish { get; set; } + public override string ToString() { var route = string.IsNullOrEmpty(AgentName) ? "" : $""; diff --git a/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs b/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs index 2450a6fc..639c907b 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs @@ -83,11 +83,11 @@ public partial class RouteToAgentFn : IFunctionCallback } var routing = _services.GetRequiredService(); - var missingfield = routing.HasMissingRequiredField(message, out var agentId); + var (missingfield, reason) = routing.HasMissingRequiredField(message, out var agentId); if (missingfield && message.CurrentAgentId != agentId) { // Stack redirection agent - _context.Push(agentId, reason: $"REDIRECTION {message.Content}"); + _context.Push(agentId, reason: $"REDIRECTION {reason}"); } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs index cca6615e..92a3b137 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs @@ -11,20 +11,20 @@ public class ResponseToUserRoutingHandler : RoutingHandlerBase, IRoutingHandler public List Parameters => new List { new ParameterPropertyDef("reason", - "why response to user directly without go to other agents"), + "why response to user directly without go to other agents."), new ParameterPropertyDef("response", "response content to user in courteous words with language English. If the user wants to end the conversation, you must set conversation_end to true and response politely."), new ParameterPropertyDef("conversation_end", - "whether to end this conversation", + "whether to end this conversation.", type: "boolean"), new ParameterPropertyDef("task_completed ", "whether the user's task request has been completed.", type: "boolean"), - new ParameterPropertyDef("user_message_in_english", - "Translate user message from non-English to English"), new ParameterPropertyDef("language", - "User prefered language, considering the whole conversation. Language could be English, Spanish or Chinese.", + "User preferred language, considering the whole conversation. Language could be English, Spanish or Chinese.", required: true), + new ParameterPropertyDef("lastest_message_translated_to_english", + "Translate user lastest message in [CONVERSATION] to English"), }; public ResponseToUserRoutingHandler(IServiceProvider services, ILogger logger, RoutingSettings settings) diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs index 6aa12998..5c08d679 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs @@ -12,28 +12,28 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler public List Parameters => new List { new ParameterPropertyDef("next_action_reason", - "the reason why route to this virtual agent", + "the reason why route to this virtual agent.", required: true), new ParameterPropertyDef("next_action_agent", - "agent for next action based on user latest response, if user is replying last agent's question, you must route to this agent", + "agent for next action based on user latest response, if user is replying last agent's question, you must route to this agent.", required: true), + new ParameterPropertyDef("args", + "useful parameters of next action agent, format: { }", + type: "object"), new ParameterPropertyDef("user_goal_description", "user goal based on user initial task.", required: true), new ParameterPropertyDef("user_goal_agent", "agent who can acheive user initial task, must align with user_goal_description.", required: true), - new ParameterPropertyDef("args", - "useful parameters of next action agent, format: { }", - type: "object"), new ParameterPropertyDef("is_new_task", "whether the user is requesting a new task that is different from the previous topic.", type: "boolean"), - new ParameterPropertyDef("user_message_in_english", - "Translate user message from non-English to English"), new ParameterPropertyDef("language", - "User prefered language, considering the whole conversation. Language could be English, Spanish or Chinese.", + "User preferred language, considering the whole conversation. Language could be English, Spanish or Chinese.", required: true), + new ParameterPropertyDef("lastest_message_translated_to_english", + "Translate lastest user message in [CONVERSATION] to English"), }; public RouteToAgentRoutingHandler(IServiceProvider services, ILogger logger, RoutingSettings settings) diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs index 4d8eba43..6b126a37 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs @@ -128,7 +128,7 @@ public class RoutingContext : IRoutingContext }; var routing = _services.GetRequiredService(); - var missingfield = routing.HasMissingRequiredField(message, out agentId); + var (missingfield, _) = routing.HasMissingRequiredField(message, out agentId); if (missingfield) { if (currentAgentId != agentId) diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.HasMissingRequiredField.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.HasMissingRequiredField.cs index 2a6b676c..60f3f331 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.HasMissingRequiredField.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.HasMissingRequiredField.cs @@ -10,8 +10,9 @@ public partial class RoutingService /// If the target agent needs some required fields but the /// /// - public bool HasMissingRequiredField(RoleDialogModel message, out string agentId) + public (bool, string) HasMissingRequiredField(RoleDialogModel message, out string agentId) { + var reason = string.Empty; var args = JsonSerializer.Deserialize(message.FunctionArgs); var routing = _services.GetRequiredService(); @@ -20,7 +21,7 @@ public partial class RoutingService if (routingRules == null || !routingRules.Any()) { agentId = message.CurrentAgentId; - return false; + return (false, reason); } agentId = routingRules.First().AgentId; @@ -68,9 +69,13 @@ public partial class RoutingService if (missingFields.Any()) { + var logger = _services.GetRequiredService>(); + // Add field to args message.FunctionArgs = AppendPropertyToArgs(message.FunctionArgs, "missing_fields", missingFields); - message.Content = $"missing some information: {string.Join(", ", missingFields)}"; + reason = $"missing some information: {string.Join(", ", missingFields)}"; + // message.Content = reason; + logger.LogWarning(reason); // Handle redirect var routingRule = routingRules.FirstOrDefault(x => missingFields.Contains(x.Field)); @@ -82,7 +87,6 @@ public partial class RoutingService // Add redirected agent message.FunctionArgs = AppendPropertyToArgs(message.FunctionArgs, "redirect_to", record.Name); agentId = routingRule.RedirectTo; - var logger = _services.GetRequiredService>(); #if DEBUG Console.WriteLine($"*** Routing redirect to {record.Name.ToUpper()} ***", Color.Yellow); #else @@ -96,7 +100,7 @@ public partial class RoutingService } } - return missingFields.Any(); + return (missingFields.Any(), reason); } private string AppendPropertyToArgs(string args, string key, string value) diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs index 86839b67..42465533 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs @@ -4,9 +4,6 @@ namespace BotSharp.Core.Routing; public partial class RoutingService { - private List _functionCallStack = new List(); - public List FunctionCallStack => _functionCallStack; - public async Task InvokeFunction(string name, RoleDialogModel message) { var function = _services.GetServices().FirstOrDefault(x => x.Name == name); @@ -38,14 +35,6 @@ public partial class RoutingService { result = await function.Execute(clonedMessage); - _functionCallStack.Add(new FunctionCallingResponse - { - Role = AgentRole.Function, - FunctionName = clonedMessage.FunctionName, - Args = JsonDocument.Parse(clonedMessage.FunctionArgs ?? "{}"), - Content = clonedMessage.Content - }); - // After functions have been executed foreach (var hook in hooks) { diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs index 0e5cc0ab..820dcb84 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs @@ -300,6 +300,7 @@ public class ChatCompletionProvider : IChatCompletion })); prompt += $"{verbose}\r\n"; + prompt += "\r\n[CONVERSATION]\r\n"; verbose = string.Join("\r\n", chatCompletionsOptions.Messages .Where(x => x.Role != AgentRole.System).Select(x => { @@ -311,7 +312,7 @@ public class ChatCompletionProvider : IChatCompletion else if (x.Role == ChatRole.User) { var m = x as ChatRequestUserMessage; - return !string.IsNullOrEmpty(m.Name) ? + return !string.IsNullOrEmpty(m.Name) && m.Name != "route_to_agent" ? $"{m.Name}: {m.Content}" : $"{m.Role}: {m.Content}"; } From 42b0e37ff3c71ae8d183aeb5d39444c85d83266f Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Wed, 24 Apr 2024 23:08:46 -0500 Subject: [PATCH 065/201] EntityFrameworkCore 8.2.1 --- src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index 0cd3468f..b4dfa0b5 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -145,7 +145,7 @@ - + From e18845e4aeebcc803803212327df81b5bb72c8bd Mon Sep 17 00:00:00 2001 From: YouWeiDH Date: Thu, 25 Apr 2024 15:43:40 +0800 Subject: [PATCH 066/201] hdong:hard code page size to 10 for Qtoss project currently. --- .../BotSharp.Abstraction/Utilities/Pagination.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs index d11752f6..df0c247b 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs @@ -3,7 +3,7 @@ namespace BotSharp.Abstraction.Utilities; public class Pagination { private int _page; - private int _size; + private int _size => 10; public int Page { @@ -20,10 +20,10 @@ public class Pagination return _size; } - set - { - _size = value; - } + //set + //{ + // _size = value; + //} } public int Offset From bb0884e4cc4e71139904e9d58ef61333079e372f Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 25 Apr 2024 10:58:28 -0500 Subject: [PATCH 067/201] refine type check --- .../Translation/TranslationService.cs | 86 ++++++++++++------- 1 file changed, 55 insertions(+), 31 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index 815b2dc9..3bb6cdbd 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -31,7 +31,7 @@ public class TranslationService : ITranslationService var unique = new HashSet(); Collect(data, ref unique); - if (unique.Count == 0) + if (unique.IsNullOrEmpty()) { return data; } @@ -68,23 +68,21 @@ public class TranslationService : ITranslationService if (data == null) return; var dataType = data.GetType(); - if (dataType == typeof(string)) + if (IsStringType(dataType)) { res.Add(data.ToString()); return; } - var interfaces = dataType.GetTypeInfo().ImplementedInterfaces; - if (interfaces.Any(x => x.Name == typeof(IDictionary<,>).Name)) + if (IsDictionaryType(dataType)) { return; } - var isList = interfaces.Any(x => x.Name == typeof(IEnumerable).Name); - if (dataType.IsArray || isList) + if (IsListType(dataType)) { var elementType = dataType.IsArray ? dataType.GetElementType() : dataType.GetGenericArguments().FirstOrDefault(); - if (elementType == typeof(string)) + if (IsStringType(elementType)) { foreach (var item in (data as IEnumerable)) { @@ -92,7 +90,7 @@ public class TranslationService : ITranslationService res.Add(item); } } - else if (elementType != null && (elementType.IsClass || elementType.IsInterface)) + else if (IsTrackToNextLevel(elementType)) { foreach (var item in (data as IEnumerable)) { @@ -113,32 +111,30 @@ public class TranslationService : ITranslationService if (value == null) continue; - if (propType == typeof(string)) + if (IsStringType(propType)) { if (translate != null) { Collect(value, ref res); } } - else if (propType.IsClass || propType.IsInterface) + else if (IsTrackToNextLevel(propType)) { - interfaces = propType.GetTypeInfo().ImplementedInterfaces; - isList = interfaces.Any(x => x.Name == typeof(IEnumerable).Name); - if (interfaces.Any(x => x.Name == typeof(IDictionary<,>).Name)) + if (IsDictionaryType(propType)) { Collect(value, ref res); } - else if (propType.IsArray || isList) + else if (IsListType(propType)) { var elementType = propType.IsArray ? propType.GetElementType() : propType.GetGenericArguments().FirstOrDefault(); - if (elementType == typeof(string)) + if (IsStringType(elementType)) { if (translate != null) { Collect(value, ref res); } } - else if (elementType != null && (elementType.IsClass || elementType.IsInterface)) + else if (IsTrackToNextLevel(elementType)) { Collect(value, ref res); } @@ -163,22 +159,20 @@ public class TranslationService : ITranslationService if (data == null) return data; var dataType = data.GetType(); - if (dataType == typeof(string) && map.TryGetValue(data.ToString(), out var target)) + if (IsStringType(dataType) && map.TryGetValue(data.ToString(), out var target)) { return target as T; } - var interfaces = dataType.GetTypeInfo().ImplementedInterfaces; - if (interfaces.Any(x => x.Name == typeof(IDictionary<,>).Name)) + if (IsDictionaryType(dataType)) { return data; } - var isList = interfaces.Any(x => x.Name == typeof(IEnumerable).Name); - if (dataType.IsArray || isList) + if (IsListType(dataType)) { var elementType = dataType.IsArray ? dataType.GetElementType() : dataType.GetGenericArguments().FirstOrDefault(); - if (elementType == typeof(string)) + if (IsStringType(elementType)) { var list = new List(); foreach (var item in (data as IEnumerable)) @@ -195,7 +189,7 @@ public class TranslationService : ITranslationService data = dataType.IsArray ? list.ToArray() as T : list as T; } - else if (elementType != null && (elementType.IsClass || elementType.IsInterface)) + else if (IsTrackToNextLevel(elementType)) { foreach (var item in (data as IEnumerable)) { @@ -216,25 +210,23 @@ public class TranslationService : ITranslationService if (value == null) continue; - if (propType == typeof(string)) + if (IsStringType(propType)) { if (translate != null) { prop.SetValue(data, Assign(value, map)); } } - else if (propType.IsClass || propType.IsInterface) + else if (IsTrackToNextLevel(propType)) { - interfaces = propType.GetTypeInfo().ImplementedInterfaces; - isList = interfaces.Any(x => x.Name == typeof(IEnumerable).Name); - if (interfaces.Any(x => x.Name == typeof(IDictionary<,>).Name)) + if (IsDictionaryType(propType)) { Assign(value, map); } - else if (propType.IsArray || isList) + else if (IsListType(propType)) { var elementType = propType.IsArray ? propType.GetElementType() : propType.GetGenericArguments().FirstOrDefault(); - if (elementType == typeof(string)) + if (IsStringType(elementType)) { if (translate != null) { @@ -242,7 +234,7 @@ public class TranslationService : ITranslationService prop.SetValue(data, targetValue); } } - else if (elementType != null && (elementType.IsClass || elementType.IsInterface)) + else if (IsTrackToNextLevel(elementType)) { prop.SetValue(data, Assign(value, map)); } @@ -305,4 +297,36 @@ public class TranslationService : ITranslationService return map; } + + #region Type methods + private static bool IsStringType(Type? type) + { + if (type == null) return false; + + return type == typeof(string); + } + + private static bool IsListType(Type? type) + { + if (type == null) return false; + + var interfaces = type.GetTypeInfo().ImplementedInterfaces; + return type.IsArray || interfaces.Any(x => x.Name == typeof(IEnumerable).Name); + } + + private static bool IsDictionaryType(Type? type) + { + if (type == null) return false; + + var underlyingInterfaces = type.UnderlyingSystemType.GetTypeInfo().ImplementedInterfaces; + return underlyingInterfaces.Any(x => x.Name == typeof(IDictionary).Name); + } + + private static bool IsTrackToNextLevel(Type? type) + { + if (type == null) return false; + + return type.IsClass || type.IsInterface || type.IsAbstract; + } + #endregion } From b43dfbac01341043e1364697711bee2275212d0a Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Thu, 25 Apr 2024 11:27:51 -0500 Subject: [PATCH 068/201] Fix language detection. --- .../Template/ButtonTemplateMessage.cs | 1 + .../Routing/Models/RoutingArgs.cs | 13 ++--- .../Handlers/ResponseToUserRoutingHandler.cs | 4 +- .../Handlers/RouteToAgentRoutingHandler.cs | 4 +- .../RoutingService.GetConversationContent.cs | 2 +- .../BotSharp.Core/Routing/RoutingService.cs | 26 ++++++--- .../Translation/TranslationService.cs | 53 +++++++++++-------- .../templates/translation_prompt.liquid | 2 +- .../Providers/ChatCompletionProvider.cs | 1 - 9 files changed, 60 insertions(+), 46 deletions(-) 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 ce8a5a93..647ebc98 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ButtonTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ButtonTemplateMessage.cs @@ -14,6 +14,7 @@ public class ButtonTemplateMessage : IRichMessage, ITemplateMessage [JsonPropertyName("text")] [JsonProperty("text")] + [Translate] public string Text { get; set; } = string.Empty; [JsonPropertyName("template_type")] diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs index 105c0c57..f68b7231 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs @@ -3,7 +3,7 @@ namespace BotSharp.Abstraction.Routing.Models; public class RoutingArgs { [JsonPropertyName("function")] - public string Function { get; set; } + public string Function { get; set; } = string.Empty; /// /// The reason why you select this function or agent @@ -30,32 +30,29 @@ public class RoutingArgs /// [JsonPropertyName("response")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string Response { get; set; } + public string Response { get; set; } = string.Empty; /// /// Agent for next action based on user latest response /// [JsonPropertyName("next_action_agent")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string AgentName { get; set; } + public string AgentName { get; set; } = string.Empty; /// /// Agent who can achieve user original goal /// [JsonPropertyName("user_goal_agent")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string OriginalAgent { get; set; } + public string OriginalAgent { get; set; } = string.Empty; [JsonPropertyName("user_goal_description")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string UserGoal { get; set; } + public string UserGoal { get; set; } = string.Empty; [JsonPropertyName("language")] public string Language { get; set; } = LanguageType.ENGLISH; - [JsonPropertyName("lastest_message_translated_to_english")] - public string UserMessageInEnglish { get; set; } - public override string ToString() { var route = string.IsNullOrEmpty(AgentName) ? "" : $""; diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs index 92a3b137..00c8c616 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs @@ -22,9 +22,7 @@ public class ResponseToUserRoutingHandler : RoutingHandlerBase, IRoutingHandler type: "boolean"), new ParameterPropertyDef("language", "User preferred language, considering the whole conversation. Language could be English, Spanish or Chinese.", - required: true), - new ParameterPropertyDef("lastest_message_translated_to_english", - "Translate user lastest message in [CONVERSATION] to English"), + required: true) }; public ResponseToUserRoutingHandler(IServiceProvider services, ILogger logger, RoutingSettings settings) diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs index 5c08d679..59561461 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs @@ -31,9 +31,7 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler type: "boolean"), new ParameterPropertyDef("language", "User preferred language, considering the whole conversation. Language could be English, Spanish or Chinese.", - required: true), - new ParameterPropertyDef("lastest_message_translated_to_english", - "Translate lastest user message in [CONVERSATION] to English"), + required: true) }; public RouteToAgentRoutingHandler(IServiceProvider services, ILogger logger, RoutingSettings settings) diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetConversationContent.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetConversationContent.cs index bd883314..5825a2c0 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetConversationContent.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetConversationContent.cs @@ -16,7 +16,7 @@ public partial class RoutingService role = agent.Name; } - conversation += $"{role}: {dialog.Content}\r\n"; + conversation += $"{role}: {dialog.SecondaryContent ?? dialog.Content}\r\n"; } return conversation; diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index 36fb21be..c139ceab 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -100,11 +100,17 @@ public partial class RoutingService : IRoutingService var inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs); // Handle multi-language for input + var translator = _services.GetRequiredService(); - if (inst.Language != LanguageType.UNKNOWN && inst.Language != LanguageType.ENGLISH) + var language = states.GetState("language", inst.Language); + if (language != LanguageType.UNKNOWN && language != LanguageType.ENGLISH) { - message.Content = inst.UserMessageInEnglish; + message.SecondaryContent = message.Content; + message.Content = await translator.Translate(_router, message.MessageId, message.Content, + language: LanguageType.ENGLISH, + clone: false); } + storage.Append(convService.ConversationId, message); int loopCount = 1; @@ -150,22 +156,26 @@ public partial class RoutingService : IRoutingService } // Handle multi-language for output - if (inst.Language != LanguageType.UNKNOWN && inst.Language != LanguageType.ENGLISH) + if (language != LanguageType.UNKNOWN && language != LanguageType.ENGLISH) { - var translator = _services.GetRequiredService(); if (response.RichContent != null) { - response.RichContent.Message = await translator.Translate(_router, + if (string.IsNullOrEmpty(response.RichContent.Message.Text)) + { + response.RichContent.Message.Text = response.Content; + } + + response.SecondaryRichContent = await translator.Translate(_router, message.MessageId, - response.RichContent.Message, - language: inst.Language); + response.RichContent, + language: language); } else { response.SecondaryContent = await translator.Translate(_router, message.MessageId, response.Content, - language: inst.Language); + language: language); } } diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index 815b2dc9..530cb1a0 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -1,8 +1,10 @@ +using BotSharp.Abstraction.MLTasks; using BotSharp.Abstraction.Options; using BotSharp.Abstraction.Templating; using BotSharp.Abstraction.Translation.Attributes; using Newtonsoft.Json; using System.Collections; +using System.Collections.Generic; using System.Reflection; namespace BotSharp.Core.Translation; @@ -14,6 +16,7 @@ public class TranslationService : ITranslationService private readonly BotSharpOptions _options; private Agent _router; private string _messageId; + private IChatCompletion _completion; public TranslationService(IServiceProvider services, ILogger logger, @@ -42,8 +45,31 @@ public class TranslationService : ITranslationService cloned = Clone(data); } - var map = await InnerTranslate(unique, language); - cloned = Assign(cloned, map); + // chat completion + _completion = CompletionProvider.GetChatCompletion(_services, + provider: _router?.LlmConfig?.Provider, + model: _router?.LlmConfig?.Model); + var template = _router.Templates.First(x => x.Name == "translation_prompt").Content; + + var texts = unique.ToArray(); + var translatedStringList = await InnerTranslate(JsonConvert.SerializeObject(texts), language, template); + + try + { + var translatedTexts = translatedStringList.JsonArrayContent(); + var map = new Dictionary(); + + for (var i = 0; i < texts.Length; i++) + { + map.Add(texts[i], translatedTexts[i]); + } + + cloned = Assign(cloned, map); + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } return cloned; } @@ -263,26 +289,19 @@ public class TranslationService : ITranslationService /// /// /// - private async Task> InnerTranslate(HashSet list, string language) + private async Task InnerTranslate(string texts, string language, string template) { - // chat completion - var completion = CompletionProvider.GetChatCompletion(_services, - provider: _router?.LlmConfig?.Provider, - model: _router?.LlmConfig?.Model); - - var texts = list.ToArray(); var translator = new Agent { Id = Guid.Empty.ToString(), Name = "Translator", TemplateDict = new Dictionary { - { "text_list", JsonConvert.SerializeObject(texts) }, + { "text_list", texts }, { "language", language } } }; - var template = _router.Templates.First(x => x.Name == "translation_prompt").Content; var render = _services.GetRequiredService(); var prompt = render.Render(template, translator.TemplateDict); @@ -294,15 +313,7 @@ public class TranslationService : ITranslationService MessageId = _messageId } }; - var translationResponse = await completion.GetChatCompletions(translator, translationDialogs); - var translatedTexts = translationResponse.Content.JsonArrayContent(); - var map = new Dictionary(); - - for (var i = 0; i < list.Count; i++) - { - map.Add(texts[i], translatedTexts[i]); - } - - return map; + var response = await _completion.GetChatCompletions(translator, translationDialogs); + return response.Content; } } diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid index 729f0f72..a3052187 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid @@ -1,4 +1,4 @@ {{ text_list }} ===== -Translate the sentences in the list into {{ language }}, only output the translated text in string list [""]. \ No newline at end of file +Translate the above sentences in the list into {{ language }}, output the translated text in JSON array [""]. \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs index 820dcb84..a9fe828a 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs @@ -300,7 +300,6 @@ public class ChatCompletionProvider : IChatCompletion })); prompt += $"{verbose}\r\n"; - prompt += "\r\n[CONVERSATION]\r\n"; verbose = string.Join("\r\n", chatCompletionsOptions.Messages .Where(x => x.Role != AgentRole.System).Select(x => { From d109468de148b92879ac77691ced49fc28750281 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Thu, 25 Apr 2024 17:03:34 -0500 Subject: [PATCH 069/201] Increase MaxInputLengthPerRequest to 512. --- .../Conversations/Settings/RateLimitSetting.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Settings/RateLimitSetting.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Settings/RateLimitSetting.cs index 0c9a7436..566f7bef 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Settings/RateLimitSetting.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Settings/RateLimitSetting.cs @@ -3,6 +3,6 @@ namespace BotSharp.Abstraction.Conversations.Settings; public class RateLimitSetting { public int MaxConversationPerDay { get; set; } = 100; - public int MaxInputLengthPerRequest { get; set; } = 256; + public int MaxInputLengthPerRequest { get; set; } = 512; public int MinTimeSecondsBetweenMessages { get; set; } = 2; } From 5604273d1dd57f50d40ad858104b7a23f7b93978 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Mon, 29 Apr 2024 09:13:17 -0500 Subject: [PATCH 070/201] WebBrowsingSettings --- .../Browsing/Settings/WebBrowsingSettings.cs | 6 +++ .../BotSharp.Plugin.WebDriver.csproj | 12 ++++-- .../SeleniumDriver/SeleniumInstance.cs | 8 +++- .../SeleniumWebDriver.EvaluateScript.cs | 13 ++++++ .../SeleniumWebDriver.HttpRequest.cs | 43 +++++++++++++++++++ .../SeleniumDriver/SeleniumWebDriver.cs | 10 ----- .../WebDriverPlugin.cs | 24 +++++++++-- src/WebStarter/appsettings.json | 6 ++- 8 files changed, 103 insertions(+), 19 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs create mode 100644 src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.EvaluateScript.cs create mode 100644 src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.HttpRequest.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs new file mode 100644 index 00000000..25149fe0 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs @@ -0,0 +1,6 @@ +namespace BotSharp.Abstraction.Browsing.Settings; + +public class WebBrowsingSettings +{ + public string Driver { get; set; } = "Playwright"; +} diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj b/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj index 234e898b..b03f585d 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj +++ b/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 @@ -11,8 +11,14 @@ - - + + + + + + + + diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumInstance.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumInstance.cs index 43586be4..5e1f5eaf 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumInstance.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumInstance.cs @@ -33,11 +33,15 @@ public class SeleniumInstance : IDisposable string tempFolderPath = $"{Path.GetTempPath()}\\_selenium\\{id}"; - var options = new ChromeOptions(); + var options = new ChromeOptions + { + // DebuggerAddress = "localhost:9222", + // BrowserVersion = "123.0.6312.46" + }; options.AddArgument("disable-infobars"); options.AddArgument($"--user-data-dir={tempFolderPath}"); var selenium = new ChromeDriver(options); - selenium.Manage().Window.Maximize(); + // selenium.Manage().Window.Maximize(); selenium.Navigate().GoToUrl("about:blank"); _contexts[id] = selenium; diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.EvaluateScript.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.EvaluateScript.cs new file mode 100644 index 00000000..9a59cb81 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.EvaluateScript.cs @@ -0,0 +1,13 @@ +namespace BotSharp.Plugin.WebDriver.Drivers.SeleniumDriver; + +public partial class SeleniumWebDriver +{ + public async Task EvaluateScript(string contextId, string script) + { + await _instance.Wait(contextId); + var driver = await _instance.InitContext(contextId); + var jsExecutor = (IJavaScriptExecutor)driver; + var result = jsExecutor.ExecuteAsyncScript(script); + return (T)result; + } +} diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.HttpRequest.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.HttpRequest.cs new file mode 100644 index 00000000..b88a5d96 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.HttpRequest.cs @@ -0,0 +1,43 @@ +using System.Net.Http; + +namespace BotSharp.Plugin.WebDriver.Drivers.SeleniumDriver; + +public partial class SeleniumWebDriver +{ + public async Task SendHttpRequest(string contextId, HttpRequestParams args) + { + var result = new BrowserActionResult(); + + var body = args.Method == HttpMethod.Post ? + $"body: '{args.Payload}'" : string.Empty; + + // Send AJAX request + string script = $@" + (async () => {{ + const response = await fetch('{args.Url}', {{ + method: '{args.Method}', + headers: {{ + 'Content-Type': 'application/json' + }}, + {body} + }}); + return await response.json(); + }})(); + "; + + try + { + var response = await EvaluateScript(contextId, script); + result.IsSuccess = true; + result.Body = JsonSerializer.Serialize(response); + } + catch (Exception ex) + { + result.Message = ex.Message; + result.StackTrace = ex.StackTrace; + _logger.LogError(ex.Message); + } + + return result; + } +} diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.cs index 26ce8ea7..40eb70d7 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.cs @@ -57,11 +57,6 @@ public partial class SeleniumWebDriver : IWebBrowser throw new NotImplementedException(); } - public Task EvaluateScript(string contextId, string script) - { - throw new NotImplementedException(); - } - public Task ExtractData(BrowserActionParams actionParams) { throw new NotImplementedException(); @@ -86,9 +81,4 @@ public partial class SeleniumWebDriver : IWebBrowser { throw new NotImplementedException(); } - - public Task SendHttpRequest(string contextId, HttpRequestParams actionParams) - { - throw new NotImplementedException(); - } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/WebDriverPlugin.cs b/src/Plugins/BotSharp.Plugin.WebDriver/WebDriverPlugin.cs index e767104b..12dd0090 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/WebDriverPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/WebDriverPlugin.cs @@ -1,3 +1,5 @@ +using BotSharp.Abstraction.Browsing.Settings; +using BotSharp.Abstraction.Settings; using BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; using BotSharp.Plugin.WebDriver.Drivers.SeleniumDriver; using BotSharp.Plugin.WebDriver.Hooks; @@ -14,12 +16,28 @@ public class WebDriverPlugin : IBotSharpPlugin public void RegisterDI(IServiceCollection services, IConfiguration config) { - // services.AddScoped(); - // services.AddSingleton(); + var settings = new WebBrowsingSettings(); + config.Bind("WebBrowsing", settings); - services.AddScoped(); + services.AddScoped(provider => + { + var settingService = provider.GetRequiredService(); + return settings; + }); + + services.AddScoped(); + services.AddSingleton(); + + services.AddScoped(); services.AddSingleton(); + services.AddScoped(provider => settings.Driver switch + { + "Playwright" => provider.GetRequiredService(), + "Selenium" => provider.GetRequiredService(), + _ => provider.GetRequiredService(), + }); + services.AddScoped(); services.AddScoped(); } diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index 83b870b7..1abd6ce3 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -133,6 +133,10 @@ } }, + "WebBrowsing": { + "Driver": "Playwright" + }, + "Statistics": { "DataDir": "stats" }, @@ -224,7 +228,7 @@ "ApiKey": "", "Map": { "Endpoint": "https://maps.googleapis.com/maps/api/geocode/json", - "Components": "country=US|country=CA", + "Components": "country=US|country=CA" }, "Youtube": { "Endpoint": "https://www.googleapis.com/youtube/v3/search", From 1cfa6a86189f6cefa6e1c1b2bba1beec5b48c146 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Tue, 30 Apr 2024 07:50:01 -0500 Subject: [PATCH 071/201] DistributedLocker --- .../Browsing/IWebBrowser.cs | 2 +- .../Browsing/Models/ElementActionArgs.cs | 25 ++++++---- .../Repositories/BotSharpDatabaseSettings.cs | 1 + .../BotSharp.Core/BotSharp.Core.csproj | 1 + .../BotSharp.Core/BotSharpCoreExtensions.cs | 2 + .../Infrastructures/DistributedLocker.cs | 50 +++++++++++++++++++ .../Repository/DataContextHelper.cs | 1 - .../PlaywrightWebDriver.GetAttributeValue.cs | 10 ++-- .../SeleniumWebDriver.GetAttributeValue.cs | 10 ++-- 9 files changed, 84 insertions(+), 18 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Core/Infrastructures/DistributedLocker.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs index 850833cd..80dd6925 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs @@ -25,5 +25,5 @@ public interface IWebBrowser Task CloseBrowser(string contextId); Task CloseCurrentPage(string contextId); Task SendHttpRequest(string contextId, HttpRequestParams actionParams); - Task GetAttributeValue(MessageInfo message, ElementLocatingArgs location, BrowserActionResult result); + Task GetAttributeValue(MessageInfo message, ElementLocatingArgs location); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs index 48eb6a26..8b644b31 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs @@ -4,24 +4,29 @@ namespace BotSharp.Abstraction.Browsing.Models; public class ElementActionArgs { - private BroswerActionEnum _action; - public BroswerActionEnum Action => _action; + public BroswerActionEnum Action { get; set; } - private string? _content; - public string? Content => _content; + public string? Content { get; set; } - private ElementPosition? _position; - public ElementPosition? Position => _position; + public ElementPosition? Position { get; set; } + + /// + /// Required for deserialization + /// + public ElementActionArgs() + { + + } public ElementActionArgs(BroswerActionEnum action, ElementPosition? position = null) { - _action = action; - _position = position; + Action = action; + Position = position; } public ElementActionArgs(BroswerActionEnum action, string content) { - _action = action; - _content = content; + Action = action; + Content = content; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/BotSharpDatabaseSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/BotSharpDatabaseSettings.cs index 458aa83e..ff077296 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/BotSharpDatabaseSettings.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/BotSharpDatabaseSettings.cs @@ -7,6 +7,7 @@ public class BotSharpDatabaseSettings : DatabaseBasicSettings public string BotSharpMongoDb { get; set; } public string TablePrefix { get; set; } public DbConnectionSetting BotSharp { get; set; } + public string Redis { get; set; } } public class DatabaseBasicSettings diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index 1650b719..6029a5c7 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -152,6 +152,7 @@ + diff --git a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs index 2b56281c..51f3eb1d 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs +++ b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs @@ -14,9 +14,11 @@ public static class BotSharpCoreExtensions { services.AddScoped(); services.AddScoped(); + services.AddSingleton(); RegisterPlugins(services, config); ConfigureBotSharpOptions(services, configOptions); + return services; } diff --git a/src/Infrastructure/BotSharp.Core/Infrastructures/DistributedLocker.cs b/src/Infrastructure/BotSharp.Core/Infrastructures/DistributedLocker.cs new file mode 100644 index 00000000..82b655c7 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Infrastructures/DistributedLocker.cs @@ -0,0 +1,50 @@ +using RedLockNet; +using RedLockNet.SERedis; +using RedLockNet.SERedis.Configuration; +using StackExchange.Redis; + +namespace BotSharp.Core.Infrastructures; + +public class DistributedLocker +{ + private readonly BotSharpDatabaseSettings _settings; + private readonly RedLockFactory _lockFactory; + + public DistributedLocker(/*BotSharpDatabaseSettings settings*/) + { + // _settings = settings; + + var multiplexers = new List(); + foreach (var x in "".Split(';')) + { + var option = new ConfigurationOptions + { + AbortOnConnectFail = false, + EndPoints = { x } + }; + var _connMuliplexer = ConnectionMultiplexer.Connect(option); + multiplexers.Add(_connMuliplexer); + } + + _lockFactory = RedLockFactory.Create(multiplexers); + } + + public async Task Lock(string resource, Func action) + { + var expiry = TimeSpan.FromSeconds(60); + var wait = TimeSpan.FromSeconds(30); + var retry = TimeSpan.FromSeconds(3); + + await using (var redLock = await _lockFactory.CreateLockAsync(resource, expiry, wait, retry)) + { + if (redLock.IsAcquired) + { + await action(); + } + else + { + Console.WriteLine($"Acquire locak failed due to {resource} after {wait}s timeout."); + } + } + } +} diff --git a/src/Infrastructure/BotSharp.Core/Repository/DataContextHelper.cs b/src/Infrastructure/BotSharp.Core/Repository/DataContextHelper.cs index 28b447c5..edcbced8 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/DataContextHelper.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/DataContextHelper.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Repositories; using Microsoft.Data.SqlClient; using MySqlConnector; using System.Data.Common; diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GetAttributeValue.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GetAttributeValue.cs index cf172d31..035bcf3b 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GetAttributeValue.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GetAttributeValue.cs @@ -2,10 +2,10 @@ namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver { - public async Task GetAttributeValue(MessageInfo message, ElementLocatingArgs location, BrowserActionResult result) + public async Task GetAttributeValue(MessageInfo message, ElementLocatingArgs location) { var page = _instance.GetPage(message.ContextId); - ILocator locator = page.Locator(result.Selector); + ILocator locator = page.Locator(location.Selector); var value = string.Empty; if (!string.IsNullOrEmpty(location?.AttributeName)) @@ -13,6 +13,10 @@ public partial class PlaywrightWebDriver value = await locator.GetAttributeAsync(location.AttributeName); } - return value ?? string.Empty; + return new BrowserActionResult + { + IsSuccess = true, + Body = value ?? string.Empty + }; } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.GetAttributeValue.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.GetAttributeValue.cs index ee5d47e7..0402b2db 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.GetAttributeValue.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.GetAttributeValue.cs @@ -2,10 +2,10 @@ namespace BotSharp.Plugin.WebDriver.Drivers.SeleniumDriver; public partial class SeleniumWebDriver { - public async Task GetAttributeValue(MessageInfo message, ElementLocatingArgs location, BrowserActionResult result) + public async Task GetAttributeValue(MessageInfo message, ElementLocatingArgs location) { var driver = await _instance.InitInstance(message.ContextId); - var locator = driver.FindElement(By.CssSelector(result.Selector)); + var locator = driver.FindElement(By.CssSelector(location.Selector)); var value = string.Empty; if (!string.IsNullOrEmpty(location?.AttributeName)) @@ -13,6 +13,10 @@ public partial class SeleniumWebDriver value = locator.GetAttribute(location.AttributeName); } - return value ?? string.Empty; + return new BrowserActionResult + { + IsSuccess = true, + Body = value ?? string.Empty + }; } } From 0ae2c4ba991b0c4b8cd2ba87c159bcf1260e66c4 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Tue, 30 Apr 2024 16:25:58 -0500 Subject: [PATCH 072/201] Server-sent Events. --- .../Conversations/Models/RoleDialogModel.cs | 5 + .../Functions/IFunctionCallback.cs | 6 + .../Routing/IRoutingHandler.cs | 2 +- .../Routing/IRoutingService.cs | 8 +- .../Routing/Planning/IExecutor.cs | 3 +- .../ConversationService.SendMessage.cs | 2 +- .../Routing/Functions/FallbackToRouterFn.cs | 2 +- .../Handlers/ResponseToUserRoutingHandler.cs | 2 +- .../RetrieveDataFromAgentRoutingHandler.cs | 4 +- .../Handlers/RouteToAgentRoutingHandler.cs | 4 +- .../Routing/Planning/InstructExecutor.cs | 7 +- .../Routing/RoutingService.InvokeAgent.cs | 11 +- .../Routing/RoutingService.InvokeFunction.cs | 8 +- .../BotSharp.Core/Routing/RoutingService.cs | 19 +--- .../Controllers/ConversationController.cs | 103 +++++++++++++++++- 15 files changed, 142 insertions(+), 44 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs index f6e1ecfe..bb84b55d 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs @@ -28,6 +28,11 @@ public class RoleDialogModel : ITrackableMessage public string? SecondaryContent { get; set; } + /// + /// Indicator message used to provide UI feedback for function execution + /// + public string? Indication { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string CurrentAgentId { get; set; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Functions/IFunctionCallback.cs b/src/Infrastructure/BotSharp.Abstraction/Functions/IFunctionCallback.cs index 3973cb9d..ffc25f33 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Functions/IFunctionCallback.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Functions/IFunctionCallback.cs @@ -3,5 +3,11 @@ namespace BotSharp.Abstraction.Functions; public interface IFunctionCallback { string Name { get; } + + /// + /// Indicator message used to provide UI feedback for function execution + /// + string Indication => string.Empty; + Task Execute(RoleDialogModel message); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingHandler.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingHandler.cs index 95cddf2e..e82ea706 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingHandler.cs @@ -16,5 +16,5 @@ public interface IRoutingHandler void SetDialogs(List dialogs); - Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message); + Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message, Func onFunctionExecuting); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs index 1577d103..5a12c0ed 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs @@ -1,5 +1,3 @@ -using BotSharp.Abstraction.Routing.Models; - namespace BotSharp.Abstraction.Routing; public interface IRoutingService @@ -30,9 +28,9 @@ public interface IRoutingService List GetHandlers(Agent router); void ResetRecursiveCounter(); - Task InvokeAgent(string agentId, List dialogs); - Task InvokeFunction(string name, RoleDialogModel message); - Task InstructLoop(RoleDialogModel message, List dialogs); + Task InvokeAgent(string agentId, List dialogs, Func onFunctionExecuting); + Task InvokeFunction(string name, RoleDialogModel messages, Func? onFunctionExecuting = null); + Task InstructLoop(RoleDialogModel message, List dialogs, Func onFunctionExecuting); /// /// Talk to a specific Agent directly, bypassing the Router diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Planning/IExecutor.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Planning/IExecutor.cs index c8bffe6c..14c02034 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Planning/IExecutor.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Planning/IExecutor.cs @@ -7,5 +7,6 @@ public interface IExecutor Task Execute(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message, - List dialogs); + List dialogs, + Func onFunctionExecuting); } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs index 0306ccaf..213d5a67 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -77,7 +77,7 @@ public partial class ConversationService var settings = _services.GetRequiredService(); response = agent.Type == AgentType.Routing ? - await routing.InstructLoop(message, dialogs) : + await routing.InstructLoop(message, dialogs, onFunctionExecuting) : await routing.InstructDirect(agent, message); routing.ResetRecursiveCounter(); diff --git a/src/Infrastructure/BotSharp.Core/Routing/Functions/FallbackToRouterFn.cs b/src/Infrastructure/BotSharp.Core/Routing/Functions/FallbackToRouterFn.cs index 59685719..16deb178 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Functions/FallbackToRouterFn.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Functions/FallbackToRouterFn.cs @@ -34,7 +34,7 @@ public class FallbackToRouterFn : IFunctionCallback routing.Context.Replace(targetAgent.Id); message.CurrentAgentId = targetAgent.Id; - var response = await routing.InstructLoop(message, dialogs); + var response = await routing.InstructLoop(message, dialogs, null); message.Content = response.Content; message.StopCompletion = true; diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs index 00c8c616..81030223 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs @@ -30,7 +30,7 @@ public class ResponseToUserRoutingHandler : RoutingHandlerBase, IRoutingHandler { } - public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message) + public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message, Func onFunctionExecuting) { var response = new RoleDialogModel(AgentRole.Assistant, inst.Response) { diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs index ad4d3cab..90d754dc 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs @@ -34,7 +34,7 @@ public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase, IRoutingH { } - public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message) + public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message, Func onFunctionExecuting) { var context = _services.GetRequiredService(); var agentId = context.GetCurrentAgentId(); @@ -47,7 +47,7 @@ public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase, IRoutingH } }; - var ret = await routing.InvokeAgent(agentId, dialogs); + var ret = await routing.InvokeAgent(agentId, dialogs, onFunctionExecuting); var response = dialogs.Last(); inst.Response = response.Content; diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs index 59561461..6afc67c9 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs @@ -39,7 +39,7 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler { } - public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message) + public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message, Func onFunctionExecuting) { var states = _services.GetRequiredService(); var goalAgent = states.GetState(StateConst.EXPECTED_GOAL_AGENT); @@ -85,7 +85,7 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler } else { - ret = await routing.InvokeAgent(agentId, _dialogs); + ret = await routing.InvokeAgent(agentId, _dialogs, onFunctionExecuting); } var response = _dialogs.Last(); diff --git a/src/Infrastructure/BotSharp.Core/Routing/Planning/InstructExecutor.cs b/src/Infrastructure/BotSharp.Core/Routing/Planning/InstructExecutor.cs index 0f436edd..5b1c8fcb 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Planning/InstructExecutor.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Planning/InstructExecutor.cs @@ -1,5 +1,3 @@ -using BotSharp.Abstraction.Functions.Models; -using BotSharp.Abstraction.Routing; using BotSharp.Abstraction.Routing.Planning; namespace BotSharp.Core.Routing.Planning; @@ -18,7 +16,8 @@ public class InstructExecutor : IExecutor public async Task Execute(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message, - List dialogs) + List dialogs, + Func onFunctionExecuting) { message.Instruction = inst; @@ -26,7 +25,7 @@ public class InstructExecutor : IExecutor var handler = handlers.FirstOrDefault(x => x.Name == inst.Function); handler.SetDialogs(dialogs); - var handled = await handler.Handle(routing, inst, message); + var handled = await handler.Handle(routing, inst, message, onFunctionExecuting); // For client display purpose var response = dialogs.Last(); diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs index 38912ef7..a682973a 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs @@ -5,7 +5,7 @@ namespace BotSharp.Core.Routing; public partial class RoutingService { private int _currentRecursionDepth = 0; - public async Task InvokeAgent(string agentId, List dialogs) + public async Task InvokeAgent(string agentId, List dialogs, Func onFunctionExecuting) { var agentService = _services.GetRequiredService(); var agent = await agentService.LoadAgent(agentId); @@ -34,7 +34,8 @@ public partial class RoutingService message.FunctionName = response.FunctionName; message.FunctionArgs = response.FunctionArgs; message.CurrentAgentId = agent.Id; - await InvokeFunction(message, dialogs); + + await InvokeFunction(message, dialogs, onFunctionExecuting); } else { @@ -54,7 +55,7 @@ public partial class RoutingService return true; } - private async Task InvokeFunction(RoleDialogModel message, List dialogs) + private async Task InvokeFunction(RoleDialogModel message, List dialogs, Func? onFunctionExecuting = null) { // execute function // Save states @@ -63,7 +64,7 @@ public partial class RoutingService var routing = _services.GetRequiredService(); // Call functions - await routing.InvokeFunction(message.FunctionName, message); + await routing.InvokeFunction(message.FunctionName, message, onFunctionExecuting); // Pass execution result to LLM to get response if (!message.StopCompletion) @@ -88,7 +89,7 @@ public partial class RoutingService // Send to Next LLM var agentId = routing.Context.GetCurrentAgentId(); - await InvokeAgent(agentId, dialogs); + await InvokeAgent(agentId, dialogs, onFunctionExecuting); } } else diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs index 42465533..a750a0ef 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs @@ -4,7 +4,7 @@ namespace BotSharp.Core.Routing; public partial class RoutingService { - public async Task InvokeFunction(string name, RoleDialogModel message) + public async Task InvokeFunction(string name, RoleDialogModel message, Func? onFunctionExecuting = null) { var function = _services.GetServices().FirstOrDefault(x => x.Name == name); if (function == null) @@ -24,6 +24,12 @@ public partial class RoutingService .ToList(); // Before executing functions + clonedMessage.Indication = function.Indication; + if (onFunctionExecuting != null) + { + await onFunctionExecuting(clonedMessage); + } + foreach (var hook in hooks) { await hook.OnFunctionExecuting(clonedMessage); diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index c139ceab..eeb85e77 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -1,19 +1,8 @@ -using BotSharp.Abstraction.Agents.Models; using BotSharp.Abstraction.Infrastructures.Enums; using BotSharp.Abstraction.Routing.Models; using BotSharp.Abstraction.Routing.Planning; using BotSharp.Abstraction.Routing.Settings; -using BotSharp.Abstraction.Templating; -using BotSharp.Core.Routing.Planning; -using Fluid.Ast; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Diagnostics.Metrics; using System.Drawing; -using System.Runtime.InteropServices; -using System.Security.Cryptography.X509Certificates; -using ThirdParty.Json.LitJson; -using static System.Net.Mime.MediaTypeNames; namespace BotSharp.Core.Routing; @@ -67,7 +56,7 @@ public partial class RoutingService : IRoutingService ExecutingDirectly = true }; - var result = await handler.Handle(this, inst, message); + var result = await handler.Handle(this, inst, message, null); var response = dialogs.Last(); response.MessageId = message.MessageId; @@ -76,7 +65,7 @@ public partial class RoutingService : IRoutingService return response; } - public async Task InstructLoop(RoleDialogModel message, List dialogs) + public async Task InstructLoop(RoleDialogModel message, List dialogs, Func onFunctionExecuting) { RoleDialogModel response = default; @@ -134,12 +123,12 @@ public partial class RoutingService : IRoutingService if (inst.HandleDialogsByPlanner) { var dialogWithoutContext = planner.BeforeHandleContext(inst, message, dialogs); - response = await executor.Execute(this, inst, message, dialogWithoutContext); + response = await executor.Execute(this, inst, message, dialogWithoutContext, onFunctionExecuting); planner.AfterHandleContext(dialogs, dialogWithoutContext); } else { - response = await executor.Execute(this, inst, message, dialogs); + response = await executor.Execute(this, inst, message, dialogs, onFunctionExecuting); } await planner.AgentExecuted(_router, inst, response, dialogs); diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index 2f00911d..a9c8d1b8 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -1,4 +1,6 @@ using BotSharp.Abstraction.Routing; +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json; namespace BotSharp.OpenAPI.Controllers; @@ -149,6 +151,15 @@ public class ConversationController : ControllerBase return response; } + private void SetStates(IConversationService conv, NewMessageModel input) + { + conv.States.SetState("channel", input.Channel, source: StateSource.External) + .SetState("provider", input.Provider, source: StateSource.External) + .SetState("model", input.Model, source: StateSource.External) + .SetState("temperature", input.Temperature, source: StateSource.External) + .SetState("sampling_factor", input.SamplingFactor, source: StateSource.External); + } + [HttpPost("/conversation/{agentId}/{conversationId}")] public async Task SendMessage([FromRoute] string agentId, [FromRoute] string conversationId, @@ -165,11 +176,7 @@ public class ConversationController : ControllerBase routing.Context.SetMessageId(conversationId, inputMsg.MessageId); conv.SetConversationId(conversationId, input.States); - conv.States.SetState("channel", input.Channel, source: StateSource.External) - .SetState("provider", input.Provider, source: StateSource.External) - .SetState("model", input.Model, source: StateSource.External) - .SetState("temperature", input.Temperature, source: StateSource.External) - .SetState("sampling_factor", input.SamplingFactor, source: StateSource.External); + SetStates(conv, input); var response = new ChatResponseModel(); @@ -194,6 +201,92 @@ public class ConversationController : ControllerBase return response; } + [HttpPost("/conversation/{agentId}/{conversationId}/sse")] + public async Task SendMessageSse([FromRoute] string agentId, + [FromRoute] string conversationId, + [FromBody] NewMessageModel input) + { + var conv = _services.GetRequiredService(); + if (!string.IsNullOrEmpty(input.TruncateMessageId)) + { + await conv.TruncateConversation(conversationId, input.TruncateMessageId); + } + + var inputMsg = new RoleDialogModel(AgentRole.User, input.Text); + var routing = _services.GetRequiredService(); + routing.Context.SetMessageId(conversationId, inputMsg.MessageId); + + conv.SetConversationId(conversationId, input.States); + SetStates(conv, input); + + var response = new ChatResponseModel(); + + Response.StatusCode = 200; + Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.ContentType, "text/event-stream"); + Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.CacheControl, "no-cache"); + Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.Connection, "keep-alive"); + + await conv.SendMessage(agentId, inputMsg, + replyMessage: input.Postback, + async msg => + { + response.Text = !string.IsNullOrEmpty(msg.SecondaryContent) ? msg.SecondaryContent : msg.Content; + response.Function = msg.FunctionName; + response.RichContent = msg.SecondaryRichContent ?? msg.RichContent; + response.Instruction = msg.Instruction; + response.Data = msg.Data; + + await OnChunkReceived(Response, msg); + }, + async msg => + { + var message = new RoleDialogModel(AgentRole.Function, msg.Content) + { + FunctionArgs = msg.FunctionArgs, + FunctionName = msg.FunctionName, + Indication = msg.Indication + }; + await OnChunkReceived(Response, message); + }, + async msg => + { + + }); + + var state = _services.GetRequiredService(); + response.States = state.GetStates(); + response.MessageId = inputMsg.MessageId; + response.ConversationId = conversationId; + + // await OnEventCompleted(Response); + } + + private async Task OnChunkReceived(HttpResponse response, RoleDialogModel message) + { + var json = JsonConvert.SerializeObject(message, new JsonSerializerSettings + { + Formatting = Formatting.None, + ContractResolver = new CamelCasePropertyNamesContractResolver(), + NullValueHandling = NullValueHandling.Ignore, + }); + + var buffer = Encoding.UTF8.GetBytes($"data:{json}\n"); + await response.Body.WriteAsync(buffer, 0, buffer.Length); + await Task.Delay(10); + + buffer = Encoding.UTF8.GetBytes("\n"); + await response.Body.WriteAsync(buffer, 0, buffer.Length); + } + + private async Task OnEventCompleted(HttpResponse response) + { + var buffer = Encoding.UTF8.GetBytes("data:[DONE]\n"); + await response.Body.WriteAsync(buffer, 0, buffer.Length); + + buffer = Encoding.UTF8.GetBytes("\n"); + await response.Body.WriteAsync(buffer, 0, buffer.Length); + } + [HttpPost("/conversation/{conversationId}/attachments")] public IActionResult UploadAttachments([FromRoute] string conversationId, IFormFile[] files) From 81b79302e9a93969a6943e9f85739211b7fc15e9 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 1 May 2024 10:45:12 -0500 Subject: [PATCH 073/201] add chat indication --- .../Models/ConversationSenderActionModel.cs | 4 ++++ .../Hooks/ChatHubConversationHook.cs | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationSenderActionModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationSenderActionModel.cs index a153472c..b49fc55d 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationSenderActionModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationSenderActionModel.cs @@ -6,6 +6,10 @@ public class ConversationSenderActionModel { [JsonPropertyName("conversation_id")] public string ConversationId { get; set; } + [JsonPropertyName("sender_action")] public SenderActionEnum SenderAction { get; set; } + + [JsonPropertyName("indication")] + public string? Indication { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs index 3b14494b..13d308e1 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs @@ -59,6 +59,20 @@ public class ChatHubConversationHook : ConversationHookBase await base.OnMessageReceived(message); } + public override async Task OnFunctionExecuting(RoleDialogModel message) + { + var conv = _services.GetRequiredService(); + + await _chatHub.Clients.User(_user.Id).SendAsync("OnSenderActionGenerated", new ConversationSenderActionModel + { + ConversationId = conv.ConversationId, + SenderAction = SenderActionEnum.TypingOn, + Indication = message.Indication + }); + + await base.OnFunctionExecuting(message); + } + public override async Task OnPostbackMessageReceived(RoleDialogModel message, PostbackMessageModel replyMsg) { await this.OnMessageReceived(message); From 991582372d69899912c82af35d2840f2d95b817d Mon Sep 17 00:00:00 2001 From: "C. Oceania" Date: Thu, 2 May 2024 10:33:23 -0500 Subject: [PATCH 074/201] Fix SecondaryContent null issue for FileRepository. --- .../Repository/FileRepository/FileRepository.Conversation.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs index b07ba3ce..9a386e92 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs @@ -512,7 +512,7 @@ namespace BotSharp.Core.Repository var content = rawDialogs[i + 2]; var trimmedContent = content.Substring(4); var secondaryContent = rawDialogs[i + 4]; - var trimmedSecondaryContent = secondaryContent.Substring(4); + var trimmedSecondaryContent = string.IsNullOrEmpty(secondaryContent) ? null : secondaryContent.Substring(4); var meta = new DialogMetaData { @@ -551,7 +551,7 @@ namespace BotSharp.Core.Repository dialogTexts.Add(content); dialogTexts.Add(encodedSecondaryRichContent); - var secondaryContent = $" - {element.SecondaryContent}"; + var secondaryContent = element.SecondaryContent == null ? null : $" - {element.SecondaryContent}"; dialogTexts.Add(secondaryContent); } From 7fb386f176a528e3358d33d3e2c45b0e80c198bd Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Thu, 2 May 2024 17:07:12 -0500 Subject: [PATCH 075/201] Anthropic AI --- .../Conversations/Models/RoleDialogModel.cs | 4 + .../Models/FunctionCallingResponse.cs | 2 +- .../Functions/Models/FunctionDef.cs | 8 +- .../Functions/Models/FunctionParametersDef.cs | 5 + .../BotSharp.Core/BotSharp.Core.csproj | 4 +- .../Routing/RoutingService.InvokeAgent.cs | 14 +- .../Templating/TemplateRender.cs | 2 + .../templates/response_with_function.liquid | 23 +- .../AnthropicPlugin.cs | 27 +++ .../BotSharp.Plugin.AnthropicAI.csproj | 17 ++ .../Providers/ChatCompletionProvider.cs | 212 ++++++++++++++++++ .../Settings/AnthropicSettings.cs | 6 + .../Settings/ClaudeSetting.cs | 5 + .../BotSharp.Plugin.AnthropicAI/Using.cs | 16 ++ .../BotSharp.Plugin.AzureOpenAI.csproj | 4 +- .../Providers/ChatCompletionProvider.cs | 57 ++++- 16 files changed, 383 insertions(+), 23 deletions(-) create mode 100644 src/Plugins/BotSharp.Plugin.AnthropicAI/AnthropicPlugin.cs create mode 100644 src/Plugins/BotSharp.Plugin.AnthropicAI/BotSharp.Plugin.AnthropicAI.csproj create mode 100644 src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs create mode 100644 src/Plugins/BotSharp.Plugin.AnthropicAI/Settings/AnthropicSettings.cs create mode 100644 src/Plugins/BotSharp.Plugin.AnthropicAI/Settings/ClaudeSetting.cs create mode 100644 src/Plugins/BotSharp.Plugin.AnthropicAI/Using.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs index bb84b55d..d1d50880 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs @@ -42,6 +42,9 @@ public class RoleDialogModel : ITrackableMessage [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? FunctionName { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? ToolCallId { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? PostbackFunctionName { get; set; } @@ -108,6 +111,7 @@ public class RoleDialogModel : ITrackableMessage MessageId = source.MessageId, FunctionArgs = source.FunctionArgs, FunctionName = source.FunctionName, + ToolCallId = source.ToolCallId, PostbackFunctionName = source.PostbackFunctionName, RichContent = source.RichContent, StopCompletion = source.StopCompletion, diff --git a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallingResponse.cs b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallingResponse.cs index d5bd5979..d437d425 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallingResponse.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallingResponse.cs @@ -13,7 +13,7 @@ public class FunctionCallingResponse [JsonPropertyName("content")] public string? Content { get; set; } - [JsonPropertyName("function_name")] + [JsonPropertyName("function")] public string? FunctionName { get; set; } [JsonPropertyName("args")] diff --git a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionDef.cs b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionDef.cs index 8458adf6..4366d323 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionDef.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionDef.cs @@ -2,8 +2,11 @@ namespace BotSharp.Abstraction.Functions.Models; public class FunctionDef { - public string Name { get; set; } - public string Description { get; set; } + [JsonPropertyName("name")] + public string Name { get; set; } = null!; + + [JsonPropertyName("description")] + public string Description { get; set; } = null!; [JsonPropertyName("visibility_expression")] public string? VisibilityExpression { get; set; } @@ -11,6 +14,7 @@ public class FunctionDef [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Impact { get; set; } + [JsonPropertyName("parameters")] public FunctionParametersDef Parameters { get; set; } = new FunctionParametersDef(); public override string ToString() diff --git a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionParametersDef.cs b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionParametersDef.cs index 975a17d6..12c313fd 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionParametersDef.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionParametersDef.cs @@ -19,6 +19,11 @@ public class FunctionParametersDef [JsonPropertyName("required")] public List Required { get; set; } = new List(); + public override string ToString() + { + return $"{{\"type\":\"{Type}\", \"properties\":{JsonSerializer.Serialize(Properties)}, \"required\":[{string.Join(",", Required.Select(x => "\"" + x + "\""))}]}}"; + } + public FunctionParametersDef() { diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index 6029a5c7..451c5c2a 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 @@ -150,7 +150,7 @@ - + diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs index a682973a..6bdac0ef 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs @@ -17,8 +17,19 @@ public partial class RoutingService return false; } + var provide = agent.LlmConfig.Provider; + var model = agent.LlmConfig.Model; + + if (provide == null || model == null) + { + var agentSettings = _services.GetRequiredService(); + provide = agentSettings.LlmConfig.Provider; + model = agentSettings.LlmConfig.Model; + } + var chatCompletion = CompletionProvider.GetChatCompletion(_services, - agentConfig: agent.LlmConfig); + provider: provide, + model: model); var message = dialogs.Last(); var response = await chatCompletion.GetChatCompletions(agent, dialogs); @@ -31,6 +42,7 @@ public partial class RoutingService { response.FunctionName = response.FunctionName.Split("/").Last(); } + message.ToolCallId = response.ToolCallId; message.FunctionName = response.FunctionName; message.FunctionArgs = response.FunctionArgs; message.CurrentAgentId = agent.Id; diff --git a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs index a5aef0f4..3bdbf7e1 100644 --- a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs +++ b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs @@ -27,6 +27,8 @@ public class TemplateRender : ITemplateRender _options.MemberAccessStrategy.Register(); _options.MemberAccessStrategy.Register(); _options.MemberAccessStrategy.Register(); + _options.MemberAccessStrategy.Register(); + _options.MemberAccessStrategy.Register(); _options.MemberAccessStrategy.Register(); } diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/response_with_function.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/response_with_function.liquid index d45771b3..81ca0f86 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/response_with_function.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/response_with_function.liquid @@ -1,9 +1,14 @@ -[Output Requirements] -1. Read the [Functions] definition, you can utilize the function to retrieve data or execute actions. -2. Think step by step, check if specific function will provider data to help complete user request based on the conversation. -3. If you need to call a function to decide how to response user, - response in format: {"role": "function", "reason":"why choose this function", "function_name": "", "args": {}}, - otherwise response in format: {"role": "assistant", "reason":"why response to user", "content":"next step question"}. -4. If the conversation already contains the function execution result, don't need to call it again. -5. If user mentioned some specific requirment, don't ask this question in your response. -6. Don't repeat the same question in your response. \ No newline at end of file +{% if functions and functions != empty %} +[FUNCTIONS] +{% for fn in functions -%} +{{ fn.name }}: {{ fn.description }} +{{ fn.parameters }} +{{ "\r\n" }} +{%- endfor %} +response_to_user: response to user directly without using any function. +{"type": "object", "properties": {"content":{"type": "string", "description": "The content responsed to user"}}, "required":["content"]} + +[RESPONSE OUTPUT REQUIREMENTS] +* Pick the appropriate function and populate the arguments defined in properties. +* Output the JSON {"function": "", "args":{}} without other text +{% endif %} \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.AnthropicAI/AnthropicPlugin.cs b/src/Plugins/BotSharp.Plugin.AnthropicAI/AnthropicPlugin.cs new file mode 100644 index 00000000..4da20e73 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AnthropicAI/AnthropicPlugin.cs @@ -0,0 +1,27 @@ +using BotSharp.Abstraction.MLTasks; +using BotSharp.Abstraction.Plugins; +using BotSharp.Plugin.AnthropicAI.Providers; +using BotSharp.Plugin.AnthropicAI.Settings; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace BotSharp.Plugin.AnthropicAI; + +public class AnthropicPlugin : IBotSharpPlugin +{ + public string Id => "012119da-8367-4be8-9a75-ab6ae55071e6"; + + public void RegisterDI(IServiceCollection services, IConfiguration config) + { + var settings = new AnthropicSettings(); + config.Bind("Anthropic", settings); + services.AddSingleton(x => + { + // Console.WriteLine($"Loaded Anthropic settings: {settings.Claude.ApiKey.SubstringMax(4)}"); + return settings; + }); + + services.AddScoped(); + // services.AddScoped(); + } +} diff --git a/src/Plugins/BotSharp.Plugin.AnthropicAI/BotSharp.Plugin.AnthropicAI.csproj b/src/Plugins/BotSharp.Plugin.AnthropicAI/BotSharp.Plugin.AnthropicAI.csproj new file mode 100644 index 00000000..357e827f --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AnthropicAI/BotSharp.Plugin.AnthropicAI.csproj @@ -0,0 +1,17 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + diff --git a/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs new file mode 100644 index 00000000..4537e92a --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs @@ -0,0 +1,212 @@ +using Anthropic.SDK.Common; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace BotSharp.Plugin.AnthropicAI.Providers; + +public class ChatCompletionProvider : IChatCompletion +{ + public string Provider => "anthropic"; + + protected readonly AnthropicSettings _settings; + protected readonly IServiceProvider _services; + protected readonly ILogger _logger; + + protected string _model; + + public ChatCompletionProvider(AnthropicSettings settings, + ILogger logger, + IServiceProvider services) + { + _settings = settings; + _logger = logger; + _services = services; + } + + public async Task GetChatCompletions(Agent agent, List conversations) + { + var contentHooks = _services.GetServices().ToList(); + + // Before chat completion hook + foreach (var hook in contentHooks) + { + await hook.BeforeGenerating(agent, conversations); + } + + var settingsService = _services.GetRequiredService(); + var settings = settingsService.GetSetting("anthropic", agent.LlmConfig?.Model ?? "claude-3-haiku"); + + var client = new AnthropicClient(new APIAuthentication(settings.ApiKey)); + var (prompt, parameters, tools) = PrepareOptions(agent, conversations); + + var response = await client.Messages.GetClaudeMessageAsync(parameters, tools); + + RoleDialogModel responseMessage; + + if (response.StopReason == "tool_use") + { + var toolResult = response.Content.OfType().First(); + + responseMessage = new RoleDialogModel(AgentRole.Function, response.FirstMessage?.Text) + { + CurrentAgentId = agent.Id, + MessageId = conversations.Last().MessageId, + ToolCallId = toolResult.Id, + FunctionName = toolResult.Name, + FunctionArgs = JsonSerializer.Serialize(toolResult.Input) + }; + } + else + { + var message = response.FirstMessage; + responseMessage = new RoleDialogModel(AgentRole.Assistant, message.Text) + { + CurrentAgentId = agent.Id, + MessageId = conversations.Last().MessageId + }; + } + + // After chat completion hook + foreach (var hook in contentHooks) + { + await hook.AfterGenerated(responseMessage, new TokenStatsModel + { + Prompt = prompt, + Provider = Provider, + Model = _model, + PromptCount = response.Usage.InputTokens, + CompletionCount = response.Usage.OutputTokens + }); + } + + return responseMessage; + } + + public Task GetChatCompletionsAsync(Agent agent, List conversations, Func onMessageReceived, Func onFunctionExecuting) + { + throw new NotImplementedException(); + } + + public Task GetChatCompletionsStreamingAsync(Agent agent, List conversations, Func onMessageReceived) + { + throw new NotImplementedException(); + } + + private (string, MessageParameters, List) PrepareOptions(Agent agent, List conversations) + { + var prompt = ""; + + var agentService = _services.GetRequiredService(); + + if (!string.IsNullOrEmpty(agent.Instruction)) + { + prompt += agentService.RenderedInstruction(agent); + } + + /*var routing = _services.GetRequiredService(); + var router = routing.Router; + + var render = _services.GetRequiredService(); + var template = router.Templates.FirstOrDefault(x => x.Name == "response_with_function").Content; + + var response_with_function = render.Render(template, new Dictionary + { + { "functions", agent.Functions } + }); + + prompt += "\r\n\r\n" + response_with_function;*/ + + var messages = new List(); + foreach (var conv in conversations) + { + if (conv.Role == AgentRole.User) + { + messages.Add(new Message(RoleType.User, conv.Content)); + } + else if (conv.Role == AgentRole.Assistant) + { + messages.Add(new Message(RoleType.Assistant, conv.Content)); + } + else if (conv.Role == AgentRole.Function) + { + messages.Add(new Message + { + Role = RoleType.Assistant, + Content = new List + { + new ToolUseContent() + { + Id = conv.ToolCallId, + Name = conv.FunctionName, + Input = JsonNode.Parse(conv.FunctionArgs ?? "{}") + } + } + }); + + messages.Add(new Message() + { + Role = RoleType.User, + Content = new List + { + new ToolResultContent() + { + ToolUseId = conv.ToolCallId, + Content = conv.Content + } + } + }); + } + } + + var parameters = new MessageParameters() + { + Messages = messages, + MaxTokens = 256, + Model = AnthropicModels.Claude3Haiku, + Stream = false, + Temperature = 0m, + SystemMessage = prompt + }; + + JsonSerializerOptions jsonSerializationOptions = new() + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Converters = { new JsonStringEnumConverter() }, + ReferenceHandler = ReferenceHandler.IgnoreCycles, + }; + var tools = new List(); + + foreach (var fn in agent.Functions) + { + /*var inputschema = new InputSchema() + { + Type = fn.Parameters.Type, + Properties = new Dictionary() + { + { "location", new Property() { Type = "string", Description = "The location of the weather" } }, + { + "tempType", new Property() + { + Type = "string", Enum = Enum.GetNames(typeof(TempType)), + Description = "The unit of temperature, celsius or fahrenheit" + } + } + }, + Required = fn.Parameters.Required + };*/ + + string jsonString = JsonSerializer.Serialize(fn.Parameters, jsonSerializationOptions); + tools.Add(new Function(fn.Name, fn.Description, + JsonNode.Parse(jsonString))); + } + + + return (prompt, parameters, tools); + } + + public void SetModelName(string model) + { + _model = model; + } +} diff --git a/src/Plugins/BotSharp.Plugin.AnthropicAI/Settings/AnthropicSettings.cs b/src/Plugins/BotSharp.Plugin.AnthropicAI/Settings/AnthropicSettings.cs new file mode 100644 index 00000000..12012528 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AnthropicAI/Settings/AnthropicSettings.cs @@ -0,0 +1,6 @@ +namespace BotSharp.Plugin.AnthropicAI.Settings; + +public class AnthropicSettings +{ + public ClaudeSetting Claude { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.AnthropicAI/Settings/ClaudeSetting.cs b/src/Plugins/BotSharp.Plugin.AnthropicAI/Settings/ClaudeSetting.cs new file mode 100644 index 00000000..4952754d --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AnthropicAI/Settings/ClaudeSetting.cs @@ -0,0 +1,5 @@ +namespace BotSharp.Plugin.AnthropicAI.Settings; + +public class ClaudeSetting +{ +} diff --git a/src/Plugins/BotSharp.Plugin.AnthropicAI/Using.cs b/src/Plugins/BotSharp.Plugin.AnthropicAI/Using.cs new file mode 100644 index 00000000..ceb477d6 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AnthropicAI/Using.cs @@ -0,0 +1,16 @@ +global using Anthropic.SDK; +global using Anthropic.SDK.Constants; +global using Anthropic.SDK.Messaging; +global using BotSharp.Abstraction.Agents; +global using BotSharp.Abstraction.Agents.Enums; +global using BotSharp.Abstraction.Agents.Models; +global using BotSharp.Abstraction.Conversations.Models; +global using BotSharp.Abstraction.Functions.Models; +global using BotSharp.Abstraction.Loggers; +global using BotSharp.Abstraction.MLTasks; +global using BotSharp.Abstraction.Routing; +global using BotSharp.Abstraction.Templating; +global using BotSharp.Plugin.AnthropicAI.Settings; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Logging; +global using BotSharp.Abstraction.Utilities; diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/BotSharp.Plugin.AzureOpenAI.csproj b/src/Plugins/BotSharp.Plugin.AzureOpenAI/BotSharp.Plugin.AzureOpenAI.csproj index 00fb4060..1ffa0081 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/BotSharp.Plugin.AzureOpenAI.csproj +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/BotSharp.Plugin.AzureOpenAI.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 @@ -11,7 +11,7 @@ - + diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs index a9fe828a..2d759505 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs @@ -52,11 +52,7 @@ public class ChatCompletionProvider : IChatCompletion var choice = response.Value.Choices[0]; var message = choice.Message; - var responseMessage = new RoleDialogModel(AgentRole.Assistant, message.Content) - { - CurrentAgentId = agent.Id, - MessageId = conversations.Last().MessageId - }; + RoleDialogModel responseMessage; if (choice.FinishReason == CompletionsFinishReason.FunctionCall) { @@ -74,6 +70,33 @@ public class ChatCompletionProvider : IChatCompletion responseMessage.FunctionName = responseMessage.FunctionName.Split('.').Last(); } } + else if (choice.FinishReason == CompletionsFinishReason.ToolCalls) + { + // Add the assistant message with tool calls to the conversation history + // ChatRequestAssistantMessage toolCallHistoryMessage = new(message); + // chatCompletionsOptions.Messages.Add(toolCallHistoryMessage); + + // Add a new tool message for each tool call that is resolved + var toolCall = message.ToolCalls.First() as ChatCompletionsFunctionToolCall; + // var toolCallResponseMessage = GetToolCallResponseMessage(toolCall); + // Now make a new request with all the messages thus far, including the original + + responseMessage = new RoleDialogModel(AgentRole.Function, message.Content) + { + CurrentAgentId = agent.Id, + MessageId = conversations.Last().MessageId, + FunctionName = toolCall.Name, + FunctionArgs = toolCall.Arguments + }; + } + else + { + responseMessage = new RoleDialogModel(AgentRole.Assistant, message.Content) + { + CurrentAgentId = agent.Id, + MessageId = conversations.Last().MessageId + }; + } // After chat completion hook foreach(var hook in contentHooks) @@ -222,7 +245,17 @@ public class ChatCompletionProvider : IChatCompletion if (agentService.RenderFunction(agent, function)) { var property = agentService.RenderFunctionProperty(agent, function); - chatCompletionsOptions.Functions.Add(new FunctionDefinition + + // legacy function call + /*chatCompletionsOptions.Functions.Add(new FunctionDefinition + { + Name = function.Name, + Description = function.Description, + Parameters = BinaryData.FromObjectAsJson(property) + });*/ + + // new chat tool + chatCompletionsOptions.Tools.Add(new ChatCompletionsFunctionToolDefinition { Name = function.Name, Description = function.Description, @@ -241,6 +274,7 @@ public class ChatCompletionProvider : IChatCompletion }); chatCompletionsOptions.Messages.Add(new ChatRequestFunctionMessage(message.FunctionName, message.Content)); + // chatCompletionsOptions.Messages.Add(new ChatRequestToolMessage(message.Content, message.ToolCallId)); } else if (message.Role == ChatRole.User) { @@ -346,4 +380,15 @@ public class ChatCompletionProvider : IChatCompletion { _model = model; } + + ChatRequestToolMessage GetToolCallResponseMessage(ChatCompletionsToolCall toolCall) + { + var functionToolCall = toolCall as ChatCompletionsFunctionToolCall; + // Validate and process the JSON arguments for the function call + string unvalidatedArguments = functionToolCall.Arguments; + var functionResultData = (object)null; // GetYourFunctionResultData(unvalidatedArguments); + // Here, replacing with an example as if returned from "GetYourFunctionResultData" + functionResultData = "31 celsius"; + return new ChatRequestToolMessage(functionResultData.ToString(), toolCall.Id); + } } From e4c5225e1b678ca494c81313a25b7b6ddcb332a4 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Fri, 3 May 2024 11:22:13 -0500 Subject: [PATCH 076/201] Fix AnthropicAI prompt log. --- .../Providers/ChatCompletionProvider.cs | 72 ++++++++++++++++--- 1 file changed, 63 insertions(+), 9 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs index 4537e92a..87a22ee8 100644 --- a/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs @@ -38,9 +38,9 @@ public class ChatCompletionProvider : IChatCompletion var settings = settingsService.GetSetting("anthropic", agent.LlmConfig?.Model ?? "claude-3-haiku"); var client = new AnthropicClient(new APIAuthentication(settings.ApiKey)); - var (prompt, parameters, tools) = PrepareOptions(agent, conversations); + var (prompt, parameters) = PrepareOptions(agent, conversations); - var response = await client.Messages.GetClaudeMessageAsync(parameters, tools); + var response = await client.Messages.GetClaudeMessageAsync(parameters); RoleDialogModel responseMessage; @@ -93,15 +93,15 @@ public class ChatCompletionProvider : IChatCompletion throw new NotImplementedException(); } - private (string, MessageParameters, List) PrepareOptions(Agent agent, List conversations) + private (string, MessageParameters) PrepareOptions(Agent agent, List conversations) { - var prompt = ""; + var instruction = ""; var agentService = _services.GetRequiredService(); if (!string.IsNullOrEmpty(agent.Instruction)) { - prompt += agentService.RenderedInstruction(agent); + instruction += agentService.RenderedInstruction(agent); } /*var routing = _services.GetRequiredService(); @@ -166,7 +166,8 @@ public class ChatCompletionProvider : IChatCompletion Model = AnthropicModels.Claude3Haiku, Stream = false, Temperature = 0m, - SystemMessage = prompt + SystemMessage = instruction, + Tools = new List() { } }; JsonSerializerOptions jsonSerializationOptions = new() @@ -175,7 +176,6 @@ public class ChatCompletionProvider : IChatCompletion Converters = { new JsonStringEnumConverter() }, ReferenceHandler = ReferenceHandler.IgnoreCycles, }; - var tools = new List(); foreach (var fn in agent.Functions) { @@ -197,12 +197,66 @@ public class ChatCompletionProvider : IChatCompletion };*/ string jsonString = JsonSerializer.Serialize(fn.Parameters, jsonSerializationOptions); - tools.Add(new Function(fn.Name, fn.Description, + parameters.Tools.Add(new Function(fn.Name, fn.Description, JsonNode.Parse(jsonString))); } + var prompt = GetPrompt(parameters); - return (prompt, parameters, tools); + return (prompt, parameters); + } + + private string GetPrompt(MessageParameters parameters) + { + var prompt = $"{parameters.SystemMessage}\r\n"; + prompt += "\r\n[CONVERSATION]"; + + var verbose = string.Join("\r\n", parameters.Messages + .Select(x => + { + var role = x.Role.ToString().ToLower(); + + if (x.Role == RoleType.User) + { + var content = string.Join("\r\n", x.Content.Select(c => + { + if (c is TextContent text) + return text.Text; + else if (c is ToolResultContent tool) + return $"{tool.Content}"; + else + return string.Empty; + })); + return $"{role}: {content}"; + } + else if (x.Role == RoleType.Assistant) + { + var content = string.Join("\r\n", x.Content.Select(c => + { + if (c is TextContent text) + return text.Text; + else if (c is ToolUseContent tool) + return $"Call function {tool.Name}({JsonSerializer.Serialize(tool.Input)})"; + else + return string.Empty; + })); + return $"{role}: {content}"; + } + return string.Empty; + })); + + prompt += $"\r\n{verbose}\r\n"; + + if (parameters.Tools != null && parameters.Tools.Count > 0) + { + var functions = string.Join("\r\n", parameters.Tools.Select(x => + { + return $"\r\n{x.Name}: {x.Description}\r\n{JsonSerializer.Serialize(x.Parameters)}"; + })); + prompt += $"\r\n[FUNCTIONS]\r\n{functions}\r\n"; + } + + return prompt; } public void SetModelName(string model) From 6ad8a3e076a8871cb1ce4156720727c265b843c8 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Fri, 3 May 2024 13:17:14 -0500 Subject: [PATCH 077/201] Fix compile error. --- BotSharp.sln | 13 ++++++++++++- .../RichContent/Template/ButtonTemplateMessage.cs | 1 - src/Infrastructure/BotSharp.Abstraction/Using.cs | 3 ++- src/Infrastructure/BotSharp.Core/Using.cs | 2 ++ .../BotSharp.Plugin.AnthropicAI/AnthropicPlugin.cs | 5 +---- .../Providers/ChatCompletionProvider.cs | 7 ++++--- .../BotSharp.Plugin.LLamaSharp.csproj | 2 +- .../Providers/TextEmbeddingProvider.cs | 2 +- src/WebStarter/WebStarter.csproj | 5 +++-- src/WebStarter/appsettings.json | 7 +++++++ 10 files changed, 33 insertions(+), 14 deletions(-) diff --git a/BotSharp.sln b/BotSharp.sln index b04cd1c7..804e4092 100644 --- a/BotSharp.sln +++ b/BotSharp.sln @@ -83,7 +83,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.SqlDriver", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.Dashboard", "src\Plugins\BotSharp.Plugin.Dashboard\BotSharp.Plugin.Dashboard.csproj", "{267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.SparkDesk", "src\Plugins\BotSharp.Plugin.SparkDesk\BotSharp.Plugin.SparkDesk.csproj", "{289E25C8-63F1-4D52-9909-207724DB40CB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.SparkDesk", "src\Plugins\BotSharp.Plugin.SparkDesk\BotSharp.Plugin.SparkDesk.csproj", "{289E25C8-63F1-4D52-9909-207724DB40CB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.AnthropicAI", "src\Plugins\BotSharp.Plugin.AnthropicAI\BotSharp.Plugin.AnthropicAI.csproj", "{806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -333,6 +335,14 @@ Global {289E25C8-63F1-4D52-9909-207724DB40CB}.Release|Any CPU.Build.0 = Release|Any CPU {289E25C8-63F1-4D52-9909-207724DB40CB}.Release|x64.ActiveCfg = Release|Any CPU {289E25C8-63F1-4D52-9909-207724DB40CB}.Release|x64.Build.0 = Release|Any CPU + {806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Debug|x64.ActiveCfg = Debug|Any CPU + {806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Debug|x64.Build.0 = Debug|Any CPU + {806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Release|Any CPU.Build.0 = Release|Any CPU + {806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Release|x64.ActiveCfg = Release|Any CPU + {806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -375,6 +385,7 @@ Global {D775DB67-A4B4-44E5-9144-522689590057} = {51AFE054-AE99-497D-A593-69BAEFB5106F} {267998C1-55C2-4ADC-8361-2CDFA5EA6D6C} = {51AFE054-AE99-497D-A593-69BAEFB5106F} {289E25C8-63F1-4D52-9909-207724DB40CB} = {D5293208-2BEF-42FC-A64C-5954F61720BA} + {806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C} = {D5293208-2BEF-42FC-A64C-5954F61720BA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19} 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 647ebc98..bdc742ce 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,3 @@ -using BotSharp.Abstraction.Messaging.Enums; using Newtonsoft.Json; namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template; diff --git a/src/Infrastructure/BotSharp.Abstraction/Using.cs b/src/Infrastructure/BotSharp.Abstraction/Using.cs index 91f672b8..6668aec6 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Using.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Using.cs @@ -14,4 +14,5 @@ global using BotSharp.Abstraction.Models; global using BotSharp.Abstraction.Routing.Models; global using BotSharp.Abstraction.Routing.Planning; global using BotSharp.Abstraction.Templating; -global using BotSharp.Abstraction.Translation.Attributes; \ No newline at end of file +global using BotSharp.Abstraction.Translation.Attributes; +global using BotSharp.Abstraction.Messaging.Enums; diff --git a/src/Infrastructure/BotSharp.Core/Using.cs b/src/Infrastructure/BotSharp.Core/Using.cs index 769f318f..453fc896 100644 --- a/src/Infrastructure/BotSharp.Core/Using.cs +++ b/src/Infrastructure/BotSharp.Core/Using.cs @@ -23,6 +23,8 @@ global using BotSharp.Abstraction.Functions.Models; global using BotSharp.Abstraction.Repositories; global using BotSharp.Abstraction.Repositories.Filters; global using BotSharp.Abstraction.Translation; +global using BotSharp.Abstraction.Translation.Attributes; +global using BotSharp.Abstraction.Messaging.Enums; global using BotSharp.Core.Repository; global using BotSharp.Core.Routing; global using BotSharp.Core.Agents.Services; diff --git a/src/Plugins/BotSharp.Plugin.AnthropicAI/AnthropicPlugin.cs b/src/Plugins/BotSharp.Plugin.AnthropicAI/AnthropicPlugin.cs index 4da20e73..701d7416 100644 --- a/src/Plugins/BotSharp.Plugin.AnthropicAI/AnthropicPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.AnthropicAI/AnthropicPlugin.cs @@ -1,9 +1,6 @@ -using BotSharp.Abstraction.MLTasks; using BotSharp.Abstraction.Plugins; using BotSharp.Plugin.AnthropicAI.Providers; -using BotSharp.Plugin.AnthropicAI.Settings; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; namespace BotSharp.Plugin.AnthropicAI; @@ -14,7 +11,7 @@ public class AnthropicPlugin : IBotSharpPlugin public void RegisterDI(IServiceCollection services, IConfiguration config) { var settings = new AnthropicSettings(); - config.Bind("Anthropic", settings); + config.Bind("AnthropicAi", settings); services.AddSingleton(x => { // Console.WriteLine($"Loaded Anthropic settings: {settings.Claude.ApiKey.SubstringMax(4)}"); diff --git a/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs index 87a22ee8..aaebfc33 100644 --- a/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs @@ -1,4 +1,5 @@ using Anthropic.SDK.Common; +using BotSharp.Abstraction.MLTasks.Settings; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; @@ -38,7 +39,7 @@ public class ChatCompletionProvider : IChatCompletion var settings = settingsService.GetSetting("anthropic", agent.LlmConfig?.Model ?? "claude-3-haiku"); var client = new AnthropicClient(new APIAuthentication(settings.ApiKey)); - var (prompt, parameters) = PrepareOptions(agent, conversations); + var (prompt, parameters) = PrepareOptions(agent, conversations, settings); var response = await client.Messages.GetClaudeMessageAsync(parameters); @@ -93,7 +94,7 @@ public class ChatCompletionProvider : IChatCompletion throw new NotImplementedException(); } - private (string, MessageParameters) PrepareOptions(Agent agent, List conversations) + private (string, MessageParameters) PrepareOptions(Agent agent, List conversations, LlmModelSetting settings) { var instruction = ""; @@ -163,7 +164,7 @@ public class ChatCompletionProvider : IChatCompletion { Messages = messages, MaxTokens = 256, - Model = AnthropicModels.Claude3Haiku, + Model = settings.Version, // AnthropicModels.Claude3Haiku Stream = false, Temperature = 0m, SystemMessage = instruction, diff --git a/src/Plugins/BotSharp.Plugin.LLamaSharp/BotSharp.Plugin.LLamaSharp.csproj b/src/Plugins/BotSharp.Plugin.LLamaSharp/BotSharp.Plugin.LLamaSharp.csproj index 5bdf5944..cb9087e9 100644 --- a/src/Plugins/BotSharp.Plugin.LLamaSharp/BotSharp.Plugin.LLamaSharp.csproj +++ b/src/Plugins/BotSharp.Plugin.LLamaSharp/BotSharp.Plugin.LLamaSharp.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/TextEmbeddingProvider.cs b/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/TextEmbeddingProvider.cs index 12006342..9a562000 100644 --- a/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/TextEmbeddingProvider.cs +++ b/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/TextEmbeddingProvider.cs @@ -27,7 +27,7 @@ public class TextEmbeddingProvider : ITextEmbedding _embedder = new LLamaEmbedder(weights, @params); } - return Task.FromResult(_embedder.GetEmbeddings(text)); + return _embedder.GetEmbeddings(text); } public Task> GetVectorsAsync(List texts) diff --git a/src/WebStarter/WebStarter.csproj b/src/WebStarter/WebStarter.csproj index 913bd83c..ad08c13c 100644 --- a/src/WebStarter/WebStarter.csproj +++ b/src/WebStarter/WebStarter.csproj @@ -27,17 +27,18 @@ - - + + + diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index 1abd6ce3..c6555f32 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -152,6 +152,12 @@ "AzureOpenAi": { }, + "AnthropicAi": { + "Claude": { + + } + }, + "GoogleAi": { "PaLM": { "Endpoint": "https://generativelanguage.googleapis.com", @@ -245,6 +251,7 @@ "BotSharp.Plugin.MongoStorage", "BotSharp.Plugin.Dashboard", "BotSharp.Plugin.AzureOpenAI", + "BotSharp.Plugin.AnthropicAI", "BotSharp.Plugin.GoogleAI", "BotSharp.Plugin.MetaAI", "BotSharp.Plugin.MetaMessenger", From c01b21baa90117f6a9c5058341e7c8fe94f6e529 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Fri, 3 May 2024 13:53:47 -0500 Subject: [PATCH 078/201] Downgrade Fluid.Core --- src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index 451c5c2a..e091f052 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -150,7 +150,7 @@ - + From 2f08a13f0529ae20b8d8052334be1d88fb1d50ec Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Fri, 3 May 2024 14:57:44 -0500 Subject: [PATCH 079/201] save file to directory --- .../IConversationAttachmentService.cs | 1 + .../Models/IncomingMessageModel.cs | 2 + .../Conversations/Models/RoleDialogModel.cs | 2 + .../Files/Models/BotSharpFile.cs | 24 +++++ .../BotSharp.Abstraction/Using.cs | 3 +- .../Services/ConversationAttachmentService.cs | 101 +++++++++++++++++- .../ConversationService.SendMessage.cs | 8 ++ .../Services/ConversationStorage.cs | 3 + src/Infrastructure/BotSharp.Core/Using.cs | 1 + .../Controllers/ConversationController.cs | 5 +- 10 files changed, 146 insertions(+), 4 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationAttachmentService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationAttachmentService.cs index feec3fab..8020ebf5 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationAttachmentService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationAttachmentService.cs @@ -3,4 +3,5 @@ namespace BotSharp.Abstraction.Conversations; public interface IConversationAttachmentService { string GetDirectory(string conversationId); + void SaveConversationFiles(List files); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/IncomingMessageModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/IncomingMessageModel.cs index 2176c7fa..cd4aad61 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/IncomingMessageModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/IncomingMessageModel.cs @@ -9,4 +9,6 @@ public class IncomingMessageModel : MessageConfig /// Postback message /// public PostbackMessageModel? Postback { get; set; } + + public List Files { get; set; } = new List(); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs index bb84b55d..8085a2c5 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs @@ -75,6 +75,8 @@ public class RoleDialogModel : ITrackableMessage public FunctionCallFromLlm Instruction { get; set; } + public List Files { get; set; } = new List(); + private RoleDialogModel() { } diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs b/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs new file mode 100644 index 00000000..acf47fbe --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs @@ -0,0 +1,24 @@ + +namespace BotSharp.Abstraction.Files.Models; + +public class BotSharpFile +{ + [JsonPropertyName("conversation_id")] + public string ConversationId { get; set; } + + [JsonPropertyName("message_id")] + public string MessageId { get; set; } + + [JsonPropertyName("file_name")] + public string FileName { get; set; } + + [JsonPropertyName("file_data")] + public string FileData { get; set; } + + [JsonPropertyName("content_type")] + public string ContentType { get; set; } + + [JsonPropertyName("file_size")] + public int FileSize { get; set; } + +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Using.cs b/src/Infrastructure/BotSharp.Abstraction/Using.cs index 91f672b8..75a9d00e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Using.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Using.cs @@ -14,4 +14,5 @@ global using BotSharp.Abstraction.Models; global using BotSharp.Abstraction.Routing.Models; global using BotSharp.Abstraction.Routing.Planning; global using BotSharp.Abstraction.Templating; -global using BotSharp.Abstraction.Translation.Attributes; \ No newline at end of file +global using BotSharp.Abstraction.Translation.Attributes; +global using BotSharp.Abstraction.Files.Models; \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationAttachmentService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationAttachmentService.cs index b0625fa8..f5bc7bcd 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationAttachmentService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationAttachmentService.cs @@ -1,5 +1,6 @@ -using BotSharp.Abstraction.Repositories; using System.IO; +using System.IO.Enumeration; +using System.Threading; namespace BotSharp.Core.Conversations.Services; @@ -7,6 +8,10 @@ public class ConversationAttachmentService : IConversationAttachmentService { private readonly BotSharpDatabaseSettings _dbSettings; private readonly IServiceProvider _services; + private readonly string _baseDir; + + private const string CONVERSATION_FOLDER = "conversations"; + private const string FILE_FOLDER = "files"; public ConversationAttachmentService( BotSharpDatabaseSettings dbSettings, @@ -14,15 +19,107 @@ public class ConversationAttachmentService : IConversationAttachmentService { _dbSettings = dbSettings; _services = services; + _baseDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, dbSettings.FileRepository); } public string GetDirectory(string conversationId) { - var dir = Path.Combine(_dbSettings.FileRepository, "conversations", conversationId, "attachments"); + var dir = Path.Combine(_dbSettings.FileRepository, CONVERSATION_FOLDER, conversationId, "attachments"); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } return dir; } + + public string GetConversationFileDirectory(string conversationId) + { + var dir = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, FILE_FOLDER); + if (!Directory.Exists(dir)) + { + Directory.CreateDirectory(dir); + } + return dir; + } + + public void SaveConversationFiles(List files) + { + if (files.IsNullOrEmpty()) return; + + var converationId = files.First().ConversationId; + var dir = GetConversationFileDirectory(converationId); + + for (int i = 0; i < files.Count; i++) + { + var file = files[i]; + if (string.IsNullOrEmpty(file.ConversationId) + || string.IsNullOrEmpty(file.MessageId) + || string.IsNullOrEmpty(file.FileData)) + { + continue; + } + + var fileType = GetFileType(file.FileData); + var bytes = GetFileBytes(file.FileData); + var parsedFormat = ParseFileFormat(fileType); + if (string.IsNullOrEmpty(parsedFormat)) + { + continue; + } + + var fileName = $"{file.MessageId}-{i+1}{parsedFormat}"; + Thread.Sleep(100); + File.WriteAllBytes(Path.Combine(dir, fileName), bytes); + } + } + + private string GetFileType(string data) + { + if (string.IsNullOrEmpty(data)) + { + return string.Empty; + } + + var startIdx = data.IndexOf(':'); + var endIdx = data.IndexOf(';'); + var fileType = data.Substring(startIdx + 1, endIdx - startIdx - 1); + return fileType; + } + + private byte[] GetFileBytes(string data) + { + if (string.IsNullOrEmpty(data)) + { + return new byte[0]; + } + + var startIdx = data.IndexOf(','); + var base64Str = data.Substring(startIdx + 1); + return Convert.FromBase64String(base64Str); + } + + private string ParseFileFormat(string type) + { + var parsed = string.Empty; + switch (type) + { + case "image/png": + parsed = ".png"; + break; + case "image/jpeg": + case "image/jpg": + parsed = ".jpeg"; + break; + case "application/pdf": + parsed = ".pdf"; + break; + case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": + parsed = ".xlsx"; + break; + case "text/plain": + parsed = ".txt"; + break; + } + return parsed; + } } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs index 213d5a67..dbfdf7dc 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Messaging; +using BotSharp.Abstraction.Messaging.Enums; using BotSharp.Abstraction.Messaging.Models.RichContent; using BotSharp.Abstraction.Routing.Settings; using System.Drawing; @@ -141,6 +142,13 @@ public partial class ConversationService Message = new TextMessage(response.SecondaryContent ?? response.Content) }; + response.RichContent = new RichContent + { + Recipient = new Recipient { Id = state.GetConversationId() }, + Editor = EditorTypeEnum.File, + Message = new TextMessage(response.SecondaryContent ?? response.Content) + }; + // Patch return function name if (response.PostbackFunctionName != null) { diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs index 4ab447b8..13cbdedd 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs @@ -25,6 +25,7 @@ public class ConversationStorage : IConversationStorage { var agentId = dialog.CurrentAgentId; var db = _services.GetRequiredService(); + var attachment = _services.GetRequiredService(); var dialogElements = new List(); // Prevent duplicate record to be inserted @@ -76,6 +77,8 @@ public class ConversationStorage : IConversationStorage } db.AppendConversationDialogs(conversationId, dialogElements); + attachment.SaveConversationFiles(dialog.Files); + dialog.Files.Clear(); } public List GetDialogs(string conversationId) diff --git a/src/Infrastructure/BotSharp.Core/Using.cs b/src/Infrastructure/BotSharp.Core/Using.cs index 769f318f..4478466d 100644 --- a/src/Infrastructure/BotSharp.Core/Using.cs +++ b/src/Infrastructure/BotSharp.Core/Using.cs @@ -23,6 +23,7 @@ global using BotSharp.Abstraction.Functions.Models; global using BotSharp.Abstraction.Repositories; global using BotSharp.Abstraction.Repositories.Filters; global using BotSharp.Abstraction.Translation; +global using BotSharp.Abstraction.Files.Models; global using BotSharp.Core.Repository; global using BotSharp.Core.Routing; global using BotSharp.Core.Agents.Services; diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index a9c8d1b8..a20f9b6b 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -171,7 +171,10 @@ public class ConversationController : ControllerBase await conv.TruncateConversation(conversationId, input.TruncateMessageId); } - var inputMsg = new RoleDialogModel(AgentRole.User, input.Text); + var inputMsg = new RoleDialogModel(AgentRole.User, input.Text) + { + Files = input.Files + }; var routing = _services.GetRequiredService(); routing.Context.SetMessageId(conversationId, inputMsg.MessageId); From 2ecc0206aa570c43cba531eb91dfa22c7ffa9a59 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Fri, 3 May 2024 17:24:18 -0500 Subject: [PATCH 080/201] temp save --- .../IConversationAttachmentService.cs | 2 + .../Files/Models/OutputFileModel.cs | 13 ++++ .../Services/ConversationAttachmentService.cs | 68 ++++++++++++++++--- .../Controllers/ConversationController.cs | 29 ++++++++ 4 files changed, 104 insertions(+), 8 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Files/Models/OutputFileModel.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationAttachmentService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationAttachmentService.cs index 8020ebf5..848cb474 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationAttachmentService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationAttachmentService.cs @@ -3,5 +3,7 @@ namespace BotSharp.Abstraction.Conversations; public interface IConversationAttachmentService { string GetDirectory(string conversationId); + IEnumerable GetConversationFiles(string conversationId, string messageId); + string? GetMessageFile(string conversationId, string messageId, string fileType, int index); void SaveConversationFiles(List files); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/Models/OutputFileModel.cs b/src/Infrastructure/BotSharp.Abstraction/Files/Models/OutputFileModel.cs new file mode 100644 index 00000000..962b01e1 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Files/Models/OutputFileModel.cs @@ -0,0 +1,13 @@ +namespace BotSharp.Abstraction.Files.Models; + +public class OutputFileModel +{ + [JsonPropertyName("file_url")] + public string FileUrl { get; set; } + + [JsonPropertyName("file_name")] + public string FileName { get; set; } + + [JsonPropertyName("file_type")] + public string FileType { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationAttachmentService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationAttachmentService.cs index f5bc7bcd..22e1158c 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationAttachmentService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationAttachmentService.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Http; using System.IO; using System.IO.Enumeration; using System.Threading; @@ -12,6 +13,7 @@ public class ConversationAttachmentService : IConversationAttachmentService private const string CONVERSATION_FOLDER = "conversations"; private const string FILE_FOLDER = "files"; + private const string SEPARATOR = "."; public ConversationAttachmentService( BotSharpDatabaseSettings dbSettings, @@ -32,22 +34,60 @@ public class ConversationAttachmentService : IConversationAttachmentService return dir; } - public string GetConversationFileDirectory(string conversationId) + public IEnumerable GetConversationFiles(string conversationId, string messageId) { - var dir = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, FILE_FOLDER); - if (!Directory.Exists(dir)) + var outputFiles = new List(); + if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId)) { - Directory.CreateDirectory(dir); + return outputFiles; } - return dir; + + var context = _services.GetRequiredService(); + var request = context.HttpContext.Request; + var host = $"{request.Scheme}{Uri.SchemeDelimiter}{request.Host.Value}"; + var dir = GetConversationFileDirectory(conversationId); + + foreach (var file in Directory.GetFiles(dir)) + { + var fileName = file.Split(Path.DirectorySeparatorChar).Last(); + var splits = fileName.Split('.'); + var fileMsgId = splits.First(); + + if (fileMsgId != messageId) continue; + + var index = splits[1]; + var fileType = splits.Last(); + var model = new OutputFileModel() + { + FileUrl = $"{host}/conversation/{conversationId}/file/{messageId}/type/{fileType}/{index}", + FileName = fileName, + FileType = fileType + }; + outputFiles.Add(model); + } + return outputFiles; + } + + public string? GetMessageFile(string conversationId, string messageId, string fileType, int index) + { + var targetFile = $"{messageId}{SEPARATOR}{index}.{fileType}"; + var dir = GetConversationFileDirectory(conversationId); + var files = Directory.GetFiles(dir); + var found = files.FirstOrDefault(f => + { + var fileName = f.Split(Path.DirectorySeparatorChar).Last(); + return fileName.IsEqualTo(targetFile); + }); + + return found; } public void SaveConversationFiles(List files) { if (files.IsNullOrEmpty()) return; - var converationId = files.First().ConversationId; - var dir = GetConversationFileDirectory(converationId); + var conversationId = files.First().ConversationId; + var dir = GetConversationFileDirectory(conversationId); for (int i = 0; i < files.Count; i++) { @@ -67,12 +107,23 @@ public class ConversationAttachmentService : IConversationAttachmentService continue; } - var fileName = $"{file.MessageId}-{i+1}{parsedFormat}"; + var fileName = $"{file.MessageId}{SEPARATOR}{i+1}{parsedFormat}"; Thread.Sleep(100); File.WriteAllBytes(Path.Combine(dir, fileName), bytes); } } + #region Private methods + private string GetConversationFileDirectory(string conversationId) + { + var dir = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, FILE_FOLDER); + if (!Directory.Exists(dir)) + { + Directory.CreateDirectory(dir); + } + return dir; + } + private string GetFileType(string data) { if (string.IsNullOrEmpty(data)) @@ -122,4 +173,5 @@ public class ConversationAttachmentService : IConversationAttachmentService } return parsed; } + #endregion } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index a20f9b6b..5940bdbb 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -1,6 +1,8 @@ using BotSharp.Abstraction.Routing; using Newtonsoft.Json.Serialization; using Newtonsoft.Json; +using BotSharp.Abstraction.Files.Models; +using Microsoft.AspNetCore.Hosting; namespace BotSharp.OpenAPI.Controllers; @@ -315,4 +317,31 @@ public class ConversationController : ControllerBase return BadRequest(new { message = "Invalid file." }); } + + [HttpGet("/conversation/{conversationId}/files/{messageId}")] + public IEnumerable GetConversationFiles([FromRoute] string conversationId, [FromRoute] string messageId) + { + var attachment = _services.GetRequiredService(); + return attachment.GetConversationFiles(conversationId, messageId); + } + + [AllowAnonymous] + [HttpGet("/conversation/{conversationId}/file/{messageId}/type/{type}/{index}")] + public async Task GetMessageFile([FromRoute] string conversationId, [FromRoute] string messageId, + [FromRoute] string type, [FromRoute] int index, [FromQuery] string token) + { + var attachment = _services.GetRequiredService(); + var file = attachment.GetMessageFile(conversationId, messageId, type, index); + if (System.IO.File.Exists(file)) + { + using Stream stream = System.IO.File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read); + var bytes = new byte[stream.Length]; + stream.Read(bytes, 0, (int)stream.Length); + return File(bytes, "application/octet-stream", Path.GetFileName(file)); + } + else + { + return NotFound(); + } + } } From 1eb8631476283fe4ed6dbdc2367195a23db43a26 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Sun, 5 May 2024 17:18:34 -0500 Subject: [PATCH 081/201] Azure.AI.OpenAI v1.0.0-beta.17 --- .../BotSharp.Plugin.AzureOpenAI.csproj | 2 +- .../Providers/ChatCompletionProvider.cs | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/BotSharp.Plugin.AzureOpenAI.csproj b/src/Plugins/BotSharp.Plugin.AzureOpenAI/BotSharp.Plugin.AzureOpenAI.csproj index 1ffa0081..6c76a9fb 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/BotSharp.Plugin.AzureOpenAI.csproj +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/BotSharp.Plugin.AzureOpenAI.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs index 2d759505..d2077ae6 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs @@ -334,6 +334,7 @@ public class ChatCompletionProvider : IChatCompletion })); prompt += $"{verbose}\r\n"; + prompt += "\r\n[CONVERSATION]"; verbose = string.Join("\r\n", chatCompletionsOptions.Messages .Where(x => x.Role != AgentRole.System).Select(x => { @@ -364,13 +365,14 @@ public class ChatCompletionProvider : IChatCompletion prompt += $"\r\n{verbose}\r\n"; } - if (chatCompletionsOptions.Functions.Count > 0) + if (chatCompletionsOptions.Tools.Count > 0) { - var functions = string.Join("\r\n", chatCompletionsOptions.Functions.Select(x => + var functions = string.Join("\r\n", chatCompletionsOptions.Tools.Select(x => { - return $"\r\n{x.Name}: {x.Description}\r\n{x.Parameters}"; + var fn = x as ChatCompletionsFunctionToolDefinition; + return $"\r\n{fn.Name}: {fn.Description}\r\n{fn.Parameters}"; })); - prompt += $"\r\n[FUNCTIONS]\r\n{functions}\r\n"; + prompt += $"\r\n[FUNCTIONS]{functions}\r\n"; } return prompt; From 0ea7d94cee444444a0b5bbe818b21e1bdb1c10c9 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Sun, 5 May 2024 17:23:42 -0500 Subject: [PATCH 082/201] Anthropic Plugin description. --- src/Plugins/BotSharp.Plugin.AnthropicAI/AnthropicPlugin.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Plugins/BotSharp.Plugin.AnthropicAI/AnthropicPlugin.cs b/src/Plugins/BotSharp.Plugin.AnthropicAI/AnthropicPlugin.cs index 701d7416..e0bd67e3 100644 --- a/src/Plugins/BotSharp.Plugin.AnthropicAI/AnthropicPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.AnthropicAI/AnthropicPlugin.cs @@ -7,6 +7,9 @@ namespace BotSharp.Plugin.AnthropicAI; public class AnthropicPlugin : IBotSharpPlugin { public string Id => "012119da-8367-4be8-9a75-ab6ae55071e6"; + public string Name => "Anthropic AI"; + public string Description => "Anthropic is an AI safety and research company"; + public string? IconUrl => "https://www.anthropic.com/images/icons/safari-pinned-tab.svg"; public void RegisterDI(IServiceCollection services, IConfiguration config) { From 67aa9ed0ae1fa2d18dae901eb34e0184c38f0b25 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 6 May 2024 01:51:59 -0500 Subject: [PATCH 083/201] refine file storage --- .../IBotSharpFileService.cs} | 8 +- .../Files/Models/BotSharpFile.cs | 4 - .../Conversations/ConversationPlugin.cs | 4 +- .../Services/ConversationStorage.cs | 5 +- .../BotSharpFileService.cs} | 110 ++++++++---------- src/Infrastructure/BotSharp.Core/Using.cs | 1 + .../Controllers/ConversationController.cs | 15 +-- 7 files changed, 70 insertions(+), 77 deletions(-) rename src/Infrastructure/BotSharp.Abstraction/{Conversations/IConversationAttachmentService.cs => Files/IBotSharpFileService.cs} (52%) rename src/Infrastructure/BotSharp.Core/{Conversations/Services/ConversationAttachmentService.cs => Files/BotSharpFileService.cs} (61%) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationAttachmentService.cs b/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs similarity index 52% rename from src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationAttachmentService.cs rename to src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs index 848cb474..af1e5a9f 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationAttachmentService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs @@ -1,9 +1,9 @@ -namespace BotSharp.Abstraction.Conversations; +namespace BotSharp.Abstraction.Files; -public interface IConversationAttachmentService +public interface IBotSharpFileService { string GetDirectory(string conversationId); IEnumerable GetConversationFiles(string conversationId, string messageId); - string? GetMessageFile(string conversationId, string messageId, string fileType, int index); - void SaveConversationFiles(List files); + string? GetMessageFile(string conversationId, string messageId, string fileName, string fileType); + void SaveConversationFiles(string conversationId, List files); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs b/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs index acf47fbe..732085a7 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs @@ -3,9 +3,6 @@ namespace BotSharp.Abstraction.Files.Models; public class BotSharpFile { - [JsonPropertyName("conversation_id")] - public string ConversationId { get; set; } - [JsonPropertyName("message_id")] public string MessageId { get; set; } @@ -20,5 +17,4 @@ public class BotSharpFile [JsonPropertyName("file_size")] public int FileSize { get; set; } - } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs index 8fb6d9a5..6887622b 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs @@ -1,9 +1,11 @@ +using BotSharp.Abstraction.Files; using BotSharp.Abstraction.Instructs; using BotSharp.Abstraction.Messaging; using BotSharp.Abstraction.Plugins.Models; using BotSharp.Abstraction.Routing.Planning; using BotSharp.Abstraction.Settings; using BotSharp.Abstraction.Templating; +using BotSharp.Core.Files; using BotSharp.Core.Instructs; using BotSharp.Core.Messaging; using BotSharp.Core.Routing.Planning; @@ -35,7 +37,7 @@ public class ConversationPlugin : IBotSharpPlugin services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); + services.AddScoped(); services.AddScoped(); // Rich content messaging diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs index 13cbdedd..eacc7211 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.Files; using BotSharp.Abstraction.Messaging; using BotSharp.Abstraction.Messaging.Models.RichContent; using BotSharp.Abstraction.Options; @@ -25,7 +26,7 @@ public class ConversationStorage : IConversationStorage { var agentId = dialog.CurrentAgentId; var db = _services.GetRequiredService(); - var attachment = _services.GetRequiredService(); + var attachment = _services.GetRequiredService(); var dialogElements = new List(); // Prevent duplicate record to be inserted @@ -77,7 +78,7 @@ public class ConversationStorage : IConversationStorage } db.AppendConversationDialogs(conversationId, dialogElements); - attachment.SaveConversationFiles(dialog.Files); + attachment.SaveConversationFiles(conversationId, dialog.Files); dialog.Files.Clear(); } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationAttachmentService.cs b/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs similarity index 61% rename from src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationAttachmentService.cs rename to src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs index 22e1158c..b24a33a2 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationAttachmentService.cs +++ b/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs @@ -1,11 +1,9 @@ -using Microsoft.AspNetCore.Http; using System.IO; -using System.IO.Enumeration; using System.Threading; -namespace BotSharp.Core.Conversations.Services; +namespace BotSharp.Core.Files; -public class ConversationAttachmentService : IConversationAttachmentService +public class BotSharpFileService : IBotSharpFileService { private readonly BotSharpDatabaseSettings _dbSettings; private readonly IServiceProvider _services; @@ -13,9 +11,8 @@ public class ConversationAttachmentService : IConversationAttachmentService private const string CONVERSATION_FOLDER = "conversations"; private const string FILE_FOLDER = "files"; - private const string SEPARATOR = "."; - public ConversationAttachmentService( + public BotSharpFileService( BotSharpDatabaseSettings dbSettings, IServiceProvider services) { @@ -37,93 +34,100 @@ public class ConversationAttachmentService : IConversationAttachmentService public IEnumerable GetConversationFiles(string conversationId, string messageId) { var outputFiles = new List(); - if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId)) + var dir = GetConversationFileDirectory(conversationId, messageId); + if (string.IsNullOrEmpty(dir)) { return outputFiles; } - var context = _services.GetRequiredService(); - var request = context.HttpContext.Request; - var host = $"{request.Scheme}{Uri.SchemeDelimiter}{request.Host.Value}"; - var dir = GetConversationFileDirectory(conversationId); - foreach (var file in Directory.GetFiles(dir)) { - var fileName = file.Split(Path.DirectorySeparatorChar).Last(); - var splits = fileName.Split('.'); - var fileMsgId = splits.First(); - - if (fileMsgId != messageId) continue; - - var index = splits[1]; - var fileType = splits.Last(); + var fileName = Path.GetFileNameWithoutExtension(file); + var extension = Path.GetExtension(file); + var fileType = extension.Substring(1); var model = new OutputFileModel() { - FileUrl = $"{host}/conversation/{conversationId}/file/{messageId}/type/{fileType}/{index}", + FileUrl = $"/conversation/{conversationId}/message/{messageId}/file/{fileName}/type/{fileType}", FileName = fileName, - FileType = fileType + FileType = extension }; outputFiles.Add(model); } return outputFiles; } - public string? GetMessageFile(string conversationId, string messageId, string fileType, int index) + public string? GetMessageFile(string conversationId, string messageId, string fileName, string fileType) { - var targetFile = $"{messageId}{SEPARATOR}{index}.{fileType}"; - var dir = GetConversationFileDirectory(conversationId); - var files = Directory.GetFiles(dir); - var found = files.FirstOrDefault(f => + var dir = GetConversationFileDirectory(conversationId, messageId); + if (string.IsNullOrEmpty(dir)) { - var fileName = f.Split(Path.DirectorySeparatorChar).Last(); - return fileName.IsEqualTo(targetFile); - }); + return null; + } + var targetFile = $"{fileName}.{fileType}"; + var found = Directory.GetFiles(dir).FirstOrDefault(f => Path.GetFileName(f).IsEqualTo(targetFile)); return found; } - public void SaveConversationFiles(List files) + public void SaveConversationFiles(string conversationId, List files) { if (files.IsNullOrEmpty()) return; - var conversationId = files.First().ConversationId; - var dir = GetConversationFileDirectory(conversationId); + var messageId = files.FirstOrDefault()?.MessageId; + var dir = GetConversationFileDirectory(conversationId, messageId, createNewDir: true); + if (string.IsNullOrEmpty(dir)) return; for (int i = 0; i < files.Count; i++) { var file = files[i]; - if (string.IsNullOrEmpty(file.ConversationId) - || string.IsNullOrEmpty(file.MessageId) - || string.IsNullOrEmpty(file.FileData)) + if (string.IsNullOrEmpty(file.MessageId) || string.IsNullOrEmpty(file.FileData)) { continue; } - var fileType = GetFileType(file.FileData); var bytes = GetFileBytes(file.FileData); - var parsedFormat = ParseFileFormat(fileType); - if (string.IsNullOrEmpty(parsedFormat)) - { - continue; - } - - var fileName = $"{file.MessageId}{SEPARATOR}{i+1}{parsedFormat}"; + var fileType = Path.GetExtension(file.FileName); + var fileName = $"{i + 1}{fileType}"; Thread.Sleep(100); File.WriteAllBytes(Path.Combine(dir, fileName), bytes); } } #region Private methods - private string GetConversationFileDirectory(string conversationId) + private string GetConversationFileDirectory(string? conversationId, string? messageId, bool createNewDir = false) { - var dir = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, FILE_FOLDER); + if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId)) + { + return string.Empty; + } + + var dir = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, FILE_FOLDER, messageId); if (!Directory.Exists(dir)) { - Directory.CreateDirectory(dir); + if (createNewDir) + { + Directory.CreateDirectory(dir); + } + else + { + return string.Empty; + } } return dir; } + private byte[] GetFileBytes(string data) + { + if (string.IsNullOrEmpty(data)) + { + return new byte[0]; + } + + var startIdx = data.IndexOf(','); + var base64Str = data.Substring(startIdx + 1); + return Convert.FromBase64String(base64Str); + } + private string GetFileType(string data) { if (string.IsNullOrEmpty(data)) @@ -137,18 +141,6 @@ public class ConversationAttachmentService : IConversationAttachmentService return fileType; } - private byte[] GetFileBytes(string data) - { - if (string.IsNullOrEmpty(data)) - { - return new byte[0]; - } - - var startIdx = data.IndexOf(','); - var base64Str = data.Substring(startIdx + 1); - return Convert.FromBase64String(base64Str); - } - private string ParseFileFormat(string type) { var parsed = string.Empty; diff --git a/src/Infrastructure/BotSharp.Core/Using.cs b/src/Infrastructure/BotSharp.Core/Using.cs index 4478466d..2a911815 100644 --- a/src/Infrastructure/BotSharp.Core/Using.cs +++ b/src/Infrastructure/BotSharp.Core/Using.cs @@ -23,6 +23,7 @@ global using BotSharp.Abstraction.Functions.Models; global using BotSharp.Abstraction.Repositories; global using BotSharp.Abstraction.Repositories.Filters; global using BotSharp.Abstraction.Translation; +global using BotSharp.Abstraction.Files; global using BotSharp.Abstraction.Files.Models; global using BotSharp.Core.Repository; global using BotSharp.Core.Routing; diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index 5940bdbb..e7eaf3f0 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -3,6 +3,7 @@ using Newtonsoft.Json.Serialization; using Newtonsoft.Json; using BotSharp.Abstraction.Files.Models; using Microsoft.AspNetCore.Hosting; +using BotSharp.Abstraction.Files; namespace BotSharp.OpenAPI.Controllers; @@ -298,7 +299,7 @@ public class ConversationController : ControllerBase { if (files != null && files.Length > 0) { - var attachmentService = _services.GetRequiredService(); + var attachmentService = _services.GetRequiredService(); var dir = attachmentService.GetDirectory(conversationId); foreach (var file in files) { @@ -321,18 +322,18 @@ public class ConversationController : ControllerBase [HttpGet("/conversation/{conversationId}/files/{messageId}")] public IEnumerable GetConversationFiles([FromRoute] string conversationId, [FromRoute] string messageId) { - var attachment = _services.GetRequiredService(); + var attachment = _services.GetRequiredService(); return attachment.GetConversationFiles(conversationId, messageId); } [AllowAnonymous] - [HttpGet("/conversation/{conversationId}/file/{messageId}/type/{type}/{index}")] + [HttpGet("/conversation/{conversationId}/message/{messageId}/file/{fileName}/type/{type}")] public async Task GetMessageFile([FromRoute] string conversationId, [FromRoute] string messageId, - [FromRoute] string type, [FromRoute] int index, [FromQuery] string token) + [FromRoute] string fileName, [FromRoute] string type) { - var attachment = _services.GetRequiredService(); - var file = attachment.GetMessageFile(conversationId, messageId, type, index); - if (System.IO.File.Exists(file)) + var attachment = _services.GetRequiredService(); + var file = attachment.GetMessageFile(conversationId, messageId, fileName, type); + if (!string.IsNullOrEmpty(file)) { using Stream stream = System.IO.File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read); var bytes = new byte[stream.Length]; From 78fcda634b03be081a536621c347730c866099c2 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 6 May 2024 02:37:58 -0500 Subject: [PATCH 084/201] refine request path middleware --- .../Controllers/ConversationController.cs | 15 ++++++--------- .../WebSocketsMiddleware.cs | 6 ++++-- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index e7eaf3f0..082b594c 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -326,23 +326,20 @@ public class ConversationController : ControllerBase return attachment.GetConversationFiles(conversationId, messageId); } - [AllowAnonymous] [HttpGet("/conversation/{conversationId}/message/{messageId}/file/{fileName}/type/{type}")] public async Task GetMessageFile([FromRoute] string conversationId, [FromRoute] string messageId, [FromRoute] string fileName, [FromRoute] string type) { var attachment = _services.GetRequiredService(); var file = attachment.GetMessageFile(conversationId, messageId, fileName, type); - if (!string.IsNullOrEmpty(file)) - { - using Stream stream = System.IO.File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read); - var bytes = new byte[stream.Length]; - stream.Read(bytes, 0, (int)stream.Length); - return File(bytes, "application/octet-stream", Path.GetFileName(file)); - } - else + if (string.IsNullOrEmpty(file)) { return NotFound(); } + + using Stream stream = System.IO.File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read); + var bytes = new byte[stream.Length]; + stream.Read(bytes, 0, (int)stream.Length); + return File(bytes, "application/octet-stream", Path.GetFileName(file)); } } diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/WebSocketsMiddleware.cs b/src/Plugins/BotSharp.Plugin.ChatHub/WebSocketsMiddleware.cs index bd3e90aa..4194cc84 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/WebSocketsMiddleware.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/WebSocketsMiddleware.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Http; +using System.Text.RegularExpressions; namespace BotSharp.Plugin.ChatHub; @@ -13,11 +14,12 @@ public class WebSocketsMiddleware public async Task Invoke(HttpContext httpContext) { - var request = httpContext.Request; + var request = httpContext.Request;; + var messageFileRegex = new Regex(@"/conversation/[a-z0-9_.-]+/message/[a-z0-9_.-]+/file/[a-z0-9_.-]+/type/[a-z0-9_.-]+", RegexOptions.IgnoreCase); // web sockets cannot pass headers so we must take the access token from query param and // add it to the header before authentication middleware runs - if (request.Path.StartsWithSegments("/chatHub", StringComparison.OrdinalIgnoreCase) && + if ((request.Path.StartsWithSegments("/chatHub", StringComparison.OrdinalIgnoreCase) || messageFileRegex.IsMatch(request.Path.Value ?? string.Empty)) && request.Query.TryGetValue("access_token", out var accessToken)) { request.Headers["Authorization"] = $"Bearer {accessToken}"; From 8d90b3a7e2723dbfd61adb37ebf72b2cc95db398 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 6 May 2024 02:59:03 -0500 Subject: [PATCH 085/201] add file controller --- .../Services/ConversationStorage.cs | 4 +- .../Controllers/ConversationController.cs | 51 --------------- .../Controllers/FileController.cs | 63 +++++++++++++++++++ src/Infrastructure/BotSharp.OpenAPI/Using.cs | 2 + 4 files changed, 67 insertions(+), 53 deletions(-) create mode 100644 src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs index eacc7211..5490daae 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs @@ -26,7 +26,7 @@ public class ConversationStorage : IConversationStorage { var agentId = dialog.CurrentAgentId; var db = _services.GetRequiredService(); - var attachment = _services.GetRequiredService(); + var fileService = _services.GetRequiredService(); var dialogElements = new List(); // Prevent duplicate record to be inserted @@ -78,7 +78,7 @@ public class ConversationStorage : IConversationStorage } db.AppendConversationDialogs(conversationId, dialogElements); - attachment.SaveConversationFiles(conversationId, dialog.Files); + fileService.SaveConversationFiles(conversationId, dialog.Files); dialog.Files.Clear(); } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index 082b594c..af7c44b3 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -2,7 +2,6 @@ using BotSharp.Abstraction.Routing; using Newtonsoft.Json.Serialization; using Newtonsoft.Json; using BotSharp.Abstraction.Files.Models; -using Microsoft.AspNetCore.Hosting; using BotSharp.Abstraction.Files; namespace BotSharp.OpenAPI.Controllers; @@ -292,54 +291,4 @@ public class ConversationController : ControllerBase buffer = Encoding.UTF8.GetBytes("\n"); await response.Body.WriteAsync(buffer, 0, buffer.Length); } - - [HttpPost("/conversation/{conversationId}/attachments")] - public IActionResult UploadAttachments([FromRoute] string conversationId, - IFormFile[] files) - { - if (files != null && files.Length > 0) - { - var attachmentService = _services.GetRequiredService(); - var dir = attachmentService.GetDirectory(conversationId); - foreach (var file in files) - { - // Save the file, process it, etc. - var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"'); - var filePath = Path.Combine(dir, fileName); - - using (var stream = new FileStream(filePath, FileMode.Create)) - { - file.CopyTo(stream); - } - } - - return Ok(new { message = "File uploaded successfully." }); - } - - return BadRequest(new { message = "Invalid file." }); - } - - [HttpGet("/conversation/{conversationId}/files/{messageId}")] - public IEnumerable GetConversationFiles([FromRoute] string conversationId, [FromRoute] string messageId) - { - var attachment = _services.GetRequiredService(); - return attachment.GetConversationFiles(conversationId, messageId); - } - - [HttpGet("/conversation/{conversationId}/message/{messageId}/file/{fileName}/type/{type}")] - public async Task GetMessageFile([FromRoute] string conversationId, [FromRoute] string messageId, - [FromRoute] string fileName, [FromRoute] string type) - { - var attachment = _services.GetRequiredService(); - var file = attachment.GetMessageFile(conversationId, messageId, fileName, type); - if (string.IsNullOrEmpty(file)) - { - return NotFound(); - } - - using Stream stream = System.IO.File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read); - var bytes = new byte[stream.Length]; - stream.Read(bytes, 0, (int)stream.Length); - return File(bytes, "application/octet-stream", Path.GetFileName(file)); - } } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs new file mode 100644 index 00000000..84bb65b0 --- /dev/null +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs @@ -0,0 +1,63 @@ +namespace BotSharp.OpenAPI.Controllers; + +[Authorize] +[ApiController] +public class FileController : ControllerBase +{ + private readonly IServiceProvider _services; + + public FileController(IServiceProvider services) + { + _services = services; + } + + [HttpPost("/conversation/{conversationId}/attachments")] + public IActionResult UploadAttachments([FromRoute] string conversationId, + IFormFile[] files) + { + if (files != null && files.Length > 0) + { + var fileService = _services.GetRequiredService(); + var dir = fileService.GetDirectory(conversationId); + foreach (var file in files) + { + // Save the file, process it, etc. + var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"'); + var filePath = Path.Combine(dir, fileName); + + using (var stream = new FileStream(filePath, FileMode.Create)) + { + file.CopyTo(stream); + } + } + + return Ok(new { message = "File uploaded successfully." }); + } + + return BadRequest(new { message = "Invalid file." }); + } + + [HttpGet("/conversation/{conversationId}/files/{messageId}")] + public IEnumerable GetConversationFiles([FromRoute] string conversationId, [FromRoute] string messageId) + { + var fileService = _services.GetRequiredService(); + return fileService.GetConversationFiles(conversationId, messageId); + } + + [HttpGet("/conversation/{conversationId}/message/{messageId}/file/{fileName}/type/{type}")] + public async Task GetMessageFile([FromRoute] string conversationId, [FromRoute] string messageId, + [FromRoute] string fileName, [FromRoute] string type) + { + var fileService = _services.GetRequiredService(); + var file = fileService.GetMessageFile(conversationId, messageId, fileName, type); + if (string.IsNullOrEmpty(file)) + { + return NotFound(); + } + + using Stream stream = System.IO.File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read); + var bytes = new byte[stream.Length]; + stream.Read(bytes, 0, (int)stream.Length); + return File(bytes, "application/octet-stream", Path.GetFileName(file)); + } +} diff --git a/src/Infrastructure/BotSharp.OpenAPI/Using.cs b/src/Infrastructure/BotSharp.OpenAPI/Using.cs index 201bf7b1..f3ba775f 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Using.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Using.cs @@ -24,6 +24,8 @@ global using BotSharp.Abstraction.Conversations.Enums; global using BotSharp.Abstraction.Conversations.Models; global using BotSharp.Abstraction.Models; global using BotSharp.Abstraction.Repositories.Filters; +global using BotSharp.Abstraction.Files.Models; +global using BotSharp.Abstraction.Files; global using BotSharp.OpenAPI.ViewModels.Conversations; global using BotSharp.OpenAPI.ViewModels.Users; global using BotSharp.OpenAPI.ViewModels.Agents; \ No newline at end of file From 2a31af5911e03064ca08db05cbef0887a6568905 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 6 May 2024 17:31:52 -0500 Subject: [PATCH 086/201] refine file service --- .../Conversations/IConversationService.cs | 10 ++- .../Conversations/Models/RoleDialogModel.cs | 1 + .../Files/IBotSharpFileService.cs | 15 +++- .../Files/Models/BotSharpFile.cs | 3 - .../Repositories/IBotSharpRepository.cs | 2 +- .../ConversationService.SendMessage.cs | 14 ++-- .../ConversationService.TruncateMessage.cs | 10 ++- .../Services/ConversationService.cs | 2 + .../Services/ConversationStorage.cs | 4 -- .../Files/BotSharpFileService.cs | 70 ++++++++++++++++--- .../Repository/BotSharpDbContext.cs | 2 +- .../FileRepository.Conversation.cs | 30 ++++++-- .../Controllers/ConversationController.cs | 17 +++-- .../Controllers/FileController.cs | 7 +- .../WebSocketsMiddleware.cs | 5 +- .../MongoRepository.Conversation.cs | 24 +++++-- 16 files changed, 159 insertions(+), 57 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs index d0a95ef8..9df6ae7f 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs @@ -15,7 +15,15 @@ public interface IConversationService Task> GetLastConversations(); Task> GetIdleConversations(int batchSize, int messageLimit, int bufferHours); Task DeleteConversations(IEnumerable ids); - Task TruncateConversation(string conversationId, string messageId); + + /// + /// Truncate conversation + /// + /// Target conversation id + /// Target message id to delete + /// If not null, delete messages while input a new message; otherwise delete messages only + /// + Task TruncateConversation(string conversationId, string messageId, string? newMessageId = null); Task> GetConversationContentLogs(string conversationId); Task> GetConversationStateLogs(string conversationId); diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs index 8085a2c5..a8e9bc67 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs @@ -86,6 +86,7 @@ public class RoleDialogModel : ITrackableMessage Role = role; Content = text; MessageId = Guid.NewGuid().ToString(); + CreatedAt = DateTime.UtcNow; } public override string ToString() diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs b/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs index af1e5a9f..9c27d7ff 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs @@ -4,6 +4,17 @@ public interface IBotSharpFileService { string GetDirectory(string conversationId); IEnumerable GetConversationFiles(string conversationId, string messageId); - string? GetMessageFile(string conversationId, string messageId, string fileName, string fileType); - void SaveConversationFiles(string conversationId, List files); + string? GetMessageFile(string conversationId, string messageId, string fileName); + void SaveMessageFiles(string conversationId, string messageId, List files); + + /// + /// Delete files under messages + /// + /// Conversation Id + /// Files in these messages will be deleted + /// The starting message to delete + /// If not null, delete messages while input a new message; otherwise, delete messages only + /// + bool DeleteMessageFiles(string conversationId, IEnumerable messageIds, string targetMessageId, string? newMessageId = null); + bool DeleteConversationFiles(IEnumerable conversationIds); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs b/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs index 732085a7..f679d52e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs @@ -3,9 +3,6 @@ namespace BotSharp.Abstraction.Files.Models; public class BotSharpFile { - [JsonPropertyName("message_id")] - public string MessageId { get; set; } - [JsonPropertyName("file_name")] public string FileName { get; set; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index a1124975..6c488385 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -63,7 +63,7 @@ public interface IBotSharpRepository ConversationBreakpoint? GetConversationBreakpoint(string conversationId); List GetLastConversations(); List GetIdleConversations(int batchSize, int messageLimit, int bufferHours); - bool TruncateConversation(string conversationId, string messageId, bool cleanLog = false); + IEnumerable TruncateConversation(string conversationId, string messageId, bool cleanLog = false); #endregion #region Execution Log diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs index dbfdf7dc..f8460e3a 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -1,5 +1,4 @@ using BotSharp.Abstraction.Messaging; -using BotSharp.Abstraction.Messaging.Enums; using BotSharp.Abstraction.Messaging.Models.RichContent; using BotSharp.Abstraction.Routing.Settings; using System.Drawing; @@ -28,7 +27,6 @@ public partial class ConversationService #endif message.CurrentAgentId = agent.Id; - message.CreatedAt = DateTime.UtcNow; if (string.IsNullOrEmpty(message.SenderId)) { message.SenderId = _user.Id; @@ -48,6 +46,11 @@ public partial class ConversationService routing.Context.SetMessageId(_conversationId, message.MessageId); routing.Context.Push(agent.Id); + // Save message files + var fileService = _services.GetRequiredService(); + fileService.SaveMessageFiles(_conversationId, message.MessageId, message.Files); + message.Files?.Clear(); + // Before chat completion hook foreach (var hook in hooks) { @@ -142,13 +145,6 @@ public partial class ConversationService Message = new TextMessage(response.SecondaryContent ?? response.Content) }; - response.RichContent = new RichContent - { - Recipient = new Recipient { Id = state.GetConversationId() }, - Editor = EditorTypeEnum.File, - Message = new TextMessage(response.SecondaryContent ?? response.Content) - }; - // Patch return function name if (response.PostbackFunctionName != null) { diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.TruncateMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.TruncateMessage.cs index e4b81f31..451cdeed 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.TruncateMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.TruncateMessage.cs @@ -2,15 +2,19 @@ namespace BotSharp.Core.Conversations.Services; public partial class ConversationService : IConversationService { - public async Task TruncateConversation(string conversationId, string messageId) + public async Task TruncateConversation(string conversationId, string messageId, string? newMessageId = null) { var db = _services.GetRequiredService(); - var isSaved = db.TruncateConversation(conversationId, messageId, true); + var fileService = _services.GetRequiredService(); + var deleteMessageIds = db.TruncateConversation(conversationId, messageId, cleanLog: true); + + fileService.DeleteMessageFiles(conversationId, deleteMessageIds, messageId, newMessageId); + var hooks = _services.GetServices().ToList(); foreach (var hook in hooks) { await hook.OnMessageDeleted(conversationId, messageId); } - return await Task.FromResult(isSaved); + return await Task.FromResult(true); } } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs index 72b6cde0..4de36b9d 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs @@ -35,7 +35,9 @@ public partial class ConversationService : IConversationService public async Task DeleteConversations(IEnumerable ids) { var db = _services.GetRequiredService(); + var fileService = _services.GetRequiredService(); var isDeleted = db.DeleteConversations(ids); + fileService.DeleteConversationFiles(ids); return await Task.FromResult(isDeleted); } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs index 5490daae..4ab447b8 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Files; using BotSharp.Abstraction.Messaging; using BotSharp.Abstraction.Messaging.Models.RichContent; using BotSharp.Abstraction.Options; @@ -26,7 +25,6 @@ public class ConversationStorage : IConversationStorage { var agentId = dialog.CurrentAgentId; var db = _services.GetRequiredService(); - var fileService = _services.GetRequiredService(); var dialogElements = new List(); // Prevent duplicate record to be inserted @@ -78,8 +76,6 @@ public class ConversationStorage : IConversationStorage } db.AppendConversationDialogs(conversationId, dialogElements); - fileService.SaveConversationFiles(conversationId, dialog.Files); - dialog.Files.Clear(); } public List GetDialogs(string conversationId) diff --git a/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs b/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs index b24a33a2..ad687274 100644 --- a/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs +++ b/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs @@ -47,16 +47,16 @@ public class BotSharpFileService : IBotSharpFileService var fileType = extension.Substring(1); var model = new OutputFileModel() { - FileUrl = $"/conversation/{conversationId}/message/{messageId}/file/{fileName}/type/{fileType}", + FileUrl = $"/conversation/{conversationId}/message/{messageId}/file/{fileName}", FileName = fileName, - FileType = extension + FileType = fileType }; outputFiles.Add(model); } return outputFiles; } - public string? GetMessageFile(string conversationId, string messageId, string fileName, string fileType) + public string? GetMessageFile(string conversationId, string messageId, string fileName) { var dir = GetConversationFileDirectory(conversationId, messageId); if (string.IsNullOrEmpty(dir)) @@ -64,23 +64,21 @@ public class BotSharpFileService : IBotSharpFileService return null; } - var targetFile = $"{fileName}.{fileType}"; - var found = Directory.GetFiles(dir).FirstOrDefault(f => Path.GetFileName(f).IsEqualTo(targetFile)); + var found = Directory.GetFiles(dir).FirstOrDefault(f => Path.GetFileNameWithoutExtension(f).IsEqualTo(fileName)); return found; } - public void SaveConversationFiles(string conversationId, List files) + public void SaveMessageFiles(string conversationId, string messageId, List files) { if (files.IsNullOrEmpty()) return; - var messageId = files.FirstOrDefault()?.MessageId; var dir = GetConversationFileDirectory(conversationId, messageId, createNewDir: true); if (string.IsNullOrEmpty(dir)) return; for (int i = 0; i < files.Count; i++) { var file = files[i]; - if (string.IsNullOrEmpty(file.MessageId) || string.IsNullOrEmpty(file.FileData)) + if (string.IsNullOrEmpty(file.FileData)) { continue; } @@ -93,6 +91,52 @@ public class BotSharpFileService : IBotSharpFileService } } + public bool DeleteMessageFiles(string conversationId, IEnumerable messageIds, string targetMessageId, string? newMessageId = null) + { + if (string.IsNullOrEmpty(conversationId) || messageIds == null) return false; + + if (!string.IsNullOrEmpty(targetMessageId) && !string.IsNullOrEmpty(newMessageId)) + { + var prevDir = GetConversationFileDirectory(conversationId, targetMessageId); + var newDir = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, FILE_FOLDER, newMessageId); + + if (Directory.Exists(prevDir)) + { + if (Directory.Exists(newDir)) + { + Directory.Delete(newDir, true); + } + + Directory.Move(prevDir, newDir); + } + } + + foreach ( var messageId in messageIds) + { + var dir = GetConversationFileDirectory(conversationId, messageId); + if (string.IsNullOrEmpty(dir)) continue; + + Thread.Sleep(100); + Directory.Delete(dir, true); + } + + return true; + } + + public bool DeleteConversationFiles(IEnumerable conversationIds) + { + if (conversationIds.IsNullOrEmpty()) return false; + + foreach (var conversationId in conversationIds) + { + var convDir = FindConversationDirectory(conversationId); + if (string.IsNullOrEmpty(convDir)) continue; + + Directory.Delete(convDir, true); + } + return true; + } + #region Private methods private string GetConversationFileDirectory(string? conversationId, string? messageId, bool createNewDir = false) { @@ -116,6 +160,16 @@ public class BotSharpFileService : IBotSharpFileService return dir; } + private string? FindConversationDirectory(string conversationId) + { + if (string.IsNullOrEmpty(conversationId)) return null; + + var dir = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId); + if (!Directory.Exists(dir)) return null; + + return dir; + } + private byte[] GetFileBytes(string data) { if (string.IsNullOrEmpty(data)) diff --git a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs index 0605c525..ed1d297d 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs @@ -172,7 +172,7 @@ public class BotSharpDbContext : Database, IBotSharpRepository public void UpdateConversationStatus(string conversationId, string status) => new NotImplementedException(); - public bool TruncateConversation(string conversationId, string messageId, bool cleanLog = false) + public IEnumerable TruncateConversation(string conversationId, string messageId, bool cleanLog = false) => throw new NotImplementedException(); #endregion diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs index 9a386e92..d9400e47 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs @@ -446,24 +446,40 @@ namespace BotSharp.Core.Repository } - public bool TruncateConversation(string conversationId, string messageId, bool cleanLog = false) + public IEnumerable TruncateConversation(string conversationId, string messageId, bool cleanLog = false) { - if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId)) return false; + var deletedMessageIds = new List(); + if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId)) + { + return deletedMessageIds; + } var dialogs = new List(); + var convDir = FindConversationDirectory(conversationId); - if (string.IsNullOrEmpty(convDir)) return false; + if (string.IsNullOrEmpty(convDir)) + { + return deletedMessageIds; + } var dialogDir = Path.Combine(convDir, DIALOG_FILE); dialogs = CollectDialogElements(dialogDir); - if (dialogs.IsNullOrEmpty()) return false; + if (dialogs.IsNullOrEmpty()) + { + return deletedMessageIds; + } var foundIdx = dialogs.FindIndex(x => x.MetaData?.MessageId == messageId); - if (foundIdx < 0) return false; + if (foundIdx < 0) + { + return deletedMessageIds; + } + + deletedMessageIds = dialogs.Where((x, idx) => idx >= foundIdx && !string.IsNullOrEmpty(x.MetaData?.MessageId)) + .Select(x => x.MetaData.MessageId).Distinct().ToList(); // Handle truncated dialogs var isSaved = HandleTruncatedDialogs(convDir, dialogDir, dialogs, foundIdx); - if (!isSaved) return false; // Handle truncated states var refTime = dialogs.ElementAt(foundIdx).MetaData.CreateTime; @@ -482,7 +498,7 @@ namespace BotSharp.Core.Repository HandleTruncatedLogs(convDir, refTime); } - return isSaved; + return deletedMessageIds; } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index af7c44b3..c85b8e43 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -168,15 +168,15 @@ public class ConversationController : ControllerBase [FromBody] NewMessageModel input) { var conv = _services.GetRequiredService(); - if (!string.IsNullOrEmpty(input.TruncateMessageId)) - { - await conv.TruncateConversation(conversationId, input.TruncateMessageId); - } - var inputMsg = new RoleDialogModel(AgentRole.User, input.Text) { Files = input.Files }; + if (!string.IsNullOrEmpty(input.TruncateMessageId)) + { + await conv.TruncateConversation(conversationId, input.TruncateMessageId, inputMsg.MessageId); + } + var routing = _services.GetRequiredService(); routing.Context.SetMessageId(conversationId, inputMsg.MessageId); @@ -212,12 +212,15 @@ public class ConversationController : ControllerBase [FromBody] NewMessageModel input) { var conv = _services.GetRequiredService(); + var inputMsg = new RoleDialogModel(AgentRole.User, input.Text) + { + Files = input.Files + }; if (!string.IsNullOrEmpty(input.TruncateMessageId)) { - await conv.TruncateConversation(conversationId, input.TruncateMessageId); + await conv.TruncateConversation(conversationId, input.TruncateMessageId, inputMsg.MessageId); } - var inputMsg = new RoleDialogModel(AgentRole.User, input.Text); var routing = _services.GetRequiredService(); routing.Context.SetMessageId(conversationId, inputMsg.MessageId); diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs index 84bb65b0..a24357a2 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs @@ -44,12 +44,11 @@ public class FileController : ControllerBase return fileService.GetConversationFiles(conversationId, messageId); } - [HttpGet("/conversation/{conversationId}/message/{messageId}/file/{fileName}/type/{type}")] - public async Task GetMessageFile([FromRoute] string conversationId, [FromRoute] string messageId, - [FromRoute] string fileName, [FromRoute] string type) + [HttpGet("/conversation/{conversationId}/message/{messageId}/file/{fileName}")] + public async Task GetMessageFile([FromRoute] string conversationId, [FromRoute] string messageId, [FromRoute] string fileName) { var fileService = _services.GetRequiredService(); - var file = fileService.GetMessageFile(conversationId, messageId, fileName, type); + var file = fileService.GetMessageFile(conversationId, messageId, fileName); if (string.IsNullOrEmpty(file)) { return NotFound(); diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/WebSocketsMiddleware.cs b/src/Plugins/BotSharp.Plugin.ChatHub/WebSocketsMiddleware.cs index 4194cc84..e20f8602 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/WebSocketsMiddleware.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/WebSocketsMiddleware.cs @@ -15,11 +15,12 @@ public class WebSocketsMiddleware public async Task Invoke(HttpContext httpContext) { var request = httpContext.Request;; - var messageFileRegex = new Regex(@"/conversation/[a-z0-9_.-]+/message/[a-z0-9_.-]+/file/[a-z0-9_.-]+/type/[a-z0-9_.-]+", RegexOptions.IgnoreCase); + var messageFileRegex = new Regex(@"/conversation/[a-z0-9-]+/message/[a-z0-9-]+/file/[a-z0-9-]+", RegexOptions.IgnoreCase); // web sockets cannot pass headers so we must take the access token from query param and // add it to the header before authentication middleware runs - if ((request.Path.StartsWithSegments("/chatHub", StringComparison.OrdinalIgnoreCase) || messageFileRegex.IsMatch(request.Path.Value ?? string.Empty)) && + if ((request.Path.StartsWithSegments("/chatHub", StringComparison.OrdinalIgnoreCase) + || messageFileRegex.IsMatch(request.Path.Value ?? string.Empty)) && request.Query.TryGetValue("access_token", out var accessToken)) { request.Headers["Authorization"] = $"Bearer {accessToken}"; diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs index ed15803e..235be196 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Conversations.Models; +using BotSharp.Abstraction.Files; using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Repositories.Models; using BotSharp.Plugin.MongoStorage.Collections; @@ -411,16 +412,29 @@ public partial class MongoRepository return conversationIds.Take(batchSize).ToList(); } - public bool TruncateConversation(string conversationId, string messageId, bool cleanLog = false) + public IEnumerable TruncateConversation(string conversationId, string messageId, bool cleanLog = false) { - if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId)) return false; + var deletedMessageIds = new List(); + if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId)) + { + return deletedMessageIds; + } var dialogFilter = Builders.Filter.Eq(x => x.ConversationId, conversationId); var foundDialog = _dc.ConversationDialogs.Find(dialogFilter).FirstOrDefault(); - if (foundDialog == null || foundDialog.Dialogs.IsNullOrEmpty()) return false; + if (foundDialog == null || foundDialog.Dialogs.IsNullOrEmpty()) + { + return deletedMessageIds; + } var foundIdx = foundDialog.Dialogs.FindIndex(x => x.MetaData?.MessageId == messageId); - if (foundIdx < 0) return false; + if (foundIdx < 0) + { + return deletedMessageIds; + } + + deletedMessageIds = foundDialog.Dialogs.Where((x, idx) => idx >= foundIdx && !string.IsNullOrEmpty(x.MetaData?.MessageId)) + .Select(x => x.MetaData.MessageId).Distinct().ToList(); // Handle truncated dialogs var truncatedDialogs = foundDialog.Dialogs.Where((x, idx) => idx < foundIdx).ToList(); @@ -499,6 +513,6 @@ public partial class MongoRepository _dc.StateLogs.DeleteMany(stateLogBuilder.And(stateLogFilters)); } - return true; + return deletedMessageIds; } } From 6ccf4afca7802eb25388c095fc68ac2f40ba1b63 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 6 May 2024 17:35:41 -0500 Subject: [PATCH 087/201] minor change --- src/WebStarter/WebStarter.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/WebStarter/WebStarter.csproj b/src/WebStarter/WebStarter.csproj index ad08c13c..1eb473c5 100644 --- a/src/WebStarter/WebStarter.csproj +++ b/src/WebStarter/WebStarter.csproj @@ -38,7 +38,6 @@ - From 91093f6a04131466c983b5051f3334d3db171bd3 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 7 May 2024 11:55:44 -0500 Subject: [PATCH 088/201] add payload --- .../Conversations/Models/Conversation.cs | 4 ++- .../Conversations/Models/RoleDialogModel.cs | 4 +++ .../Services/ConversationStorage.cs | 20 +++++++++-- .../FileRepository.Conversation.cs | 34 ++++++++++++------- .../Controllers/ConversationController.cs | 3 +- .../Conversations/ChatResponseModel.cs | 4 +++ .../Models/DialogMongoElement.cs | 7 ++-- 7 files changed, 58 insertions(+), 18 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs index c99a819b..5613c088 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs @@ -37,6 +37,7 @@ public class DialogElement public string? SecondaryContent { get; set; } public string? RichContent { get; set; } public string? SecondaryRichContent { get; set; } + public string? Payload { get; set; } public DialogElement() { @@ -44,13 +45,14 @@ public class DialogElement } public DialogElement(DialogMetaData meta, string content, string? richContent = null, - string? secondaryContent = null, string? secondaryRichContent = null) + string? secondaryContent = null, string? secondaryRichContent = null, string? payload = null) { MetaData = meta; Content = content; RichContent = richContent; SecondaryContent = secondaryContent; SecondaryRichContent = secondaryRichContent; + Payload = payload; } public override string ToString() diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs index c3a6403a..e019eae4 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs @@ -28,6 +28,10 @@ public class RoleDialogModel : ITrackableMessage public string? SecondaryContent { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("payload")] + public string? Payload { get; set; } + /// /// Indicator message used to provide UI feedback for function execution /// diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs index 4ab447b8..9bb8f103 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs @@ -50,7 +50,13 @@ public class ConversationStorage : IConversationStorage { return; } - dialogElements.Add(new DialogElement(meta, content, dialog.SecondaryContent)); + dialogElements.Add(new DialogElement + { + MetaData = meta, + Content = dialog.Content, + SecondaryContent = dialog.SecondaryContent, + Payload = dialog.Payload + }); } else { @@ -72,7 +78,15 @@ public class ConversationStorage : IConversationStorage var richContent = dialog.RichContent != null ? JsonSerializer.Serialize(dialog.RichContent, _options.JsonSerializerOptions) : null; var secondaryRichContent = dialog.SecondaryRichContent != null ? JsonSerializer.Serialize(dialog.SecondaryRichContent, _options.JsonSerializerOptions) : null; - dialogElements.Add(new DialogElement(meta, content, richContent, dialog.SecondaryContent, secondaryRichContent)); + dialogElements.Add(new DialogElement + { + MetaData = meta, + Content = dialog.Content, + SecondaryContent = dialog.SecondaryContent, + RichContent = richContent, + SecondaryRichContent = secondaryRichContent, + Payload = dialog.Payload + }); } db.AppendConversationDialogs(conversationId, dialogElements); @@ -90,6 +104,7 @@ public class ConversationStorage : IConversationStorage var meta = dialog.MetaData; var content = dialog.Content; var secondaryContent = dialog.SecondaryContent; + var payload = dialog.Payload; var role = meta.Role; var currentAgentId = meta.AgentId; var messageId = meta.MessageId; @@ -111,6 +126,7 @@ public class ConversationStorage : IConversationStorage RichContent = richContent, SecondaryContent = secondaryContent, SecondaryRichContent = secondaryRichContent, + Payload = payload }; results.Add(record); diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs index d9400e47..6708f49a 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs @@ -529,6 +529,7 @@ namespace BotSharp.Core.Repository var trimmedContent = content.Substring(4); var secondaryContent = rawDialogs[i + 4]; var trimmedSecondaryContent = string.IsNullOrEmpty(secondaryContent) ? null : secondaryContent.Substring(4); + var payload = blocks.Count() > 6 ? blocks[6] : null; var meta = new DialogMetaData { @@ -540,9 +541,17 @@ namespace BotSharp.Core.Repository CreateTime = DateTime.Parse(blocks[0]) }; - var richContent = DecodeRichContent(rawDialogs[i + 1]); - var secondaryRichContent = DecodeRichContent(rawDialogs[i + 3]); - dialogs.Add(new DialogElement(meta, trimmedContent, richContent, trimmedSecondaryContent, secondaryRichContent)); + var richContent = DecodeText(rawDialogs[i + 1]); + var secondaryRichContent = DecodeText(rawDialogs[i + 3]); + dialogs.Add(new DialogElement + { + MetaData = meta, + Content = trimmedContent, + SecondaryContent = trimmedSecondaryContent, + RichContent = richContent, + SecondaryRichContent = secondaryRichContent, + Payload = payload + }); } } return dialogs; @@ -557,9 +566,10 @@ 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 encodedSecondaryRichContent = EncodeRichContent(element.SecondaryRichContent); - var metaStr = $"{createTime}|{meta.Role}|{meta.AgentId}|{meta.MessageId}|{meta.SenderId}|{meta.FunctionName}"; + var encodedRichContent = EncodeText(element.RichContent); + var encodedSecondaryRichContent = EncodeText(element.SecondaryRichContent); + var payload = element.Payload; + var metaStr = $"{createTime}|{meta.Role}|{meta.AgentId}|{meta.MessageId}|{meta.SenderId}|{meta.FunctionName}|{payload}"; dialogTexts.Add(metaStr); dialogTexts.Add(encodedRichContent); @@ -715,20 +725,20 @@ namespace BotSharp.Core.Repository return true; } - private string? EncodeRichContent(string? content) + private string? EncodeText(string? text) { - if (string.IsNullOrEmpty(content)) return content; + if (string.IsNullOrEmpty(text)) return text; - var bytes = Encoding.UTF8.GetBytes(content); + var bytes = Encoding.UTF8.GetBytes(text); var encoded = Convert.ToBase64String(bytes); return encoded; } - private string? DecodeRichContent(string? content) + private string? DecodeText(string? text) { - if (string.IsNullOrEmpty(content)) return content; + if (string.IsNullOrEmpty(text)) return text; - var decoded = Convert.FromBase64String(content); + var decoded = Convert.FromBase64String(text); var origin = Encoding.UTF8.GetString(decoded); return origin; } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index c85b8e43..8d91d111 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -89,7 +89,8 @@ public class ConversationController : ControllerBase CreatedAt = message.CreatedAt, Text = !string.IsNullOrEmpty(message.SecondaryContent) ? message.SecondaryContent : message.Content, Data = message.Data, - Sender = UserViewModel.FromUser(user) + Sender = UserViewModel.FromUser(user), + Payload = message.Payload }); } else if (message.Role == AgentRole.Assistant) diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ChatResponseModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ChatResponseModel.cs index cf71b8f9..a03f1211 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ChatResponseModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ChatResponseModel.cs @@ -28,6 +28,10 @@ public class ChatResponseModel : InstructResult [JsonPropertyName("rich_content")] public RichContent? RichContent { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("payload")] + public string? Payload { 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 6ebc16ff..5c2a1698 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/DialogMongoElement.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/DialogMongoElement.cs @@ -9,6 +9,7 @@ public class DialogMongoElement public string? SecondaryContent { get; set; } public string? RichContent { get; set; } public string? SecondaryRichContent { get; set; } + public string? Payload { get; set; } public DialogMongoElement() { @@ -23,7 +24,8 @@ public class DialogMongoElement Content = dialog.Content, SecondaryContent = dialog.SecondaryContent, RichContent = dialog.RichContent, - SecondaryRichContent = dialog.SecondaryRichContent + SecondaryRichContent = dialog.SecondaryRichContent, + Payload = dialog.Payload }; } @@ -35,7 +37,8 @@ public class DialogMongoElement Content = dialog.Content, SecondaryContent = dialog.SecondaryContent, RichContent = dialog.RichContent, - SecondaryRichContent = dialog.SecondaryRichContent + SecondaryRichContent = dialog.SecondaryRichContent, + Payload = dialog.Payload }; } } From c0523a118fa6a67ee7508787882efd7efd2d1918 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Tue, 7 May 2024 12:19:55 -0500 Subject: [PATCH 089/201] Fallback Agent. --- .../BotSharp.Core/BotSharp.Core.csproj | 8 ++++ .../Handlers/ResponseToUserRoutingHandler.cs | 46 ------------------- .../agent.json | 11 +++++ .../instruction.liquid | 1 + .../instruction.liquid | 10 ++-- 5 files changed, 24 insertions(+), 52 deletions(-) delete mode 100644 src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs create mode 100644 src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d/agent.json create mode 100644 src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d/instruction.liquid diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index e091f052..8d05c40b 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -49,6 +49,8 @@ + + @@ -80,6 +82,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs deleted file mode 100644 index 81030223..00000000 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs +++ /dev/null @@ -1,46 +0,0 @@ -using BotSharp.Abstraction.Routing.Settings; - -namespace BotSharp.Core.Routing.Handlers; - -public class ResponseToUserRoutingHandler : RoutingHandlerBase, IRoutingHandler -{ - public string Name => "response_to_user"; - - public string Description => "When you can handle the conversation without asking specific agent."; - - public List Parameters => new List - { - new ParameterPropertyDef("reason", - "why response to user directly without go to other agents."), - new ParameterPropertyDef("response", - "response content to user in courteous words with language English. If the user wants to end the conversation, you must set conversation_end to true and response politely."), - new ParameterPropertyDef("conversation_end", - "whether to end this conversation.", - type: "boolean"), - new ParameterPropertyDef("task_completed ", - "whether the user's task request has been completed.", - type: "boolean"), - new ParameterPropertyDef("language", - "User preferred language, considering the whole conversation. Language could be English, Spanish or Chinese.", - required: true) - }; - - public ResponseToUserRoutingHandler(IServiceProvider services, ILogger logger, RoutingSettings settings) - : base(services, logger, settings) - { - } - - public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message, Func onFunctionExecuting) - { - var response = new RoleDialogModel(AgentRole.Assistant, inst.Response) - { - CurrentAgentId = message.CurrentAgentId, - MessageId = message.MessageId, - StopCompletion = true - }; - - _dialogs.Add(response); - - return true; - } -} diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d/agent.json b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d/agent.json new file mode 100644 index 00000000..a7fd580e --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d/agent.json @@ -0,0 +1,11 @@ +{ + "id": "01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d", + "name": "Fallback Agent", + "description": "Don't have sufficient confidence to trigger any of existing agent.", + "type": "task", + "createdDateTime": "2024-05-07T10:00:00Z", + "updatedDateTime": "2024-05-07T10:00:00Z", + "disabled": false, + "isPublic": true, + "profiles": [ "fallback" ] +} diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d/instruction.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d/instruction.liquid new file mode 100644 index 00000000..ced0264c --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d/instruction.liquid @@ -0,0 +1 @@ +You are a smart AI Assistant. \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid index 85cd1ff9..66fef298 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid @@ -2,12 +2,10 @@ You're {{router.name}} ({{router.description}}). You can understand messages sent by users in different languages. Follow these steps to handle user request: 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 on whether the function you chose matches the reason. -5. For agent required arguments, think carefully, leave it as blank object if user doesn't provide specific arguments. -6. You must include all required args when using selected FUNCTIONS, but you must not make up any parameters when there is no exact value provided, those parameters must set value as null if not declared. -7. Response must be in JSON format. +2. Determine which agent is suitable to handle this conversation. +3. For agent required arguments, think carefully, leave it as blank object if user didn't provide the specific arguments. +4. You must include all required args for the selected agent, but you must not make up any parameters when there is no exact value provided, those parameters must set value as null if not declared. +5. Response must be in JSON format. {% if routing_requirements and routing_requirements != empty %} [REQUIREMENTS] From c753fd88dd6d92053a87b3f39b67f9b4007f0b3d Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 7 May 2024 15:31:39 -0500 Subject: [PATCH 090/201] refactor file dialog structure --- .../Conversations/Models/Conversation.cs | 22 ++++ .../Repositories/IBotSharpRepository.cs | 1 - .../Repository/BotSharpDbContext.cs | 3 - .../FileRepository.Conversation.cs | 118 +++++------------- .../FileRepository/FileRepository.cs | 2 +- .../MongoRepository.Conversation.cs | 21 ---- 6 files changed, 51 insertions(+), 116 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs index 5613c088..ad7ffd04 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs @@ -32,11 +32,22 @@ public class Conversation public class DialogElement { + [JsonPropertyName("meta_data")] public DialogMetaData MetaData { get; set; } + + [JsonPropertyName("content")] public string Content { get; set; } + + [JsonPropertyName("secondary_content")] public string? SecondaryContent { get; set; } + + [JsonPropertyName("rich_content")] public string? RichContent { get; set; } + + [JsonPropertyName("secondary_rich_content")] public string? SecondaryRichContent { get; set; } + + [JsonPropertyName("payload")] public string? Payload { get; set; } public DialogElement() @@ -63,10 +74,21 @@ public class DialogElement public class DialogMetaData { + [JsonPropertyName("role")] public string Role { get; set; } + + [JsonPropertyName("agent_id")] public string AgentId { get; set; } + + [JsonPropertyName("message_id")] public string MessageId { get; set; } + + [JsonPropertyName("function_name")] public string? FunctionName { get; set; } + + [JsonPropertyName("sender_id")] public string? SenderId { get; set; } + + [JsonPropertyName("create_at")] public DateTime CreateTime { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index 6c488385..c301c738 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -51,7 +51,6 @@ public interface IBotSharpRepository void CreateNewConversation(Conversation conversation); bool DeleteConversations(IEnumerable conversationIds); List GetConversationDialogs(string conversationId); - void UpdateConversationDialogElements(string conversationId, List updateElements); void AppendConversationDialogs(string conversationId, List dialogs); ConversationState GetConversationStates(string conversationId); void UpdateConversationStates(string conversationId, List states); diff --git a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs index ed1d297d..650c9c4f 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs @@ -148,9 +148,6 @@ public class BotSharpDbContext : Database, IBotSharpRepository public List GetConversationDialogs(string conversationId) => throw new NotImplementedException(); - public void UpdateConversationDialogElements(string conversationId, List updateElements) - => new NotImplementedException(); - public ConversationState GetConversationStates(string conversationId) => throw new NotImplementedException(); diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs index 6708f49a..38f47f9f 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs @@ -28,7 +28,7 @@ namespace BotSharp.Core.Repository var dialogFile = Path.Combine(dir, DIALOG_FILE); if (!File.Exists(dialogFile)) { - File.WriteAllText(dialogFile, string.Empty); + File.WriteAllText(dialogFile, "[]"); } var stateFile = Path.Combine(dir, STATE_FILE); @@ -65,39 +65,20 @@ namespace BotSharp.Core.Repository if (!string.IsNullOrEmpty(convDir)) { var dialogDir = Path.Combine(convDir, DIALOG_FILE); - dialogs = CollectDialogElements(dialogDir); + var texts = File.ReadAllText(dialogDir); + try + { + dialogs = JsonSerializer.Deserialize>(texts, _options) ?? new List(); + } + catch + { + dialogs = new List(); + } } return dialogs; } - public void UpdateConversationDialogElements(string conversationId, List updateElements) - { - var dialogElements = GetConversationDialogs(conversationId); - if (dialogElements.IsNullOrEmpty() || updateElements.IsNullOrEmpty()) return; - - var convDir = FindConversationDirectory(conversationId); - if (!string.IsNullOrEmpty(convDir)) - { - var dialogDir = Path.Combine(convDir, DIALOG_FILE); - if (File.Exists(dialogDir)) - { - var updated = dialogElements.Select((x, idx) => - { - var found = updateElements.FirstOrDefault(e => e.Index == idx); - if (found != null) - { - x.Content = found.UpdateContent; - } - return x; - }).ToList(); - - var texts = ParseDialogElements(updated); - File.WriteAllLines(dialogDir, texts); - } - } - } - public void AppendConversationDialogs(string conversationId, List dialogs) { var convDir = FindConversationDirectory(conversationId); @@ -106,8 +87,18 @@ namespace BotSharp.Core.Repository var dialogFile = Path.Combine(convDir, DIALOG_FILE); if (File.Exists(dialogFile)) { - var texts = ParseDialogElements(dialogs); - File.AppendAllLines(dialogFile, texts); + var prevDialogs = File.ReadAllText(dialogFile); + var elements = JsonSerializer.Deserialize>(prevDialogs, _options); + if (elements != null) + { + elements.AddRange(dialogs); + } + else + { + elements = elements ?? new List(); + } + + File.WriteAllText(dialogFile, JsonSerializer.Serialize(elements, _options)); } var convFile = Path.Combine(convDir, CONVERSATION_FILE); @@ -519,69 +510,16 @@ namespace BotSharp.Core.Repository if (!File.Exists(dialogDir)) return dialogs; - var rawDialogs = File.ReadAllLines(dialogDir); - if (!rawDialogs.IsNullOrEmpty()) - { - for (int i = 0; i < rawDialogs.Count(); i += 5) - { - var blocks = rawDialogs[i].Split("|"); - var content = rawDialogs[i + 2]; - var trimmedContent = content.Substring(4); - var secondaryContent = rawDialogs[i + 4]; - var trimmedSecondaryContent = string.IsNullOrEmpty(secondaryContent) ? null : secondaryContent.Substring(4); - var payload = blocks.Count() > 6 ? blocks[6] : null; - - var meta = new DialogMetaData - { - Role = blocks[1], - AgentId = blocks[2], - MessageId = blocks[3], - SenderId = !string.IsNullOrWhiteSpace(blocks[4]) ? blocks[4] : null, - FunctionName = !string.IsNullOrWhiteSpace(blocks[5]) ? blocks[5] : null, - CreateTime = DateTime.Parse(blocks[0]) - }; - - var richContent = DecodeText(rawDialogs[i + 1]); - var secondaryRichContent = DecodeText(rawDialogs[i + 3]); - dialogs.Add(new DialogElement - { - MetaData = meta, - Content = trimmedContent, - SecondaryContent = trimmedSecondaryContent, - RichContent = richContent, - SecondaryRichContent = secondaryRichContent, - Payload = payload - }); - } - } + var texts = File.ReadAllText(dialogDir); + dialogs = JsonSerializer.Deserialize>(texts) ?? new List(); return dialogs; } - private List ParseDialogElements(List dialogs) + private string ParseDialogElements(List dialogs) { - var dialogTexts = new List(); - if (dialogs.IsNullOrEmpty()) return dialogTexts; + if (dialogs.IsNullOrEmpty()) return "[]"; - foreach (var element in dialogs) - { - var meta = element.MetaData; - var createTime = meta.CreateTime.ToString("MM/dd/yyyy hh:mm:ss.ffffff tt", CultureInfo.InvariantCulture); - var encodedRichContent = EncodeText(element.RichContent); - var encodedSecondaryRichContent = EncodeText(element.SecondaryRichContent); - var payload = element.Payload; - var metaStr = $"{createTime}|{meta.Role}|{meta.AgentId}|{meta.MessageId}|{meta.SenderId}|{meta.FunctionName}|{payload}"; - dialogTexts.Add(metaStr); - - dialogTexts.Add(encodedRichContent); - var content = $" - {element.Content}"; - dialogTexts.Add(content); - - dialogTexts.Add(encodedSecondaryRichContent); - var secondaryContent = element.SecondaryContent == null ? null : $" - {element.SecondaryContent}"; - dialogTexts.Add(secondaryContent); - } - - return dialogTexts; + return JsonSerializer.Serialize(dialogs, _options) ?? "[]"; } private List CollectConversationStates(string stateFile) @@ -701,7 +639,7 @@ namespace BotSharp.Core.Repository if (!File.Exists(dialogDir)) File.Create(dialogDir); var texts = ParseDialogElements(dialogs); - File.WriteAllLines(dialogDir, texts); + File.WriteAllText(dialogDir, texts); return true; } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs index 2d2c7889..2094b821 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs @@ -27,7 +27,7 @@ public partial class FileRepository : IBotSharpRepository private const string USER_AGENT_FILE = "agents.json"; private const string CONVERSATION_FILE = "conversation.json"; private const string STATS_FILE = "stats.json"; - private const string DIALOG_FILE = "dialogs.txt"; + private const string DIALOG_FILE = "dialogs.json"; private const string STATE_FILE = "state.json"; private const string BREAKPOINT_FILE = "breakpoint.json"; private const string EXECUTION_LOG_FILE = "execution.log"; diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs index 235be196..cfd3199d 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs @@ -85,27 +85,6 @@ public partial class MongoRepository return formattedDialog ?? new List(); } - public void UpdateConversationDialogElements(string conversationId, List updateElements) - { - if (string.IsNullOrEmpty(conversationId) || updateElements.IsNullOrEmpty()) return; - - var filterDialog = Builders.Filter.Eq(x => x.ConversationId, conversationId); - var foundDialog = _dc.ConversationDialogs.Find(filterDialog).FirstOrDefault(); - if (foundDialog == null || foundDialog.Dialogs.IsNullOrEmpty()) return; - - foundDialog.Dialogs = foundDialog.Dialogs.Select((x, idx) => - { - var found = updateElements.FirstOrDefault(e => e.Index == idx); - if (found != null) - { - x.Content = found.UpdateContent; - } - return x; - }).ToList(); - - _dc.ConversationDialogs.ReplaceOne(filterDialog, foundDialog); - } - public void AppendConversationDialogs(string conversationId, List dialogs) { if (string.IsNullOrEmpty(conversationId)) return; From 6554c43dbc173ca79dee7b595d9823164b5d3183 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Tue, 7 May 2024 17:16:07 -0500 Subject: [PATCH 091/201] Only show playload to AI. --- .../Conversations/Models/RoleDialogModel.cs | 3 +++ .../Conversations/Services/ConversationService.SendMessage.cs | 3 +++ .../Conversations/Services/ConversationStorage.cs | 2 +- .../Routing/RoutingService.GetConversationContent.cs | 2 +- .../Providers/ChatCompletionProvider.cs | 2 +- .../Providers/ChatCompletionProvider.cs | 2 +- 6 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs index e019eae4..bc9ea0ef 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs @@ -28,6 +28,9 @@ public class RoleDialogModel : ITrackableMessage public string? SecondaryContent { get; set; } + /// + /// Postback content + /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("payload")] public string? Payload { 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 f8460e3a..cf7f74f0 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -51,6 +51,9 @@ public partial class ConversationService fileService.SaveMessageFiles(_conversationId, message.MessageId, message.Files); message.Files?.Clear(); + // Save payload + message.Payload = string.IsNullOrEmpty(replyMessage.Payload) ? message.Content : replyMessage.Payload; + // Before chat completion hook foreach (var hook in hooks) { diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs index 9bb8f103..5abc4fc5 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs @@ -104,7 +104,7 @@ public class ConversationStorage : IConversationStorage var meta = dialog.MetaData; var content = dialog.Content; var secondaryContent = dialog.SecondaryContent; - var payload = dialog.Payload; + var payload = string.IsNullOrEmpty(dialog.Payload) ? null : dialog.Payload; var role = meta.Role; var currentAgentId = meta.AgentId; var messageId = meta.MessageId; diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetConversationContent.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetConversationContent.cs index 5825a2c0..eb690b9c 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetConversationContent.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetConversationContent.cs @@ -16,7 +16,7 @@ public partial class RoutingService role = agent.Name; } - conversation += $"{role}: {dialog.SecondaryContent ?? dialog.Content}\r\n"; + conversation += $"{role}: {dialog.Payload ?? dialog.SecondaryContent ?? dialog.Content}\r\n"; } return conversation; diff --git a/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs index aaebfc33..87202109 100644 --- a/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs @@ -123,7 +123,7 @@ public class ChatCompletionProvider : IChatCompletion { if (conv.Role == AgentRole.User) { - messages.Add(new Message(RoleType.User, conv.Content)); + messages.Add(new Message(RoleType.User, conv.Payload ?? conv.Content)); } else if (conv.Role == AgentRole.Assistant) { diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs index d2077ae6..4bca97ff 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs @@ -278,7 +278,7 @@ public class ChatCompletionProvider : IChatCompletion } else if (message.Role == ChatRole.User) { - var userMessage = new ChatRequestUserMessage(message.Content) + var userMessage = new ChatRequestUserMessage(message.Payload ?? message.Content) { // To display Planner name in log Name = message.FunctionName, From 2a5d0ac4efca4d20809797bea19ea1c8bdf43bc8 Mon Sep 17 00:00:00 2001 From: "C. Oceania" Date: Tue, 7 May 2024 17:23:17 -0500 Subject: [PATCH 092/201] Update ConversationService.SendMessage.cs --- .../Services/ConversationService.SendMessage.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs index cf7f74f0..489e400e 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -52,7 +52,10 @@ public partial class ConversationService message.Files?.Clear(); // Save payload - message.Payload = string.IsNullOrEmpty(replyMessage.Payload) ? message.Content : replyMessage.Payload; + if (replyMessage != null) + { + message.Payload = string.IsNullOrEmpty(replyMessage.Payload) ? message.Content : replyMessage.Payload; + } // Before chat completion hook foreach (var hook in hooks) From 07034886609e7af1428f192fe4086de3303410da Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Tue, 7 May 2024 21:14:01 -0500 Subject: [PATCH 093/201] Template [Translate] --- .../Models/RichContent/Template/GenericTemplateMessage.cs | 2 ++ .../Models/RichContent/Template/MultiSelectTemplateMessage.cs | 3 ++- .../Models/RichContent/Template/ProductTemplateMessage.cs | 2 +- .../Messaging/Models/RichContent/TextMessage.cs | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) 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 3d3e5eef..b9226798 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/GenericTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/GenericTemplateMessage.cs @@ -39,6 +39,8 @@ public class GenericElement { [Translate] public string Title { get; set; } + + [Translate] public string Subtitle { get; set; } [JsonPropertyName("image_url")] 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 c57f144a..5e18868f 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/MultiSelectTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/MultiSelectTemplateMessage.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Messaging.Enums; using Newtonsoft.Json; namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template; @@ -11,6 +10,7 @@ public class MultiSelectTemplateMessage : IRichMessage, ITemplateMessage [JsonPropertyName("text")] [JsonProperty("text")] + [Translate] public string Text { get; set; } = string.Empty; [JsonPropertyName("template_type")] @@ -28,6 +28,7 @@ public class MultiSelectTemplateMessage : IRichMessage, ITemplateMessage public class OptionElement { + [Translate] public string Title { get; set; } = string.Empty; public string Type { get; set; } = string.Empty; public string? Payload { 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 61d22dcc..48287f53 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProductTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProductTemplateMessage.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Messaging.Enums; using Newtonsoft.Json; namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template; @@ -11,6 +10,7 @@ public class ProductTemplateMessage : IRichMessage, ITemplateMessage [JsonPropertyName("text")] [JsonProperty("text")] + [Translate] public string Text { get; set; } = string.Empty; [JsonPropertyName("template_type")] diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/TextMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/TextMessage.cs index d2639ef7..c78ed69c 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/TextMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/TextMessage.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Messaging.Enums; using Newtonsoft.Json; namespace BotSharp.Abstraction.Messaging.Models.RichContent; @@ -9,6 +8,7 @@ public class TextMessage : IRichMessage [JsonProperty("rich_type")] public string RichType => RichTypeEnum.Text; + [Translate] public string Text { get; set; } = string.Empty; public TextMessage(string text) From 0ab2e4d041526c5043c23ded19d5bf38b4d7661f Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 8 May 2024 11:47:21 -0500 Subject: [PATCH 094/201] unite json serializer --- .../Messaging/BotSharpMessageParser.cs | 11 ++++----- .../Messaging/IRichMessage.cs | 5 ---- .../Messaging/ITemplateMessage.cs | 3 --- .../RichContentJsonConverter .cs | 2 +- .../TemplateMessageJsonConverter.cs | 2 +- .../Models/RichContent/ElementAction.cs | 4 ---- .../Models/RichContent/ElementButton.cs | 6 ----- .../Models/RichContent/QuickReplyElement.cs | 4 ---- .../Models/RichContent/QuickReplyMessage.cs | 5 ---- .../Models/RichContent/RichContent.cs | 2 -- .../Template/ButtonTemplateMessage.cs | 7 ------ .../Template/CouponTemplateMessage.cs | 12 ---------- .../Template/GenericTemplateMessage.cs | 12 ---------- .../Template/MultiSelectTemplateMessage.cs | 7 ------ .../Template/ProductTemplateMessage.cs | 5 +--- .../Models/RichContent/TextMessage.cs | 3 --- .../Translation/TranslationService.cs | 24 ++++++++++--------- src/WebStarter/WebStarter.csproj | 1 + 18 files changed, 21 insertions(+), 94 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs index e22150e9..1f495a44 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs @@ -1,16 +1,14 @@ -using BotSharp.Abstraction.Messaging.Enums; using BotSharp.Abstraction.Messaging.Models.RichContent.Template; using BotSharp.Abstraction.Messaging.Models.RichContent; using System.Text.Json; using System.Reflection; -using Newtonsoft.Json; namespace BotSharp.Abstraction.Messaging; public static class BotSharpMessageParser { - public static IRichMessage? ParseRichMessage(JsonElement root) + public static IRichMessage? ParseRichMessage(JsonElement root, JsonSerializerOptions options) { IRichMessage? res = null; Type? targetType = null; @@ -58,13 +56,13 @@ public static class BotSharpMessageParser if (targetType != null) { - res = JsonConvert.DeserializeObject(jsonText, targetType) as IRichMessage; + res = JsonSerializer.Deserialize(jsonText, targetType, options) as IRichMessage; } return res; } - public static ITemplateMessage? ParseTemplateMessage(JsonElement root) + public static ITemplateMessage? ParseTemplateMessage(JsonElement root, JsonSerializerOptions options) { ITemplateMessage? res = null; Type? targetType = null; @@ -101,7 +99,6 @@ public static class BotSharpMessageParser if (wrapperType != null && genericType != null) { targetType = wrapperType.MakeGenericType(genericType); - res = JsonConvert.DeserializeObject(jsonText, targetType) as ITemplateMessage; } } } @@ -109,7 +106,7 @@ public static class BotSharpMessageParser if (targetType != null) { - res = JsonConvert.DeserializeObject(jsonText, targetType) as ITemplateMessage; + res = JsonSerializer.Deserialize(jsonText, targetType, options) as ITemplateMessage; } return res; diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/IRichMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/IRichMessage.cs index 84d8fcfb..605fc3f7 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/IRichMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/IRichMessage.cs @@ -1,15 +1,10 @@ -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 7c9ccadf..63890240 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/ITemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/ITemplateMessage.cs @@ -1,10 +1,7 @@ -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/JsonConverters/RichContentJsonConverter .cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/RichContentJsonConverter .cs index 1748eb5a..bc456aca 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/RichContentJsonConverter .cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/RichContentJsonConverter .cs @@ -8,7 +8,7 @@ public class RichContentJsonConverter : JsonConverter { using var jsonDoc = JsonDocument.ParseValue(ref reader); var root = jsonDoc.RootElement; - var res = BotSharpMessageParser.ParseRichMessage(root); + var res = BotSharpMessageParser.ParseRichMessage(root, options); return res; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/TemplateMessageJsonConverter.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/TemplateMessageJsonConverter.cs index ce42c489..69fb6c28 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/TemplateMessageJsonConverter.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/TemplateMessageJsonConverter.cs @@ -8,7 +8,7 @@ public class TemplateMessageJsonConverter : JsonConverter { using var jsonDoc = JsonDocument.ParseValue(ref reader); var root = jsonDoc.RootElement; - var res = BotSharpMessageParser.ParseTemplateMessage(root); + var res = BotSharpMessageParser.ParseTemplateMessage(root, options); return res; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementAction.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementAction.cs index 39d03e86..5a2682bd 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementAction.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementAction.cs @@ -1,6 +1,3 @@ -using Newtonsoft.Json; -using JsonIgnoreAttribute = System.Text.Json.Serialization.JsonIgnoreAttribute; - namespace BotSharp.Abstraction.Messaging.Models.RichContent; public class ElementAction @@ -11,7 +8,6 @@ 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 b4baf793..cd3d9da6 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs @@ -1,6 +1,3 @@ -using Newtonsoft.Json; -using JsonIgnoreAttribute = System.Text.Json.Serialization.JsonIgnoreAttribute; - namespace BotSharp.Abstraction.Messaging.Models.RichContent; /// @@ -20,15 +17,12 @@ 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")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [Translate] 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 583cf966..4ad88ab9 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/QuickReplyElement.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/QuickReplyElement.cs @@ -1,6 +1,3 @@ -using Newtonsoft.Json; -using JsonIgnoreAttribute = System.Text.Json.Serialization.JsonIgnoreAttribute; - namespace BotSharp.Abstraction.Messaging.Models.RichContent; public class QuickReplyElement @@ -12,7 +9,6 @@ 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 73690649..c81fe73e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/QuickReplyMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/QuickReplyMessage.cs @@ -1,16 +1,11 @@ -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/RichContent.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/RichContent.cs index c8c3b3fe..1f812bf8 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/RichContent.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/RichContent.cs @@ -1,5 +1,3 @@ -using BotSharp.Abstraction.Messaging.Enums; - namespace BotSharp.Abstraction.Messaging.Models.RichContent; public class RichContent where T : IRichMessage 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 bdc742ce..0d8470d7 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ButtonTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ButtonTemplateMessage.cs @@ -1,5 +1,3 @@ -using Newtonsoft.Json; - namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template; /// @@ -8,23 +6,18 @@ 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")] [Translate] 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 171947ab..a3bf116d 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/CouponTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/CouponTemplateMessage.cs @@ -1,6 +1,3 @@ -using BotSharp.Abstraction.Messaging.Enums; -using Newtonsoft.Json; - namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template; /// @@ -10,40 +7,31 @@ 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 b9226798..f165dbc1 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/GenericTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/GenericTemplateMessage.cs @@ -1,37 +1,27 @@ -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")] [Translate] 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; } @@ -44,11 +34,9 @@ public class GenericElement 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 5e18868f..4ef62fc1 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/MultiSelectTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/MultiSelectTemplateMessage.cs @@ -1,28 +1,21 @@ -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")] [Translate] 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 48287f53..17b230fe 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProductTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProductTemplateMessage.cs @@ -1,20 +1,17 @@ -using Newtonsoft.Json; +//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")] [Translate] 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 c78ed69c..3a37ad54 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/TextMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/TextMessage.cs @@ -1,11 +1,8 @@ -using Newtonsoft.Json; - namespace BotSharp.Abstraction.Messaging.Models.RichContent; public class TextMessage : IRichMessage { [JsonPropertyName("rich_type")] - [JsonProperty("rich_type")] public string RichType => RichTypeEnum.Text; [Translate] diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index 0e44e3e2..01ae898f 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -1,10 +1,7 @@ using BotSharp.Abstraction.MLTasks; using BotSharp.Abstraction.Options; using BotSharp.Abstraction.Templating; -using BotSharp.Abstraction.Translation.Attributes; -using Newtonsoft.Json; using System.Collections; -using System.Collections.Generic; using System.Reflection; namespace BotSharp.Core.Translation; @@ -39,10 +36,14 @@ public class TranslationService : ITranslationService return data; } - var cloned = data; + var clonedData = data; if (clone) { - cloned = Clone(data); + clonedData = Clone(data); + if (clonedData == null) + { + return data; + } } // chat completion @@ -52,7 +53,7 @@ public class TranslationService : ITranslationService var template = _router.Templates.First(x => x.Name == "translation_prompt").Content; var texts = unique.ToArray(); - var translatedStringList = await InnerTranslate(JsonConvert.SerializeObject(texts), language, template); + var translatedStringList = await InnerTranslate(JsonSerializer.Serialize(texts, _options.JsonSerializerOptions), language, template); try { @@ -64,22 +65,22 @@ public class TranslationService : ITranslationService map.Add(texts[i], translatedTexts[i]); } - cloned = Assign(cloned, map); + clonedData = Assign(clonedData, map); } catch (Exception ex) { _logger.LogError(ex.Message); } - return cloned; + return clonedData; } private T Clone(T data) where T : class { if (data == null) return data; - var str = System.Text.Json.JsonSerializer.Serialize(data, _options.JsonSerializerOptions); - var cloned = System.Text.Json.JsonSerializer.Deserialize(str, _options.JsonSerializerOptions); + var str = JsonSerializer.Serialize(data, _options.JsonSerializerOptions); + var cloned = JsonSerializer.Deserialize(str, _options.JsonSerializerOptions); return cloned; } @@ -256,7 +257,8 @@ public class TranslationService : ITranslationService { if (translate != null) { - var targetValue = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(Assign(value, map)), propType); + var json = JsonSerializer.Serialize(Assign(value, map), _options.JsonSerializerOptions); + var targetValue = JsonSerializer.Deserialize(json, propType, _options.JsonSerializerOptions); prop.SetValue(data, targetValue); } } diff --git a/src/WebStarter/WebStarter.csproj b/src/WebStarter/WebStarter.csproj index 1eb473c5..b6d7ccfb 100644 --- a/src/WebStarter/WebStarter.csproj +++ b/src/WebStarter/WebStarter.csproj @@ -27,6 +27,7 @@ + From 40e7df69c4bf7ca4bfabe9141141a552a6ea4a7f Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 8 May 2024 11:48:39 -0500 Subject: [PATCH 095/201] minor change --- .../Models/RichContent/Template/ProductTemplateMessage.cs | 2 -- 1 file changed, 2 deletions(-) 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 17b230fe..05af097a 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProductTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProductTemplateMessage.cs @@ -1,5 +1,3 @@ -//using Newtonsoft.Json; - namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template; public class ProductTemplateMessage : IRichMessage, ITemplateMessage From 29a8e2433517e540dd2ef0e80ad4065ac506ffa2 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 8 May 2024 11:52:39 -0500 Subject: [PATCH 096/201] add AnthropicAI --- src/WebStarter/WebStarter.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WebStarter/WebStarter.csproj b/src/WebStarter/WebStarter.csproj index b6d7ccfb..89b09482 100644 --- a/src/WebStarter/WebStarter.csproj +++ b/src/WebStarter/WebStarter.csproj @@ -27,7 +27,6 @@ - @@ -55,6 +54,7 @@ + From 49ab87425b7aa0615a5b957e0a88e48e5f65fb01 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 8 May 2024 14:32:09 -0500 Subject: [PATCH 097/201] fix json log --- .../Hooks/StreamingLogHook.cs | 42 +++++++++++++++---- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs index 26e894c7..cd34fa1a 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs @@ -16,6 +16,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR { private readonly ConversationSetting _convSettings; private readonly BotSharpOptions _options; + private readonly JsonSerializerOptions _localJsonOptions; private readonly IServiceProvider _services; private readonly IHubContext _chatHub; private readonly IConversationStateService _state; @@ -41,6 +42,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR _user = user; _agentService = agentService; _routingCtx = routingCtx; + _localJsonOptions = InitLocalJsonOptions(options); } #region IConversationHook @@ -188,15 +190,15 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR var log = $"{GetMessageContent(message)}"; if (message.RichContent != null || message.SecondaryRichContent != null) { - var jsonOptions = new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true, - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - AllowTrailingCommas = true, - WriteIndented = true, - Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) - }; - var richContent = JsonSerializer.Serialize(message.SecondaryRichContent ?? message.RichContent, jsonOptions); + //var jsonOptions = new JsonSerializerOptions + //{ + // PropertyNameCaseInsensitive = true, + // PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + // AllowTrailingCommas = true, + // WriteIndented = true, + // Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) + //}; + var richContent = JsonSerializer.Serialize(message.SecondaryRichContent ?? message.RichContent, _localJsonOptions); log += $"\r\n```json\r\n{richContent}\r\n```"; } @@ -494,4 +496,26 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR { return !string.IsNullOrEmpty(message.SecondaryContent) ? message.SecondaryContent : message.Content; } + + private JsonSerializerOptions InitLocalJsonOptions(BotSharpOptions options) + { + var localOptions = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + AllowTrailingCommas = true, + WriteIndented = true, + Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) + }; + + if (options?.JsonSerializerOptions != null && !options.JsonSerializerOptions.Converters.IsNullOrEmpty()) + { + foreach (var converter in options.JsonSerializerOptions.Converters) + { + localOptions.Converters.Add(converter); + } + } + + return localOptions; + } } From bec142f8ba7489a16260418c15cd1151ab6081fd Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 8 May 2024 14:33:03 -0500 Subject: [PATCH 098/201] remove comments --- .../BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs index cd34fa1a..e32a1c36 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs @@ -190,14 +190,6 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR var log = $"{GetMessageContent(message)}"; if (message.RichContent != null || message.SecondaryRichContent != null) { - //var jsonOptions = new JsonSerializerOptions - //{ - // PropertyNameCaseInsensitive = true, - // PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - // AllowTrailingCommas = true, - // WriteIndented = true, - // Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) - //}; var richContent = JsonSerializer.Serialize(message.SecondaryRichContent ?? message.RichContent, _localJsonOptions); log += $"\r\n```json\r\n{richContent}\r\n```"; } From b30aa58aa625e10d09dc5ea7abe605ebd35d5d8a Mon Sep 17 00:00:00 2001 From: "LAPTOP-3CFGGVOS\\rabbit" Date: Thu, 9 May 2024 19:42:05 +0800 Subject: [PATCH 099/201] optimize translation --- .../BotSharp.Core/Routing/RoutingService.cs | 24 --------- .../BotSharpLoggerExtensions.cs | 1 + .../Hooks/TranslationResponseHook.cs | 54 +++++++++++++++++++ 3 files changed, 55 insertions(+), 24 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Logger/Hooks/TranslationResponseHook.cs diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index eeb85e77..eb2496bf 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -144,30 +144,6 @@ public partial class RoutingService : IRoutingService loopCount++; } - // Handle multi-language for output - if (language != LanguageType.UNKNOWN && language != LanguageType.ENGLISH) - { - if (response.RichContent != null) - { - if (string.IsNullOrEmpty(response.RichContent.Message.Text)) - { - response.RichContent.Message.Text = response.Content; - } - - response.SecondaryRichContent = await translator.Translate(_router, - message.MessageId, - response.RichContent, - language: language); - } - else - { - response.SecondaryContent = await translator.Translate(_router, - message.MessageId, - response.Content, - language: language); - } - } - return response; } diff --git a/src/Infrastructure/BotSharp.Logger/BotSharpLoggerExtensions.cs b/src/Infrastructure/BotSharp.Logger/BotSharpLoggerExtensions.cs index dfc9c27d..74dbbe6b 100644 --- a/src/Infrastructure/BotSharp.Logger/BotSharpLoggerExtensions.cs +++ b/src/Infrastructure/BotSharp.Logger/BotSharpLoggerExtensions.cs @@ -14,6 +14,7 @@ public static class BotSharpLoggerExtensions services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); return services; } } diff --git a/src/Infrastructure/BotSharp.Logger/Hooks/TranslationResponseHook.cs b/src/Infrastructure/BotSharp.Logger/Hooks/TranslationResponseHook.cs new file mode 100644 index 00000000..ce154cc2 --- /dev/null +++ b/src/Infrastructure/BotSharp.Logger/Hooks/TranslationResponseHook.cs @@ -0,0 +1,54 @@ +using BotSharp.Abstraction.Agents; +using BotSharp.Abstraction.Infrastructures.Enums; +using BotSharp.Abstraction.Translation; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Logger.Hooks +{ + public class TranslationResponseHook : ConversationHookBase + { + private readonly IServiceProvider _services; + private readonly IConversationStateService _states; + private const string LessenCopilot = "2cd4b805-7078-4405-87e9-2ec9aadf8a11"; + + public TranslationResponseHook(IServiceProvider services, + IConversationStateService states) + { + _services = services; + _states = states; + } + public override async Task OnResponseGenerated(RoleDialogModel message) + { + // Handle multi-language for output + var agentService = _services.GetRequiredService(); + var router = await agentService.LoadAgent(LessenCopilot); + var translator = _services.GetRequiredService(); + var language = _states.GetState("language", LanguageType.ENGLISH); + if (language != LanguageType.UNKNOWN && language != LanguageType.ENGLISH) + { + if (message.RichContent != null) + { + if (string.IsNullOrEmpty(message.RichContent.Message.Text)) + { + message.RichContent.Message.Text = message.Content; + } + + message.SecondaryRichContent = await translator.Translate(router, + message.MessageId, + message.RichContent, + language: language); + } + else + { + message.SecondaryContent = await translator.Translate(router, + message.MessageId, + message.Content, + language: language); + } + } + await base.OnResponseGenerated(message); + } + } +} From ac42fa1302698259fadc75e92c45097229d3ad41 Mon Sep 17 00:00:00 2001 From: "LAPTOP-3CFGGVOS\\rabbit" Date: Thu, 9 May 2024 20:25:47 +0800 Subject: [PATCH 100/201] optimize hard code --- .../BotSharp.Logger/Hooks/TranslationResponseHook.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Logger/Hooks/TranslationResponseHook.cs b/src/Infrastructure/BotSharp.Logger/Hooks/TranslationResponseHook.cs index ce154cc2..77b411af 100644 --- a/src/Infrastructure/BotSharp.Logger/Hooks/TranslationResponseHook.cs +++ b/src/Infrastructure/BotSharp.Logger/Hooks/TranslationResponseHook.cs @@ -11,7 +11,6 @@ namespace BotSharp.Logger.Hooks { private readonly IServiceProvider _services; private readonly IConversationStateService _states; - private const string LessenCopilot = "2cd4b805-7078-4405-87e9-2ec9aadf8a11"; public TranslationResponseHook(IServiceProvider services, IConversationStateService states) @@ -23,7 +22,7 @@ namespace BotSharp.Logger.Hooks { // Handle multi-language for output var agentService = _services.GetRequiredService(); - var router = await agentService.LoadAgent(LessenCopilot); + var router = await agentService.LoadAgent(message.CurrentAgentId); var translator = _services.GetRequiredService(); var language = _states.GetState("language", LanguageType.ENGLISH); if (language != LanguageType.UNKNOWN && language != LanguageType.ENGLISH) From 346fab18597c9b0443281c7cbb9cbd5e39015292 Mon Sep 17 00:00:00 2001 From: "LAPTOP-3CFGGVOS\\rabbit" Date: Thu, 9 May 2024 23:05:21 +0800 Subject: [PATCH 101/201] optimize translation --- .../BotSharp.Logger/Hooks/TranslationResponseHook.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Logger/Hooks/TranslationResponseHook.cs b/src/Infrastructure/BotSharp.Logger/Hooks/TranslationResponseHook.cs index 77b411af..fbb998ad 100644 --- a/src/Infrastructure/BotSharp.Logger/Hooks/TranslationResponseHook.cs +++ b/src/Infrastructure/BotSharp.Logger/Hooks/TranslationResponseHook.cs @@ -11,6 +11,7 @@ namespace BotSharp.Logger.Hooks { private readonly IServiceProvider _services; private readonly IConversationStateService _states; + private const string AIAssistant = "01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a"; public TranslationResponseHook(IServiceProvider services, IConversationStateService states) @@ -22,7 +23,7 @@ namespace BotSharp.Logger.Hooks { // Handle multi-language for output var agentService = _services.GetRequiredService(); - var router = await agentService.LoadAgent(message.CurrentAgentId); + var router = await agentService.LoadAgent(AIAssistant); var translator = _services.GetRequiredService(); var language = _states.GetState("language", LanguageType.ENGLISH); if (language != LanguageType.UNKNOWN && language != LanguageType.ENGLISH) From f97cb0e8a475e5550a36089636c6258bd507312e Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Thu, 9 May 2024 11:09:21 -0500 Subject: [PATCH 102/201] Fix translation plugin. --- .../Translation/TranslationPlugin.cs | 16 ++++++++++++++++ .../Translation}/TranslationResponseHook.cs | 0 .../BotSharp.Logger/BotSharpLoggerExtensions.cs | 1 - 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 src/Infrastructure/BotSharp.Core/Translation/TranslationPlugin.cs rename src/Infrastructure/{BotSharp.Logger/Hooks => BotSharp.Core/Translation}/TranslationResponseHook.cs (100%) diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationPlugin.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationPlugin.cs new file mode 100644 index 00000000..448fbd59 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationPlugin.cs @@ -0,0 +1,16 @@ +using BotSharp.Logger.Hooks; +using Microsoft.Extensions.Configuration; + +namespace BotSharp.Core.Translation; + +public class TranslationPlugin : IBotSharpPlugin +{ + public string Id => "a81997c3-5d3a-4f18-bae0-be7a81d233ba"; + public string Name => "Multi-language Translator"; + public string Description => "Output the corresponding language response according to the user language"; + + public void RegisterDI(IServiceCollection services, IConfiguration config) + { + services.AddScoped(); + } +} diff --git a/src/Infrastructure/BotSharp.Logger/Hooks/TranslationResponseHook.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationResponseHook.cs similarity index 100% rename from src/Infrastructure/BotSharp.Logger/Hooks/TranslationResponseHook.cs rename to src/Infrastructure/BotSharp.Core/Translation/TranslationResponseHook.cs diff --git a/src/Infrastructure/BotSharp.Logger/BotSharpLoggerExtensions.cs b/src/Infrastructure/BotSharp.Logger/BotSharpLoggerExtensions.cs index 74dbbe6b..dfc9c27d 100644 --- a/src/Infrastructure/BotSharp.Logger/BotSharpLoggerExtensions.cs +++ b/src/Infrastructure/BotSharp.Logger/BotSharpLoggerExtensions.cs @@ -14,7 +14,6 @@ public static class BotSharpLoggerExtensions services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); return services; } } From bdcf8c5a2ac4c0aa44b1adc4743cec9b4a7660a5 Mon Sep 17 00:00:00 2001 From: "C. Oceania" Date: Thu, 9 May 2024 12:11:30 -0500 Subject: [PATCH 103/201] Update TranslationResponseHook.cs --- .../BotSharp.Core/Translation/TranslationResponseHook.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationResponseHook.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationResponseHook.cs index fbb998ad..fef6ee17 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationResponseHook.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationResponseHook.cs @@ -39,6 +39,7 @@ namespace BotSharp.Logger.Hooks message.MessageId, message.RichContent, language: language); + message.SecondaryContent = message.SecondaryRichContent.Message.Text; } else { From 76dc9080ad2a50132dd794e77c80afc6a6e2e603 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Thu, 9 May 2024 15:59:59 -0500 Subject: [PATCH 104/201] Fix translator. --- .../Agents/Settings/AgentSettings.cs | 1 + .../Translation/Models/TranslationOutput.cs | 13 ++++++++ .../Handlers/RouteToAgentRoutingHandler.cs | 5 +-- .../RoutingService.GetConversationContent.cs | 2 +- .../BotSharp.Core/Routing/RoutingService.cs | 31 ++++++++++--------- .../Translation/TranslationResponseHook.cs | 6 ++++ .../Translation/TranslationService.cs | 14 +++++++-- .../templates/translation_prompt.liquid | 3 +- 8 files changed, 52 insertions(+), 23 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Translation/Models/TranslationOutput.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Settings/AgentSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Settings/AgentSettings.cs index e95f72bc..232e7b60 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Settings/AgentSettings.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Settings/AgentSettings.cs @@ -5,6 +5,7 @@ public class AgentSettings public string DataDir { get; set; } = string.Empty; public string TemplateFormat { get; set; } = "liquid"; public string HostAgentId { get; set; } = string.Empty; + public bool EnableTranslator { get; set; } = false; /// /// This is the default LLM config for agent diff --git a/src/Infrastructure/BotSharp.Abstraction/Translation/Models/TranslationOutput.cs b/src/Infrastructure/BotSharp.Abstraction/Translation/Models/TranslationOutput.cs new file mode 100644 index 00000000..52ad54ec --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Translation/Models/TranslationOutput.cs @@ -0,0 +1,13 @@ +namespace BotSharp.Abstraction.Translation.Models; + +public class TranslationOutput +{ + [JsonPropertyName("input_lang")] + public string InputLanguage { get; set; } = null!; + + [JsonPropertyName("output_lang")] + public string OutputLanguage { get; set; } = LanguageType.ENGLISH; + + [JsonPropertyName("texts")] + public string[] Texts { get; set; } = Array.Empty(); +} diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs index 6afc67c9..9f768696 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs @@ -28,10 +28,7 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler required: true), new ParameterPropertyDef("is_new_task", "whether the user is requesting a new task that is different from the previous topic.", - type: "boolean"), - new ParameterPropertyDef("language", - "User preferred language, considering the whole conversation. Language could be English, Spanish or Chinese.", - required: true) + type: "boolean") }; public RouteToAgentRoutingHandler(IServiceProvider services, ILogger logger, RoutingSettings settings) diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetConversationContent.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetConversationContent.cs index eb690b9c..2757d5d8 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetConversationContent.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetConversationContent.cs @@ -16,7 +16,7 @@ public partial class RoutingService role = agent.Name; } - conversation += $"{role}: {dialog.Payload ?? dialog.SecondaryContent ?? dialog.Content}\r\n"; + conversation += $"{role}: {dialog.Payload ?? dialog.Content}\r\n"; } return conversation; diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index eb2496bf..885728a1 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -82,26 +82,29 @@ public partial class RoutingService : IRoutingService _context.Push(_router.Id); + // Handle multi-language for input + var agentSettings = _services.GetRequiredService(); + if (agentSettings.EnableTranslator) + { + var translator = _services.GetRequiredService(); + + var language = states.GetState("language", LanguageType.UNKNOWN); + if (language != LanguageType.ENGLISH) + { + message.SecondaryContent = message.Content; + message.Content = await translator.Translate(_router, message.MessageId, message.Content, + language: LanguageType.ENGLISH, + clone: false); + } + } + dialogs.Add(message); + storage.Append(convService.ConversationId, message); // Get first instruction _router.TemplateDict["conversation"] = await GetConversationContent(dialogs); var inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs); - // Handle multi-language for input - var translator = _services.GetRequiredService(); - - var language = states.GetState("language", inst.Language); - if (language != LanguageType.UNKNOWN && language != LanguageType.ENGLISH) - { - message.SecondaryContent = message.Content; - message.Content = await translator.Translate(_router, message.MessageId, message.Content, - language: LanguageType.ENGLISH, - clone: false); - } - - storage.Append(convService.ConversationId, message); - int loopCount = 1; while (true) { diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationResponseHook.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationResponseHook.cs index fef6ee17..d79c73a0 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationResponseHook.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationResponseHook.cs @@ -21,6 +21,12 @@ namespace BotSharp.Logger.Hooks } public override async Task OnResponseGenerated(RoleDialogModel message) { + var agentSettings = _services.GetRequiredService(); + if (!agentSettings.EnableTranslator) + { + return; + } + // Handle multi-language for output var agentService = _services.GetRequiredService(); var router = await agentService.LoadAgent(AIAssistant); diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index 01ae898f..b18c7ab1 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -1,6 +1,8 @@ +using BotSharp.Abstraction.Infrastructures.Enums; using BotSharp.Abstraction.MLTasks; using BotSharp.Abstraction.Options; using BotSharp.Abstraction.Templating; +using BotSharp.Abstraction.Translation.Models; using System.Collections; using System.Reflection; @@ -57,7 +59,13 @@ public class TranslationService : ITranslationService try { - var translatedTexts = translatedStringList.JsonArrayContent(); + // Override language if it's Unknown, it's used to output the corresponding language. + var states = _services.GetRequiredService(); + var inputLanguage = string.IsNullOrEmpty(translatedStringList.InputLanguage) ? LanguageType.ENGLISH : translatedStringList.InputLanguage; + var languageState = states.GetState("language", inputLanguage); + states.SetState("language", languageState, activeRounds: 1); + + var translatedTexts = translatedStringList.Texts; var map = new Dictionary(); for (var i = 0; i < texts.Length; i++) @@ -283,7 +291,7 @@ public class TranslationService : ITranslationService /// /// /// - private async Task InnerTranslate(string texts, string language, string template) + private async Task InnerTranslate(string texts, string language, string template) { var translator = new Agent { @@ -308,7 +316,7 @@ public class TranslationService : ITranslationService } }; var response = await _completion.GetChatCompletions(translator, translationDialogs); - return response.Content; + return response.Content.JsonContent(); } #region Type methods diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid index a3052187..4cd12bd0 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid @@ -1,4 +1,5 @@ {{ text_list }} ===== -Translate the above sentences in the list into {{ language }}, output the translated text in JSON array [""]. \ No newline at end of file +Translate the above sentences in the list into {{ language }}. +Output the translated text in JSON {"input_lang":"", "output_lang":"{{ language }}", "texts":[""]}, input_lang is based on the original sentences. \ No newline at end of file From 9a93fd10e6291905deb45acc78c081a9dd4fc624 Mon Sep 17 00:00:00 2001 From: "C. Oceania" Date: Thu, 9 May 2024 16:30:30 -0500 Subject: [PATCH 105/201] Update RoutingArgs.cs --- .../BotSharp.Abstraction/Routing/Models/RoutingArgs.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs index f68b7231..dad45cb9 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs @@ -50,9 +50,6 @@ public class RoutingArgs [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string UserGoal { get; set; } = string.Empty; - [JsonPropertyName("language")] - public string Language { get; set; } = LanguageType.ENGLISH; - public override string ToString() { var route = string.IsNullOrEmpty(AgentName) ? "" : $""; From 1360adbb46aa224ad9f59da4da29ac7571dc4f48 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 9 May 2024 17:06:03 -0500 Subject: [PATCH 106/201] add agent template update endpoint --- .../Agents/IAgentService.cs | 7 +++ .../Repositories/IBotSharpRepository.cs | 2 +- .../Services/AgentService.UpdateAgent.cs | 56 ++++++++++++++++++- .../Repository/BotSharpDbContext.cs | 3 + .../FileRepository/FileRepository.Agent.cs | 19 +++++++ .../Controllers/AgentController.cs | 8 +++ .../Agents/AgentTemplatePatchModel.cs | 23 ++++++++ .../Repository/MongoRepository.Agent.cs | 17 ++++++ 8 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentTemplatePatchModel.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs index 5ab249b8..db4db62a 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs @@ -37,6 +37,13 @@ public interface IAgentService Task DeleteAgent(string id); Task UpdateAgent(Agent agent, AgentField updateField); + + /// + /// Path existing templates of agent, cannot create new or delete templates + /// + /// + /// + Task PatchAgentTemplate(Agent agent); Task UpdateAgentFromFile(string id); string GetDataDir(); string GetAgentDataDir(string agentId); diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index c301c738..a451071e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -1,7 +1,6 @@ using BotSharp.Abstraction.Loggers.Models; using BotSharp.Abstraction.Plugins.Models; using BotSharp.Abstraction.Repositories.Filters; -using BotSharp.Abstraction.Repositories.Models; using BotSharp.Abstraction.Tasks.Models; using BotSharp.Abstraction.Users.Models; @@ -35,6 +34,7 @@ public interface IBotSharpRepository bool DeleteAgent(string agentId); List GetAgentResponses(string agentId, string prefix, string intent); string GetAgentTemplate(string agentId, string templateName); + bool PatchAgentTemplate(string agentId, AgentTemplate template); #endregion #region Agent Task diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs index 0cce4209..154d7398 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs @@ -1,6 +1,3 @@ -using BotSharp.Abstraction.Agents.Models; -using BotSharp.Abstraction.Functions.Models; -using BotSharp.Abstraction.Repositories; using BotSharp.Abstraction.Repositories.Enums; using BotSharp.Abstraction.Routing.Models; using System.IO; @@ -106,6 +103,59 @@ public partial class AgentService } } + + public async Task PatchAgentTemplate(Agent agent) + { + var patchResult = string.Empty; + if (agent == null || agent.Templates.IsNullOrEmpty()) + { + patchResult = $"Null agent instance or empty input templates"; + _logger.LogWarning(patchResult); + return patchResult; + } + + var record = _db.GetAgent(agent.Id); + if (record == null) + { + patchResult = $"Cannot find agent {agent.Id}"; + _logger.LogWarning(patchResult); + return patchResult; + } + + var successTemplates = new List(); + var failTemplates = new List(); + foreach (var template in agent.Templates) + { + if (template == null) continue; + + var result = _db.PatchAgentTemplate(agent.Id, template); + if (result) + { + successTemplates.Add(template.Name); + _logger.LogInformation($"Template {template.Name} is updated successfully!"); + } + else + { + failTemplates.Add(template.Name); + _logger.LogWarning($"Template {template.Name} is failed to be updated!"); + } + } + + Utilities.ClearCache(); + + if (!successTemplates.IsNullOrEmpty()) + { + patchResult += $"Success templates:\n{string.Join('\n', successTemplates)}\n\n"; + } + + if (!failTemplates.IsNullOrEmpty()) + { + patchResult += $"Failed templates:\n{string.Join('\n', failTemplates)}"; + } + + return patchResult; + } + private Agent? FetchAgentFileById(string agentId, string filePath) { if (!Directory.Exists(filePath)) return null; diff --git a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs index 650c9c4f..28aac06b 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs @@ -87,6 +87,9 @@ public class BotSharpDbContext : Database, IBotSharpRepository public string GetAgentTemplate(string agentId, string templateName) => throw new NotImplementedException(); + public bool PatchAgentTemplate(string agentId, AgentTemplate template) + => throw new NotImplementedException(); + public List GetAgentResponses(string agentId, string prefix, string intent) => throw new NotImplementedException(); diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs index 20f8bcdd..a46669a5 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs @@ -401,6 +401,25 @@ namespace BotSharp.Core.Repository return string.Empty; } + public bool PatchAgentTemplate(string agentId, AgentTemplate template) + { + if (string.IsNullOrEmpty(agentId) || template == null) return false; + + var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, "templates"); + if (!Directory.Exists(dir)) return false; + + var foundTemplate = Directory.GetFiles(dir).FirstOrDefault(f => + { + var fileName = Path.GetFileNameWithoutExtension(f); + var extension = Path.GetExtension(f).Substring(1); + return fileName.IsEqualTo(template.Name) && extension.IsEqualTo(_agentSettings.TemplateFormat); + }); + + if (foundTemplate == null) return false; + + File.WriteAllText(foundTemplate, template.Content); + return true; + } public void BulkInsertAgents(List agents) { diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs index 77f73061..bb67de2c 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs @@ -110,4 +110,12 @@ public class AgentController : ControllerBase model.Id = agentId; await _agentService.UpdateAgent(model, field); } + + [HttpPatch("/agent/{agentId}/templates")] + public async Task PatchAgentTemplates([FromRoute] string agentId, [FromBody] AgentTemplatePatchModel agent) + { + var model = agent.ToAgent(); + model.Id = agentId; + return await _agentService.PatchAgentTemplate(model); + } } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentTemplatePatchModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentTemplatePatchModel.cs new file mode 100644 index 00000000..2d1beba6 --- /dev/null +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentTemplatePatchModel.cs @@ -0,0 +1,23 @@ +using BotSharp.Abstraction.Agents.Models; + +namespace BotSharp.OpenAPI.ViewModels.Agents; + +public class AgentTemplatePatchModel +{ + public List? Templates { get; set; } + + public AgentTemplatePatchModel() + { + + } + + public Agent ToAgent() + { + var agent = new Agent() + { + Templates = Templates ?? new List(), + }; + + return agent; + } +} diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs index 07d30984..8cffb61f 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs @@ -332,6 +332,23 @@ public partial class MongoRepository return agent.Templates?.FirstOrDefault(x => x.Name == templateName.ToLower())?.Content ?? string.Empty; } + public bool PatchAgentTemplate(string agentId, AgentTemplate template) + { + if (string.IsNullOrEmpty(agentId) || template == null) return false; + + var filter = Builders.Filter.Eq(x => x.Id, agentId); + var agent = _dc.Agents.Find(filter).FirstOrDefault(); + if (agent == null || agent.Templates.IsNullOrEmpty()) return false; + + var foundTemplate = agent.Templates.FirstOrDefault(x => x.Name.IsEqualTo(template.Name)); + if (foundTemplate == null) return false; + + foundTemplate.Content = template.Content; + var update = Builders.Update.Set(x => x.Templates, agent.Templates); + _dc.Agents.UpdateOne(filter, update); + return true; + } + public void BulkInsertAgents(List agents) { if (agents.IsNullOrEmpty()) return; From 4ca70dfdf08b0f1e49f6967cc948561943733aa4 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 9 May 2024 21:37:32 -0500 Subject: [PATCH 107/201] fix log order --- .../Conversations/Models/RoleDialogModel.cs | 1 - .../Controllers/ConversationController.cs | 8 ++++++-- .../BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs index bc9ea0ef..a4aba84d 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs @@ -96,7 +96,6 @@ public class RoleDialogModel : ITrackableMessage Role = role; Content = text; MessageId = Guid.NewGuid().ToString(); - CreatedAt = DateTime.UtcNow; } public override string ToString() diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index 8d91d111..810bc08d 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -171,8 +171,10 @@ public class ConversationController : ControllerBase var conv = _services.GetRequiredService(); var inputMsg = new RoleDialogModel(AgentRole.User, input.Text) { - Files = input.Files + Files = input.Files, + CreatedAt = DateTime.UtcNow }; + if (!string.IsNullOrEmpty(input.TruncateMessageId)) { await conv.TruncateConversation(conversationId, input.TruncateMessageId, inputMsg.MessageId); @@ -215,8 +217,10 @@ public class ConversationController : ControllerBase var conv = _services.GetRequiredService(); var inputMsg = new RoleDialogModel(AgentRole.User, input.Text) { - Files = input.Files + Files = input.Files, + CreatedAt = DateTime.UtcNow }; + if (!string.IsNullOrEmpty(input.TruncateMessageId)) { await conv.TruncateConversation(conversationId, input.TruncateMessageId, inputMsg.MessageId); diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs index e32a1c36..ab662546 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs @@ -417,7 +417,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR Role = input.Message.Role, Content = input.Log, Source = input.Source, - CreateTime = input.Message.CreatedAt + CreateTime = DateTime.UtcNow }; var json = JsonSerializer.Serialize(output, _options.JsonSerializerOptions); @@ -439,7 +439,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR ConversationId = conversationId, MessageId = message.MessageId, States = states, - CreateTime = message.CreatedAt + CreateTime = DateTime.UtcNow }; var convSettings = _services.GetRequiredService(); From 8ce51d4f6fc3e58fd52a95603529d40bc8164a51 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Fri, 10 May 2024 14:29:46 -0500 Subject: [PATCH 108/201] fix invoke function --- .../BotSharp.Core/Routing/RoutingService.InvokeFunction.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs index a750a0ef..2bc11f76 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs @@ -10,7 +10,7 @@ public partial class RoutingService if (function == null) { message.StopCompletion = true; - message.Content = $"Can't find function implementation of {message.FunctionName}."; + message.Content = $"Can't find function implementation of {name}."; _logger.LogError(message.Content); return false; } From 41da7d6627830f56497f96690d5b489c1c0a6b8e Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Fri, 10 May 2024 17:48:55 -0500 Subject: [PATCH 109/201] Remove language detection from router. --- .../templates/planner_prompt.naive.liquid | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 5177a002..3fa0a7f7 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 @@ -9,5 +9,4 @@ Next action agent is inferred based on user lastest response. Expected user goal agent is {{ expected_user_goal_agent }}. {%- else -%} User goal agent is inferred based on user initial request. -{%- endif %} -Detect language based on the overall content in [CONVERSATION] and only include user message. \ No newline at end of file +{%- endif %} \ No newline at end of file From 5699842b49f0c0eedcc286114c8b9fe977d80939 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Fri, 10 May 2024 18:27:41 -0500 Subject: [PATCH 110/201] Save payload only when it's not empty. --- .../Conversations/Services/ConversationService.SendMessage.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs index 489e400e..d6940fe1 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -52,9 +52,9 @@ public partial class ConversationService message.Files?.Clear(); // Save payload - if (replyMessage != null) + if (replyMessage != null && !string.IsNullOrEmpty(replyMessage.Payload)) { - message.Payload = string.IsNullOrEmpty(replyMessage.Payload) ? message.Content : replyMessage.Payload; + message.Payload = replyMessage.Payload; } // Before chat completion hook From 07dc85375e72e17eaeef84cdeee814a3b2706edf Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Fri, 10 May 2024 21:31:16 -0500 Subject: [PATCH 111/201] Add Description to ElementButton --- .../Messaging/Models/RichContent/ElementButton.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs index cd3d9da6..daf3de65 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/ElementButton.cs @@ -8,13 +8,18 @@ public class ElementButton public string Type { get; set; } = "web_url"; [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string Url { get; set; } + public string? Url { get; set; } [Translate] public string Title { get; set; } = string.Empty; + [JsonPropertyName("description")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string Payload { get; set; } + [Translate] + public string? Description { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Payload { get; set; } [JsonPropertyName("is_primary")] public bool IsPrimary { get; set; } From e620d5d16129630aa05bb14503a9222be0cc1f98 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Fri, 10 May 2024 21:59:51 -0500 Subject: [PATCH 112/201] Not set language if exists. --- .../BotSharp.Core/Translation/TranslationService.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index b18c7ab1..07c2c973 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -61,9 +61,11 @@ public class TranslationService : ITranslationService { // Override language if it's Unknown, it's used to output the corresponding language. var states = _services.GetRequiredService(); - var inputLanguage = string.IsNullOrEmpty(translatedStringList.InputLanguage) ? LanguageType.ENGLISH : translatedStringList.InputLanguage; - var languageState = states.GetState("language", inputLanguage); - states.SetState("language", languageState, activeRounds: 1); + if (!states.ContainsState("language")) + { + var inputLanguage = string.IsNullOrEmpty(translatedStringList.InputLanguage) ? LanguageType.ENGLISH : translatedStringList.InputLanguage; + states.SetState("language", inputLanguage, activeRounds: 1); + } var translatedTexts = translatedStringList.Texts; var map = new Dictionary(); From d1baf6d0923da253ef71b6eb8c0da74f980688e7 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Fri, 10 May 2024 22:04:59 -0500 Subject: [PATCH 113/201] minor fix --- .../Providers/ChatCompletionProvider.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs index 4bca97ff..07a3ff8f 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs @@ -278,7 +278,8 @@ public class ChatCompletionProvider : IChatCompletion } else if (message.Role == ChatRole.User) { - var userMessage = new ChatRequestUserMessage(message.Payload ?? message.Content) + var text = !string.IsNullOrWhiteSpace(message.Payload) ? message.Payload : message.Content; + var userMessage = new ChatRequestUserMessage(text) { // To display Planner name in log Name = message.FunctionName, From 8e2a97e39ffdc981475b02b1bc98a3c2eb2ab69a Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Sat, 11 May 2024 22:22:14 -0500 Subject: [PATCH 114/201] Add UserLanguage to IUserIdentity --- .../BotSharp.Abstraction/Users/IUserIdentity.cs | 1 + .../BotSharp.Core/Users/Services/UserIdentity.cs | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IUserIdentity.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IUserIdentity.cs index a0998117..4fecb48c 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/IUserIdentity.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/IUserIdentity.cs @@ -8,4 +8,5 @@ public interface IUserIdentity string FirstName { get; } string LastName { get; } string FullName { get; } + string? UserLanguage { get; } } diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs index b8acdeea..a8eaac5e 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs @@ -56,4 +56,14 @@ public class UserIdentity : IUserIdentity return $"{FirstName} {LastName}".Trim(); } } + + [JsonPropertyName("user_language")] + public string? UserLanguage + { + get + { + _contextAccessor.HttpContext.Request.Headers.TryGetValue("User-Language", out var languages); + return languages.FirstOrDefault(); + } + } } From 434b7a68e7cf4b8e763ac0a7bee43aca3a3695de Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Mon, 13 May 2024 08:57:39 -0500 Subject: [PATCH 115/201] Upgrade LLamaSharp to v0.12 --- .../BotSharp.OpenAPI/BotSharp.OpenAPI.csproj | 8 +++++--- .../BotSharp.OpenAPI/BotSharpOpenApiExtensions.cs | 14 ++++++++++++++ .../BotSharp.Plugin.LLamaSharp.csproj | 2 +- src/WebStarter/WebStarter.csproj | 4 ++-- src/WebStarter/appsettings.json | 4 ++++ 5 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj b/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj index f3495bb9..22fcc9e0 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj +++ b/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj @@ -22,17 +22,19 @@ - + - - + + + + diff --git a/src/Infrastructure/BotSharp.OpenAPI/BotSharpOpenApiExtensions.cs b/src/Infrastructure/BotSharp.OpenAPI/BotSharpOpenApiExtensions.cs index 63a9bc92..eabdba84 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/BotSharpOpenApiExtensions.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/BotSharpOpenApiExtensions.cs @@ -126,6 +126,20 @@ public static class BotSharpOpenApiExtensions }); } + // Wexin OAuth + if (!string.IsNullOrWhiteSpace(config["OAuth:Wexin:ClientId"]) && !string.IsNullOrWhiteSpace(config["OAuth:Wexin:ClientSecret"])) + { + builder = builder.AddWeixin(options => + { + options.ClientId = config["OAuth:GitHub:ClientId"]; + options.ClientSecret = config["OAuth:GitHub:ClientSecret"]; + options.Scope.Add("user:email"); + options.Backchannel = builder.Services.BuildServiceProvider() + .GetRequiredService() + .CreateClient(); + }); + } + // Add services to the container. services.AddControllers() .AddJsonOptions(options => diff --git a/src/Plugins/BotSharp.Plugin.LLamaSharp/BotSharp.Plugin.LLamaSharp.csproj b/src/Plugins/BotSharp.Plugin.LLamaSharp/BotSharp.Plugin.LLamaSharp.csproj index cb9087e9..722dfa41 100644 --- a/src/Plugins/BotSharp.Plugin.LLamaSharp/BotSharp.Plugin.LLamaSharp.csproj +++ b/src/Plugins/BotSharp.Plugin.LLamaSharp/BotSharp.Plugin.LLamaSharp.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/WebStarter/WebStarter.csproj b/src/WebStarter/WebStarter.csproj index 89b09482..bef66c67 100644 --- a/src/WebStarter/WebStarter.csproj +++ b/src/WebStarter/WebStarter.csproj @@ -1,4 +1,4 @@ - + $(TargetFramework) @@ -22,7 +22,7 @@ - + diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index c6555f32..5482e6b1 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -33,6 +33,10 @@ "ClientId": "", "ClientSecret": "", "Version": 22 + }, + "Weixin": { + "AppId": "", + "AppSecret": "" } }, From d12954824b1920377314574d80d4ca409af29c3d Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Mon, 13 May 2024 08:58:26 -0500 Subject: [PATCH 116/201] All exclude some states when cleaning states. --- .../Conversations/IConversationStateService.cs | 2 +- .../Infrastructures/Enums/StateConst.cs | 2 ++ .../Services/ConversationService.UpdateBreakpoint.cs | 5 ++++- .../Conversations/Services/ConversationStateService.cs | 8 +++++++- .../BotSharp.Core/Routing/RoutingService.cs | 2 +- .../BotSharp.Core/Translation/TranslationResponseHook.cs | 2 +- .../BotSharp.Core/Translation/TranslationService.cs | 6 +++--- 7 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs index fe3f9193..5663e9a5 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs @@ -17,6 +17,6 @@ public interface IConversationStateService int activeRounds = -1, string valueType = StateDataType.String, string source = StateSource.User, bool readOnly = false); void SaveStateByArgs(JsonDocument args); bool RemoveState(string name); - void CleanStates(); + void CleanStates(params string[] keepStates); void Save(); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/StateConst.cs b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/StateConst.cs index 8b7fc37c..442c8ef8 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/StateConst.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/StateConst.cs @@ -7,4 +7,6 @@ public class StateConst public const string NEXT_ACTION_AGENT = "next_action_agent"; public const string NEXT_ACTION_REASON = "next_action_reason"; public const string USER_GOAL_AGENT = "user_goal_agent"; + + public const string LANGUAGE = "language"; } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.UpdateBreakpoint.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.UpdateBreakpoint.cs index 6a3397fc..7453a202 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.UpdateBreakpoint.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.UpdateBreakpoint.cs @@ -1,3 +1,5 @@ +using BotSharp.Abstraction.Infrastructures.Enums; + namespace BotSharp.Core.Conversations.Services; public partial class ConversationService : IConversationService @@ -19,7 +21,8 @@ public partial class ConversationService : IConversationService if (resetStates) { var states = _services.GetRequiredService(); - states.CleanStates(); + // keep language state + states.CleanStates(StateConst.LANGUAGE); } var hooks = _services.GetServices() diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index 636d622e..4901a7c6 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -270,7 +270,7 @@ public class ConversationStateService : IConversationStateService, IDisposable return true; } - public void CleanStates() + public void CleanStates(params string[] keepStates) { var routingCtx = _services.GetRequiredService(); var curMsgId = routingCtx.MessageId; @@ -278,6 +278,12 @@ public class ConversationStateService : IConversationStateService, IDisposable foreach (var key in _curStates.Keys) { + // skip state + if (keepStates.Contains(key)) + { + continue; + } + var value = _curStates[key]; if (value == null || !value.Versioning || value.Values.IsNullOrEmpty()) continue; diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index 885728a1..5e0ca8ac 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -88,7 +88,7 @@ public partial class RoutingService : IRoutingService { var translator = _services.GetRequiredService(); - var language = states.GetState("language", LanguageType.UNKNOWN); + var language = states.GetState(StateConst.LANGUAGE, LanguageType.UNKNOWN); if (language != LanguageType.ENGLISH) { message.SecondaryContent = message.Content; diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationResponseHook.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationResponseHook.cs index d79c73a0..755aa110 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationResponseHook.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationResponseHook.cs @@ -31,7 +31,7 @@ namespace BotSharp.Logger.Hooks var agentService = _services.GetRequiredService(); var router = await agentService.LoadAgent(AIAssistant); var translator = _services.GetRequiredService(); - var language = _states.GetState("language", LanguageType.ENGLISH); + var language = _states.GetState(StateConst.LANGUAGE, LanguageType.ENGLISH); if (language != LanguageType.UNKNOWN && language != LanguageType.ENGLISH) { if (message.RichContent != null) diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index 07c2c973..4f23ade1 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -61,10 +61,10 @@ public class TranslationService : ITranslationService { // Override language if it's Unknown, it's used to output the corresponding language. var states = _services.GetRequiredService(); - if (!states.ContainsState("language")) + if (!states.ContainsState(StateConst.LANGUAGE)) { var inputLanguage = string.IsNullOrEmpty(translatedStringList.InputLanguage) ? LanguageType.ENGLISH : translatedStringList.InputLanguage; - states.SetState("language", inputLanguage, activeRounds: 1); + states.SetState(StateConst.LANGUAGE, inputLanguage, activeRounds: 1); } var translatedTexts = translatedStringList.Texts; @@ -302,7 +302,7 @@ public class TranslationService : ITranslationService TemplateDict = new Dictionary { { "text_list", texts }, - { "language", language } + { StateConst.LANGUAGE, language } } }; From 3b68ef75b66f1afb67886a888beaa2468698a493 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 13 May 2024 11:32:11 -0500 Subject: [PATCH 117/201] fix truncate breakpoint --- .../FileRepository/FileRepository.Conversation.cs | 7 +++---- .../Repository/MongoRepository.Conversation.cs | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs index 38f47f9f..5ccd3777 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs @@ -481,7 +481,7 @@ namespace BotSharp.Core.Repository // Handle truncated breakpoints var breakpointDir = Path.Combine(convDir, BREAKPOINT_FILE); var breakpoints = CollectConversationBreakpoints(breakpointDir); - isSaved = HandleTruncatedBreakpoints(breakpointDir, breakpoints, messageId); + isSaved = HandleTruncatedBreakpoints(breakpointDir, breakpoints, refTime); // Remove logs if (cleanLog) @@ -585,10 +585,9 @@ namespace BotSharp.Core.Repository return isSaved; } - private bool HandleTruncatedBreakpoints(string breakpointDir, List breakpoints, string refMessageId) + private bool HandleTruncatedBreakpoints(string breakpointDir, List breakpoints, DateTime refTime) { - var targetIdx = breakpoints.FindIndex(x => x.MessageId == refMessageId); - var truncatedBreakpoints = breakpoints?.Where((x, idx) => idx < targetIdx)? + var truncatedBreakpoints = breakpoints?.Where(x => x.CreatedTime < refTime)? .ToList() ?? new List(); var isSaved = SaveTruncatedBreakpoints(breakpointDir, truncatedBreakpoints); diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs index cfd3199d..aefd0db5 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs @@ -452,8 +452,7 @@ public partial class MongoRepository if (!foundStates.Breakpoints.IsNullOrEmpty()) { var breakpoints = foundStates.Breakpoints ?? new List(); - var targetIdx = breakpoints.FindIndex(x => x.MessageId == messageId); - var truncatedBreakpoints = breakpoints.Where((x, idx) => idx < targetIdx).ToList(); + var truncatedBreakpoints = breakpoints.Where(x => x.CreatedTime < refTime).ToList(); foundStates.Breakpoints = truncatedBreakpoints; } From 8c2b77b45da5255af7e0f7393f8b33c18a296ebe Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 13 May 2024 11:51:33 -0500 Subject: [PATCH 118/201] change name --- .../Conversations/IConversationStateService.cs | 2 +- .../Conversations/Services/ConversationStateService.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs index 5663e9a5..de26ef5b 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs @@ -17,6 +17,6 @@ public interface IConversationStateService int activeRounds = -1, string valueType = StateDataType.String, string source = StateSource.User, bool readOnly = false); void SaveStateByArgs(JsonDocument args); bool RemoveState(string name); - void CleanStates(params string[] keepStates); + void CleanStates(params string[] excludedStates); void Save(); } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index 4901a7c6..57f09c71 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -270,7 +270,7 @@ public class ConversationStateService : IConversationStateService, IDisposable return true; } - public void CleanStates(params string[] keepStates) + public void CleanStates(params string[] excludedStates) { var routingCtx = _services.GetRequiredService(); var curMsgId = routingCtx.MessageId; @@ -279,7 +279,7 @@ public class ConversationStateService : IConversationStateService, IDisposable foreach (var key in _curStates.Keys) { // skip state - if (keepStates.Contains(key)) + if (excludedStates.Contains(key)) { continue; } From 6024a53476a3b314a7661f03fc7f95e5fc9f74d5 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Mon, 13 May 2024 16:00:39 -0500 Subject: [PATCH 119/201] Add Sort parameter to Pagination. --- .../BotSharp.Abstraction/Utilities/Pagination.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs index d11752f6..4e42e2ee 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs @@ -26,6 +26,11 @@ public class Pagination } } + /// + /// Sort by field + /// + public string? Sort { get; set; } + public int Offset { get { return (Page - 1) * Size; } From 1e882e41b24467472bb99ddf1f07e22b9a347e03 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Mon, 13 May 2024 16:09:53 -0500 Subject: [PATCH 120/201] Sort order --- .../BotSharp.Abstraction/Utilities/Pagination.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs index 4e42e2ee..92e3eacb 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs @@ -31,6 +31,11 @@ public class Pagination /// public string? Sort { get; set; } + /// + /// Sort order: asc or desc + /// + public string Order { get; set; } = "asc"; + public int Offset { get { return (Page - 1) * Size; } From f9e63097a9ccb91449039eaa6ad7ca7b63db077b Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 13 May 2024 16:53:41 -0500 Subject: [PATCH 121/201] add chat files --- .../Files/IBotSharpFileService.cs | 3 +- .../Files/Models/MessageFileModel.cs | 32 +++++ .../Files/Models/OutputFileModel.cs | 13 -- .../MLTasks/Settings/LlmModelSetting.cs | 5 + .../BotSharp.Core/BotSharp.Core.csproj | 1 + .../ConversationService.SendMessage.cs | 7 ++ .../Files/BotSharpFileService.cs | 114 ++++++++++-------- .../Routing/RoutingService.InvokeAgent.cs | 8 +- .../Controllers/FileController.cs | 5 +- src/Infrastructure/BotSharp.OpenAPI/Using.cs | 3 +- .../ViewModels/Files/MessageFileViewModel.cs | 34 ++++++ .../Providers/ChatCompletionProvider.cs | 47 ++++++-- 12 files changed, 193 insertions(+), 79 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Files/Models/MessageFileModel.cs delete mode 100644 src/Infrastructure/BotSharp.Abstraction/Files/Models/OutputFileModel.cs create mode 100644 src/Infrastructure/BotSharp.OpenAPI/ViewModels/Files/MessageFileViewModel.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs b/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs index 9c27d7ff..edc04b7b 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs @@ -3,7 +3,8 @@ namespace BotSharp.Abstraction.Files; public interface IBotSharpFileService { string GetDirectory(string conversationId); - IEnumerable GetConversationFiles(string conversationId, string messageId); + IEnumerable GetChatImages(string conversationId, List conversations, int offset = 2); + IEnumerable GetMessageFiles(string conversationId, IEnumerable messageIds, bool imageOnly = false); string? GetMessageFile(string conversationId, string messageId, string fileName); void SaveMessageFiles(string conversationId, string messageId, List files); diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/Models/MessageFileModel.cs b/src/Infrastructure/BotSharp.Abstraction/Files/Models/MessageFileModel.cs new file mode 100644 index 00000000..3ec63fd8 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Files/Models/MessageFileModel.cs @@ -0,0 +1,32 @@ +namespace BotSharp.Abstraction.Files.Models; + +public class MessageFileModel +{ + [JsonPropertyName("message_id")] + public string MessageId { get; set; } + + [JsonPropertyName("file_url")] + public string FileUrl { get; set; } + + [JsonPropertyName("file_storage_url")] + public string FileStorageUrl { get; set; } + + [JsonPropertyName("file_name")] + public string FileName { get; set; } + + [JsonPropertyName("file_type")] + public string FileType { get; set; } + + [JsonPropertyName("content_type")] + public string ContentType { get; set; } + + public MessageFileModel() + { + + } + + public override string ToString() + { + return $"File name: {FileName}, File type: {FileType}, Content type: {ContentType}"; + } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/Models/OutputFileModel.cs b/src/Infrastructure/BotSharp.Abstraction/Files/Models/OutputFileModel.cs deleted file mode 100644 index 962b01e1..00000000 --- a/src/Infrastructure/BotSharp.Abstraction/Files/Models/OutputFileModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace BotSharp.Abstraction.Files.Models; - -public class OutputFileModel -{ - [JsonPropertyName("file_url")] - public string FileUrl { get; set; } - - [JsonPropertyName("file_name")] - public string FileName { get; set; } - - [JsonPropertyName("file_type")] - public string FileType { get; set; } -} diff --git a/src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs b/src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs index 1faf3c52..b86578fe 100644 --- a/src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs +++ b/src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs @@ -27,6 +27,11 @@ public class LlmModelSetting public string Endpoint { get; set; } public LlmModelType Type { get; set; } = LlmModelType.Chat; + /// + /// If true, allow sending images/vidoes to this model + /// + public bool MultiModal { get; set; } + /// /// Prompt cost per 1K token /// diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index 8d05c40b..0ddc52e8 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -159,6 +159,7 @@ + diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs index d6940fe1..4ca64922 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -151,6 +151,13 @@ public partial class ConversationService Message = new TextMessage(response.SecondaryContent ?? response.Content) }; + response.RichContent = new RichContent + { + Recipient = new Recipient { Id = state.GetConversationId() }, + Editor = "file", + Message = new TextMessage(response.SecondaryContent ?? response.Content) + }; + // Patch return function name if (response.PostbackFunctionName != null) { diff --git a/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs b/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs index ad687274..581569a1 100644 --- a/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs +++ b/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.StaticFiles; using System.IO; using System.Threading; @@ -8,9 +9,12 @@ public class BotSharpFileService : IBotSharpFileService private readonly BotSharpDatabaseSettings _dbSettings; private readonly IServiceProvider _services; private readonly string _baseDir; + private readonly IEnumerable _allowedTypes = new List { "image/png", "image/jpeg" }; private const string CONVERSATION_FOLDER = "conversations"; private const string FILE_FOLDER = "files"; + private const int MIN_OFFSET = 1; + private const int MAX_OFFSET = 5; public BotSharpFileService( BotSharpDatabaseSettings dbSettings, @@ -31,29 +35,67 @@ public class BotSharpFileService : IBotSharpFileService return dir; } - public IEnumerable GetConversationFiles(string conversationId, string messageId) + public IEnumerable GetChatImages(string conversationId, List conversations, int offset = 2) { - var outputFiles = new List(); - var dir = GetConversationFileDirectory(conversationId, messageId); - if (string.IsNullOrEmpty(dir)) + var files = new List(); + if (string.IsNullOrEmpty(conversationId) || conversations.IsNullOrEmpty()) { - return outputFiles; + return files; } - foreach (var file in Directory.GetFiles(dir)) + if (offset <= 0) { - var fileName = Path.GetFileNameWithoutExtension(file); - var extension = Path.GetExtension(file); - var fileType = extension.Substring(1); - var model = new OutputFileModel() - { - FileUrl = $"/conversation/{conversationId}/message/{messageId}/file/{fileName}", - FileName = fileName, - FileType = fileType - }; - outputFiles.Add(model); + offset = MIN_OFFSET; } - return outputFiles; + else if (offset > MAX_OFFSET) + { + offset = MAX_OFFSET; + } + + var messageIds = conversations.Select(x => x.MessageId).Distinct().TakeLast(offset).ToList(); + files = GetMessageFiles(conversationId, messageIds, imageOnly: true).ToList(); + return files; + } + + public IEnumerable GetMessageFiles(string conversationId, IEnumerable messageIds, bool imageOnly = false) + { + var files = new List(); + if (messageIds.IsNullOrEmpty()) return files; + + foreach (var messageId in messageIds) + { + var dir = GetConversationFileDirectory(conversationId, messageId); + if (string.IsNullOrEmpty(dir)) + { + continue; + } + + foreach (var file in Directory.GetFiles(dir)) + { + var contentType = GetFileContentType(file); + if (imageOnly && !_allowedTypes.Contains(contentType)) + { + continue; + } + + var fileName = Path.GetFileNameWithoutExtension(file); + var extension = Path.GetExtension(file); + var fileType = extension.Substring(1); + + var model = new MessageFileModel() + { + MessageId = messageId, + FileUrl = $"/conversation/{conversationId}/message/{messageId}/file/{fileName}", + FileStorageUrl = file, + FileName = fileName, + FileType = fileType, + ContentType = contentType + }; + files.Add(model); + } + } + + return files; } public string? GetMessageFile(string conversationId, string messageId, string fileName) @@ -182,42 +224,16 @@ public class BotSharpFileService : IBotSharpFileService return Convert.FromBase64String(base64Str); } - private string GetFileType(string data) + private string GetFileContentType(string filePath) { - if (string.IsNullOrEmpty(data)) + string contentType; + var provider = new FileExtensionContentTypeProvider(); + if (!provider.TryGetContentType(filePath, out contentType)) { - return string.Empty; + contentType = string.Empty; } - var startIdx = data.IndexOf(':'); - var endIdx = data.IndexOf(';'); - var fileType = data.Substring(startIdx + 1, endIdx - startIdx - 1); - return fileType; - } - - private string ParseFileFormat(string type) - { - var parsed = string.Empty; - switch (type) - { - case "image/png": - parsed = ".png"; - break; - case "image/jpeg": - case "image/jpg": - parsed = ".jpeg"; - break; - case "application/pdf": - parsed = ".pdf"; - break; - case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": - parsed = ".xlsx"; - break; - case "text/plain": - parsed = ".txt"; - break; - } - return parsed; + return contentType; } #endregion } diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs index 6bdac0ef..6046c5bb 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs @@ -17,18 +17,18 @@ public partial class RoutingService return false; } - var provide = agent.LlmConfig.Provider; + var provider = agent.LlmConfig.Provider; var model = agent.LlmConfig.Model; - if (provide == null || model == null) + if (provider == null || model == null) { var agentSettings = _services.GetRequiredService(); - provide = agentSettings.LlmConfig.Provider; + provider = agentSettings.LlmConfig.Provider; model = agentSettings.LlmConfig.Model; } var chatCompletion = CompletionProvider.GetChatCompletion(_services, - provider: provide, + provider: provider, model: model); var message = dialogs.Last(); diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs index a24357a2..cfae602c 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs @@ -38,10 +38,11 @@ public class FileController : ControllerBase } [HttpGet("/conversation/{conversationId}/files/{messageId}")] - public IEnumerable GetConversationFiles([FromRoute] string conversationId, [FromRoute] string messageId) + public IEnumerable GetMessageFiles([FromRoute] string conversationId, [FromRoute] string messageId) { var fileService = _services.GetRequiredService(); - return fileService.GetConversationFiles(conversationId, messageId); + var files = fileService.GetMessageFiles(conversationId, new List { messageId }); + return files?.Select(x => MessageFileViewModel.Transform(x))?.ToList() ?? new List(); } [HttpGet("/conversation/{conversationId}/message/{messageId}/file/{fileName}")] diff --git a/src/Infrastructure/BotSharp.OpenAPI/Using.cs b/src/Infrastructure/BotSharp.OpenAPI/Using.cs index f3ba775f..8771b81c 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Using.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Using.cs @@ -28,4 +28,5 @@ global using BotSharp.Abstraction.Files.Models; global using BotSharp.Abstraction.Files; global using BotSharp.OpenAPI.ViewModels.Conversations; global using BotSharp.OpenAPI.ViewModels.Users; -global using BotSharp.OpenAPI.ViewModels.Agents; \ No newline at end of file +global using BotSharp.OpenAPI.ViewModels.Agents; +global using BotSharp.OpenAPI.ViewModels.Files; \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Files/MessageFileViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Files/MessageFileViewModel.cs new file mode 100644 index 00000000..a9eb33bd --- /dev/null +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Files/MessageFileViewModel.cs @@ -0,0 +1,34 @@ +using System.Text.Json.Serialization; + +namespace BotSharp.OpenAPI.ViewModels.Files; + +public class MessageFileViewModel +{ + [JsonPropertyName("file_url")] + public string FileUrl { get; set; } + + [JsonPropertyName("file_name")] + public string FileName { get; set; } + + [JsonPropertyName("file_type")] + public string FileType { get; set; } + + [JsonPropertyName("content_type")] + public string ContentType { get; set; } + + public MessageFileViewModel() + { + + } + + public static MessageFileViewModel Transform(MessageFileModel model) + { + return new MessageFileViewModel + { + FileUrl = model.FileUrl, + FileName = model.FileName, + FileType = model.FileType, + ContentType = model.ContentType + }; + } +} diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs index 07a3ff8f..b2bbe71b 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs @@ -4,13 +4,17 @@ using BotSharp.Abstraction.Agents.Enums; using BotSharp.Abstraction.Agents.Models; using BotSharp.Abstraction.Conversations; using BotSharp.Abstraction.Conversations.Models; +using BotSharp.Abstraction.Files; +using BotSharp.Abstraction.Files.Models; using BotSharp.Abstraction.Loggers; using BotSharp.Abstraction.MLTasks; +using BotSharp.Abstraction.Utilities; using BotSharp.Plugin.AzureOpenAI.Settings; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading.Tasks; @@ -218,6 +222,16 @@ public class ChatCompletionProvider : IChatCompletion protected (string, ChatCompletionsOptions) PrepareOptions(Agent agent, List conversations) { var agentService = _services.GetRequiredService(); + var fileService = _services.GetRequiredService(); + var state = _services.GetRequiredService(); + var settingsService = _services.GetRequiredService(); + var settings = settingsService.GetSetting(Provider, _model); + + var chatFiles = new List(); + if (settings != null && settings.MultiModal) + { + chatFiles = fileService.GetChatImages(state.GetConversationId(), conversations, offset: 2).ToList(); + } var chatCompletionsOptions = new ChatCompletionsOptions(); @@ -279,19 +293,34 @@ public class ChatCompletionProvider : IChatCompletion else if (message.Role == ChatRole.User) { var text = !string.IsNullOrWhiteSpace(message.Payload) ? message.Payload : message.Content; - var userMessage = new ChatRequestUserMessage(text) + var chatItems = new List() + { + new ChatMessageTextContentItem(text) + }; + + var files = chatFiles.Where(x => x.MessageId == message.MessageId).ToList(); + if (!files.IsNullOrEmpty()) + { + foreach (var file in files) + { + using var stream = File.OpenRead(file.FileStorageUrl); + chatItems.Add(new ChatMessageImageContentItem(stream, file.ContentType, ChatMessageImageDetailLevel.Low)); + } + } + + //if (!string.IsNullOrEmpty(message.ImageUrl)) + //{ + // var uri = new Uri(message.ImageUrl); + // userMessage.MultimodalContentItems.Add( + // new ChatMessageImageContentItem(uri, ChatMessageImageDetailLevel.Low)); + //} + + var userMessage = new ChatRequestUserMessage(chatItems) { // To display Planner name in log Name = message.FunctionName, }; - if (!string.IsNullOrEmpty(message.ImageUrl)) - { - var uri = new Uri(message.ImageUrl); - userMessage.MultimodalContentItems.Add( - new ChatMessageImageContentItem(uri, ChatMessageImageDetailLevel.Low)); - } - chatCompletionsOptions.Messages.Add(userMessage); } else if (message.Role == ChatRole.Assistant) @@ -301,7 +330,7 @@ public class ChatCompletionProvider : IChatCompletion } // https://community.openai.com/t/cheat-sheet-mastering-temperature-and-top-p-in-chatgpt-api-a-few-tips-and-tricks-on-controlling-the-creativity-deterministic-output-of-prompt-responses/172683 - var state = _services.GetRequiredService(); + //var state = _services.GetRequiredService(); var temperature = float.Parse(state.GetState("temperature", "0.0")); var samplingFactor = float.Parse(state.GetState("sampling_factor", "0.0")); chatCompletionsOptions.Temperature = temperature; From f3bbb0259f281269db4fd58deaae205449e3dd50 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 14 May 2024 10:14:08 -0500 Subject: [PATCH 122/201] remove test code --- .../Services/ConversationService.SendMessage.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs index 4ca64922..d6940fe1 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -151,13 +151,6 @@ public partial class ConversationService Message = new TextMessage(response.SecondaryContent ?? response.Content) }; - response.RichContent = new RichContent - { - Recipient = new Recipient { Id = state.GetConversationId() }, - Editor = "file", - Message = new TextMessage(response.SecondaryContent ?? response.Content) - }; - // Patch return function name if (response.PostbackFunctionName != null) { From 83a78d2b5af71219fc59a5ded3e541ae80c341d7 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 14 May 2024 11:51:39 -0500 Subject: [PATCH 123/201] add instruct multi modal --- .../Files/IBotSharpFileService.cs | 7 +++ .../Files/Models/BotSharpFile.cs | 11 ++-- .../Files/BotSharpFileService.cs | 59 ++++++++++++------- .../Infrastructures/CompletionProvider.cs | 2 +- .../Controllers/InstructModeController.cs | 35 ++++++++++- .../Providers/ChatCompletionProvider.cs | 22 ++++++- 6 files changed, 104 insertions(+), 32 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs b/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs index edc04b7b..272abf0d 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs @@ -18,4 +18,11 @@ public interface IBotSharpFileService /// bool DeleteMessageFiles(string conversationId, IEnumerable messageIds, string targetMessageId, string? newMessageId = null); bool DeleteConversationFiles(IEnumerable conversationIds); + + /// + /// Get file bytes and content type from data, e.g., "data:image/png;base64,aaaaaaaaa" + /// + /// + /// + (string, byte[]) GetFileInfoFromData(string data); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs b/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs index f679d52e..9581b83f 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs @@ -4,14 +4,11 @@ namespace BotSharp.Abstraction.Files.Models; public class BotSharpFile { [JsonPropertyName("file_name")] - public string FileName { get; set; } + public string FileName { get; set; } = string.Empty; [JsonPropertyName("file_data")] - public string FileData { get; set; } + public string FileData { get; set; } = string.Empty; - [JsonPropertyName("content_type")] - public string ContentType { get; set; } - - [JsonPropertyName("file_size")] - public int FileSize { get; set; } + [JsonPropertyName("file_url")] + public string FileUrl { get; set; } = string.Empty; } diff --git a/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs b/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs index 581569a1..d7e961be 100644 --- a/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs +++ b/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs @@ -8,6 +8,7 @@ public class BotSharpFileService : IBotSharpFileService { private readonly BotSharpDatabaseSettings _dbSettings; private readonly IServiceProvider _services; + private readonly ILogger _logger; private readonly string _baseDir; private readonly IEnumerable _allowedTypes = new List { "image/png", "image/jpeg" }; @@ -18,9 +19,11 @@ public class BotSharpFileService : IBotSharpFileService public BotSharpFileService( BotSharpDatabaseSettings dbSettings, + ILogger logger, IServiceProvider services) { _dbSettings = dbSettings; + _logger = logger; _services = services; _baseDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, dbSettings.FileRepository); } @@ -117,19 +120,26 @@ public class BotSharpFileService : IBotSharpFileService var dir = GetConversationFileDirectory(conversationId, messageId, createNewDir: true); if (string.IsNullOrEmpty(dir)) return; - for (int i = 0; i < files.Count; i++) + try { - var file = files[i]; - if (string.IsNullOrEmpty(file.FileData)) + for (int i = 0; i < files.Count; i++) { - continue; - } + var file = files[i]; + if (string.IsNullOrEmpty(file.FileData)) + { + continue; + } - var bytes = GetFileBytes(file.FileData); - var fileType = Path.GetExtension(file.FileName); - var fileName = $"{i + 1}{fileType}"; - Thread.Sleep(100); - File.WriteAllBytes(Path.Combine(dir, fileName), bytes); + var (_, bytes) = GetFileInfoFromData(file.FileData); + var fileType = Path.GetExtension(file.FileName); + var fileName = $"{i + 1}{fileType}"; + Thread.Sleep(100); + File.WriteAllBytes(Path.Combine(dir, fileName), bytes); + } + } + catch (Exception ex) + { + _logger.LogError($"Error when saving conversation files: {ex.Message}"); } } @@ -179,6 +189,23 @@ public class BotSharpFileService : IBotSharpFileService return true; } + public (string, byte[]) GetFileInfoFromData(string data) + { + if (string.IsNullOrEmpty(data)) + { + return (string.Empty, new byte[0]); + } + + var typeStartIdx = data.IndexOf(':'); + var typeEndIdx = data.IndexOf(';'); + var contentType = data.Substring(typeStartIdx + 1, typeEndIdx - typeStartIdx - 1); + + var base64startIdx = data.IndexOf(','); + var base64Str = data.Substring(base64startIdx + 1); + + return (contentType, Convert.FromBase64String(base64Str)); + } + #region Private methods private string GetConversationFileDirectory(string? conversationId, string? messageId, bool createNewDir = false) { @@ -212,18 +239,6 @@ public class BotSharpFileService : IBotSharpFileService return dir; } - private byte[] GetFileBytes(string data) - { - if (string.IsNullOrEmpty(data)) - { - return new byte[0]; - } - - var startIdx = data.IndexOf(','); - var base64Str = data.Substring(startIdx + 1); - return Convert.FromBase64String(base64Str); - } - private string GetFileContentType(string filePath) { string contentType; diff --git a/src/Infrastructure/BotSharp.Core/Infrastructures/CompletionProvider.cs b/src/Infrastructure/BotSharp.Core/Infrastructures/CompletionProvider.cs index c55ed9a8..bc0266ed 100644 --- a/src/Infrastructure/BotSharp.Core/Infrastructures/CompletionProvider.cs +++ b/src/Infrastructure/BotSharp.Core/Infrastructures/CompletionProvider.cs @@ -47,7 +47,7 @@ public class CompletionProvider logger.LogError($"Can't resolve completion provider by {provider}"); } - completer.SetModelName(model); + completer?.SetModelName(model); return completer; } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs index 985526d9..9f00a3e0 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs @@ -11,10 +11,12 @@ namespace BotSharp.OpenAPI.Controllers; public class InstructModeController : ControllerBase { private readonly IServiceProvider _services; + private readonly ILogger _logger; - public InstructModeController(IServiceProvider services) + public InstructModeController(IServiceProvider services, ILogger logger) { _services = services; + _logger = logger; } [HttpPost("/instruct/{agentId}")] @@ -72,4 +74,35 @@ public class InstructModeController : ControllerBase }); return message.Content; } + + [HttpPost("/instruct/multi-modal")] + public async Task MultiModalCompletion([FromBody] IncomingMessageModel input) + { + var state = _services.GetRequiredService(); + input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); + state.SetState("provider", input.Provider, source: StateSource.External) + .SetState("model", input.Model, source: StateSource.External) + .SetState("model_id", input.ModelId, source: StateSource.External); + + try + { + var completion = CompletionProvider.GetChatCompletion(_services, input.Provider ?? "openai", input.Model ?? "gpt-4-turbo"); + var message = await completion.GetChatCompletions(new Agent() + { + Id = Guid.Empty.ToString(), + }, new List + { + new RoleDialogModel(AgentRole.User, input.Text) + { + Files = input.Files + } + }); + return message.Content; + } + catch (Exception ex) + { + _logger.LogError($"Error in analyzing files. {ex.Message}"); + return $"Error in analyzing files. {ex.Message}"; + } + } } diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs index b2bbe71b..d884e386 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs @@ -16,6 +16,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.InteropServices.ComTypes; using System.Threading.Tasks; namespace BotSharp.Plugin.AzureOpenAI.Providers; @@ -226,9 +227,10 @@ public class ChatCompletionProvider : IChatCompletion var state = _services.GetRequiredService(); var settingsService = _services.GetRequiredService(); var settings = settingsService.GetSetting(Provider, _model); + var allowMultiModal = settings != null && settings.MultiModal; var chatFiles = new List(); - if (settings != null && settings.MultiModal) + if (allowMultiModal) { chatFiles = fileService.GetChatImages(state.GetConversationId(), conversations, offset: 2).ToList(); } @@ -308,6 +310,24 @@ public class ChatCompletionProvider : IChatCompletion } } + if (allowMultiModal && !message.Files.IsNullOrEmpty()) + { + foreach (var file in message.Files) + { + if (!string.IsNullOrEmpty(file.FileUrl)) + { + var uri = new Uri(file.FileUrl); + chatItems.Add(new ChatMessageImageContentItem(uri, ChatMessageImageDetailLevel.Low)); + } + else if (!string.IsNullOrEmpty(file.FileData)) + { + var (contentType, bytes) = fileService.GetFileInfoFromData(file.FileData); + using var stream = new MemoryStream(bytes, 0, bytes.Length); + chatItems.Add(new ChatMessageImageContentItem(stream, contentType, ChatMessageImageDetailLevel.Low)); + } + } + } + //if (!string.IsNullOrEmpty(message.ImageUrl)) //{ // var uri = new Uri(message.ImageUrl); From 7e908d33868a8a972ed18663929312bf747584c9 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 14 May 2024 11:55:11 -0500 Subject: [PATCH 124/201] add comment --- .../BotSharp.Abstraction/Files/Models/BotSharpFile.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs b/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs index 9581b83f..de226f58 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs @@ -6,6 +6,9 @@ public class BotSharpFile [JsonPropertyName("file_name")] public string FileName { get; set; } = string.Empty; + /// + /// File data, e.g., "data:image/png;base64,aaaaaaaa" + /// [JsonPropertyName("file_data")] public string FileData { get; set; } = string.Empty; From 3d3359cb951c40a7b04584e764fe3a837cfd3e74 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 14 May 2024 13:34:10 -0500 Subject: [PATCH 125/201] filter by model id and multi-modal --- .../MLTasks/ILlmProviderService.cs | 2 +- .../Infrastructures/CompletionProvider.cs | 13 +++++++++---- .../Infrastructures/LlmProviderService.cs | 4 ++-- .../Controllers/InstructModeController.cs | 5 +---- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/MLTasks/ILlmProviderService.cs b/src/Infrastructure/BotSharp.Abstraction/MLTasks/ILlmProviderService.cs index 75fd60e6..120304d2 100644 --- a/src/Infrastructure/BotSharp.Abstraction/MLTasks/ILlmProviderService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/MLTasks/ILlmProviderService.cs @@ -6,6 +6,6 @@ public interface ILlmProviderService { LlmModelSetting GetSetting(string provider, string model); List GetProviders(); - LlmModelSetting GetProviderModel(string provider, string id); + LlmModelSetting GetProviderModel(string provider, string id, bool multiModal = false); List GetProviderModels(string provider); } diff --git a/src/Infrastructure/BotSharp.Core/Infrastructures/CompletionProvider.cs b/src/Infrastructure/BotSharp.Core/Infrastructures/CompletionProvider.cs index bc0266ed..ace1e664 100644 --- a/src/Infrastructure/BotSharp.Core/Infrastructures/CompletionProvider.cs +++ b/src/Infrastructure/BotSharp.Core/Infrastructures/CompletionProvider.cs @@ -35,10 +35,13 @@ public class CompletionProvider public static IChatCompletion GetChatCompletion(IServiceProvider services, string? provider = null, string? model = null, + string? modelId = null, + bool multiModal = false, AgentLlmConfig? agentConfig = null) { var completions = services.GetServices(); - (provider, model) = GetProviderAndModel(services, provider: provider, model: model, agentConfig: agentConfig); + (provider, model) = GetProviderAndModel(services, provider: provider, model: model, modelId: modelId, + multiModal: multiModal, agentConfig: agentConfig); var completer = completions.FirstOrDefault(x => x.Provider == provider); if (completer == null) @@ -55,6 +58,8 @@ public class CompletionProvider private static (string, string) GetProviderAndModel(IServiceProvider services, string? provider = null, string? model = null, + string? modelId = null, + bool multiModal = false, AgentLlmConfig? agentConfig = null) { var agentSetting = services.GetRequiredService(); @@ -73,11 +78,11 @@ public class CompletionProvider { model = state.GetState("model", model ?? "gpt-35-turbo-4k"); } - else if (state.ContainsState("model_id")) + else if (state.ContainsState("model_id") || !string.IsNullOrEmpty(modelId)) { - var modelId = state.GetState("model_id"); + var modelIdentity = state.ContainsState("model_id") ? state.GetState("model_id") : modelId; var llmProviderService = services.GetRequiredService(); - model = llmProviderService.GetProviderModel(provider, modelId)?.Name; + model = llmProviderService.GetProviderModel(provider, modelIdentity, multiModal)?.Name; } } diff --git a/src/Infrastructure/BotSharp.Core/Infrastructures/LlmProviderService.cs b/src/Infrastructure/BotSharp.Core/Infrastructures/LlmProviderService.cs index eb92ac51..7d92a687 100644 --- a/src/Infrastructure/BotSharp.Core/Infrastructures/LlmProviderService.cs +++ b/src/Infrastructure/BotSharp.Core/Infrastructures/LlmProviderService.cs @@ -44,10 +44,10 @@ public class LlmProviderService : ILlmProviderService ?.Models ?? new List(); } - public LlmModelSetting GetProviderModel(string provider, string id) + public LlmModelSetting GetProviderModel(string provider, string id, bool multiModal = false) { var models = GetProviderModels(provider) - .Where(x => x.Id == id) + .Where(x => x.Id == id && x.MultiModal == multiModal) .ToList(); var random = new Random(); diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs index 9f00a3e0..96fe3728 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs @@ -80,13 +80,10 @@ public class InstructModeController : ControllerBase { var state = _services.GetRequiredService(); input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); - state.SetState("provider", input.Provider, source: StateSource.External) - .SetState("model", input.Model, source: StateSource.External) - .SetState("model_id", input.ModelId, source: StateSource.External); try { - var completion = CompletionProvider.GetChatCompletion(_services, input.Provider ?? "openai", input.Model ?? "gpt-4-turbo"); + var completion = CompletionProvider.GetChatCompletion(_services, provider: "openai", modelId: "gpt-4-turbo", multiModal: true); var message = await completion.GetChatCompletions(new Agent() { Id = Guid.Empty.ToString(), From ed9e6457fea8d674f4a549bd49a031ba0d4e88b4 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Tue, 14 May 2024 13:45:20 -0500 Subject: [PATCH 126/201] Fix translation issue. --- .../BotSharp.Core/Translation/TranslationService.cs | 10 ++++++---- .../templates/translation_prompt.liquid | 6 ++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index 4f23ade1..e7dc107c 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -54,8 +54,10 @@ public class TranslationService : ITranslationService model: _router?.LlmConfig?.Model); var template = _router.Templates.First(x => x.Name == "translation_prompt").Content; - var texts = unique.ToArray(); - var translatedStringList = await InnerTranslate(JsonSerializer.Serialize(texts, _options.JsonSerializerOptions), language, template); + var texts = unique.ToArray() + .Select((text, i) => $"{i + 1}. {text}") + .ToList(); + var translatedStringList = await InnerTranslate(texts, language, template); try { @@ -70,7 +72,7 @@ public class TranslationService : ITranslationService var translatedTexts = translatedStringList.Texts; var map = new Dictionary(); - for (var i = 0; i < texts.Length; i++) + for (var i = 0; i < texts.Count; i++) { map.Add(texts[i], translatedTexts[i]); } @@ -293,7 +295,7 @@ public class TranslationService : ITranslationService /// /// /// - private async Task InnerTranslate(string texts, string language, string template) + private async Task InnerTranslate(List texts, string language, string template) { var translator = new Agent { diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid index 4cd12bd0..9d9672b5 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid @@ -1,5 +1,7 @@ -{{ text_list }} +{% for text in text_list %} +{{ text }} +{% endfor %} ===== Translate the above sentences in the list into {{ language }}. -Output the translated text in JSON {"input_lang":"", "output_lang":"{{ language }}", "texts":[""]}, input_lang is based on the original sentences. \ No newline at end of file +Output the translated text in JSON {"input_lang":"", "output_lang":"{{ language }}", "texts":[]}, input_lang is based on the original sentences. \ No newline at end of file From 971018ba503df6acd85bd405506a81490c9fb5bf Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 14 May 2024 14:30:27 -0500 Subject: [PATCH 127/201] remove error message --- .../BotSharp.OpenAPI/Controllers/InstructModeController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs index 96fe3728..157cc33a 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs @@ -83,7 +83,7 @@ public class InstructModeController : ControllerBase try { - var completion = CompletionProvider.GetChatCompletion(_services, provider: "openai", modelId: "gpt-4-turbo", multiModal: true); + var completion = CompletionProvider.GetChatCompletion(_services, provider: "openai", modelId: "gpt-multi-modal", multiModal: true); var message = await completion.GetChatCompletions(new Agent() { Id = Guid.Empty.ToString(), @@ -99,7 +99,7 @@ public class InstructModeController : ControllerBase catch (Exception ex) { _logger.LogError($"Error in analyzing files. {ex.Message}"); - return $"Error in analyzing files. {ex.Message}"; + return $"Error in analyzing files."; } } } From 535e20ea90d421d8a432bc1e21e9160c853a03e5 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Tue, 14 May 2024 14:47:36 -0500 Subject: [PATCH 128/201] Fix map key. --- .../BotSharp.Core/Translation/TranslationService.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index e7dc107c..38b3cedf 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -1,3 +1,4 @@ +using Amazon.Runtime.Internal.Transform; using BotSharp.Abstraction.Infrastructures.Enums; using BotSharp.Abstraction.MLTasks; using BotSharp.Abstraction.Options; @@ -54,8 +55,9 @@ public class TranslationService : ITranslationService model: _router?.LlmConfig?.Model); var template = _router.Templates.First(x => x.Name == "translation_prompt").Content; + var keys = unique.ToArray(); var texts = unique.ToArray() - .Select((text, i) => $"{i + 1}. {text}") + .Select((text, i) => $"{i + 1}. \"{text}\"") .ToList(); var translatedStringList = await InnerTranslate(texts, language, template); @@ -74,7 +76,7 @@ public class TranslationService : ITranslationService for (var i = 0; i < texts.Count; i++) { - map.Add(texts[i], translatedTexts[i]); + map.Add(keys[0], translatedTexts[i]); } clonedData = Assign(clonedData, map); From d33e0f69185d131b0f4683fb33548b8d6c7f3f57 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 14 May 2024 15:10:21 -0500 Subject: [PATCH 129/201] change model id --- .../BotSharp.OpenAPI/Controllers/InstructModeController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs index 157cc33a..a884fd7e 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs @@ -83,7 +83,7 @@ public class InstructModeController : ControllerBase try { - var completion = CompletionProvider.GetChatCompletion(_services, provider: "openai", modelId: "gpt-multi-modal", multiModal: true); + var completion = CompletionProvider.GetChatCompletion(_services, provider: "openai", modelId: "gpt-4", multiModal: true); var message = await completion.GetChatCompletions(new Agent() { Id = Guid.Empty.ToString(), From c29e76c18412115b3a6046ba2dd4cbd47d2d15d3 Mon Sep 17 00:00:00 2001 From: "C. Oceania" Date: Tue, 14 May 2024 15:28:24 -0500 Subject: [PATCH 130/201] Update TranslationService.cs --- .../BotSharp.Core/Translation/TranslationService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index 38b3cedf..d24fd7d4 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -76,7 +76,7 @@ public class TranslationService : ITranslationService for (var i = 0; i < texts.Count; i++) { - map.Add(keys[0], translatedTexts[i]); + map[keys[i]] = translatedTexts[i]; } clonedData = Assign(clonedData, map); From 1fa31ad68046fe5fdfb570824d30ea37f6e858ba Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Wed, 15 May 2024 06:43:05 -0500 Subject: [PATCH 131/201] release v1.4 --- Directory.Build.props | 4 ++-- .../BotSharp.Plugin.AnthropicAI.csproj | 10 +++++++--- src/Plugins/BotSharp.Plugin.AnthropicAI/Using.cs | 6 ++++++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 99a8ba4c..068b6e13 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,8 +2,8 @@ net8.0 10.0 - 1.3.1 - false + 1.4.0 + true false \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.AnthropicAI/BotSharp.Plugin.AnthropicAI.csproj b/src/Plugins/BotSharp.Plugin.AnthropicAI/BotSharp.Plugin.AnthropicAI.csproj index 357e827f..40f54f60 100644 --- a/src/Plugins/BotSharp.Plugin.AnthropicAI/BotSharp.Plugin.AnthropicAI.csproj +++ b/src/Plugins/BotSharp.Plugin.AnthropicAI/BotSharp.Plugin.AnthropicAI.csproj @@ -1,9 +1,13 @@ - + - net8.0 - enable + netstandard2.1 enable + $(LangVersion) + $(BotSharpVersion) + $(GeneratePackageOnBuild) + $(GenerateDocumentationFile) + $(SolutionDir)packages diff --git a/src/Plugins/BotSharp.Plugin.AnthropicAI/Using.cs b/src/Plugins/BotSharp.Plugin.AnthropicAI/Using.cs index ceb477d6..d00446fc 100644 --- a/src/Plugins/BotSharp.Plugin.AnthropicAI/Using.cs +++ b/src/Plugins/BotSharp.Plugin.AnthropicAI/Using.cs @@ -1,3 +1,9 @@ +global using System; +global using System.Collections.Generic; +global using System.Text; +global using System.Threading.Tasks; +global using System.Linq; +global using System.Text.Json; global using Anthropic.SDK; global using Anthropic.SDK.Constants; global using Anthropic.SDK.Messaging; From 6fba6c8e76384af35170b6ee2000a67ad8e8334f Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 15 May 2024 11:23:02 -0500 Subject: [PATCH 132/201] add conv user endpoint --- .../Controllers/ConversationController.cs | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index 810bc08d..d84e62fa 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -1,8 +1,6 @@ using BotSharp.Abstraction.Routing; using Newtonsoft.Json.Serialization; using Newtonsoft.Json; -using BotSharp.Abstraction.Files.Models; -using BotSharp.Abstraction.Files; namespace BotSharp.OpenAPI.Controllers; @@ -138,6 +136,34 @@ public class ConversationController : ControllerBase return result; } + [HttpGet("/conversation/{conversationId}/user")] + public async Task GetConversationUser([FromRoute] string conversationId) + { + var service = _services.GetRequiredService(); + var conversations = await service.GetConversations(new ConversationFilter + { + Id = conversationId + }); + + var userService = _services.GetRequiredService(); + var conversation = conversations?.Items?.FirstOrDefault(); + var userId = conversation == null ? _user.Id : conversation.UserId; + var user = await userService.GetUser(userId); + if (user == null) + { + return new UserViewModel + { + Id = _user.Id, + FirstName = _user.FirstName, + LastName = _user.LastName, + Email = _user.Email, + Source = "Unknown" + }; + } + + return UserViewModel.FromUser(user); + } + [HttpDelete("/conversation/{conversationId}")] public async Task DeleteConversation([FromRoute] string conversationId) { From 2a9b5ec0c89b51045ef956a3d4639feba40b4f0e Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 15 May 2024 11:23:29 -0500 Subject: [PATCH 133/201] minor change --- .../BotSharp.OpenAPI/Controllers/ConversationController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index d84e62fa..34095acf 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -154,6 +154,7 @@ public class ConversationController : ControllerBase return new UserViewModel { Id = _user.Id, + UserName = _user.UserName, FirstName = _user.FirstName, LastName = _user.LastName, Email = _user.Email, From 842f1e788edf5237c3a1eff1fb9438e1e0a4b7cb Mon Sep 17 00:00:00 2001 From: "C. Oceania" Date: Wed, 15 May 2024 14:09:38 -0500 Subject: [PATCH 134/201] Update translation_prompt.liquid --- .../templates/translation_prompt.liquid | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid index 9d9672b5..be4f1077 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid @@ -1,7 +1,9 @@ {% for text in text_list %} {{ text }} {% endfor %} - ===== -Translate the above sentences in the list into {{ language }}. -Output the translated text in JSON {"input_lang":"", "output_lang":"{{ language }}", "texts":[]}, input_lang is based on the original sentences. \ No newline at end of file +Translate the above sentences into {{ language }}. +Output the translated text in JSON {"input_lang":"original text language", "output_lang":"{{ language }}", "texts":[""]}. +Do not include the serial number before each sentence. +Do not include double quotes outside the sentence. +The number of output sentences must be {{ text_list | size }}. From 3bf73bec4aeb05c2d4ecb5e5927cbff2de032f39 Mon Sep 17 00:00:00 2001 From: "C. Oceania" Date: Wed, 15 May 2024 14:32:59 -0500 Subject: [PATCH 135/201] Update RoutingService.cs --- src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index 5e0ca8ac..1a9ce427 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -88,7 +88,7 @@ public partial class RoutingService : IRoutingService { var translator = _services.GetRequiredService(); - var language = states.GetState(StateConst.LANGUAGE, LanguageType.UNKNOWN); + var language = states.GetState(StateConst.LANGUAGE, LanguageType.ENGLISH); if (language != LanguageType.ENGLISH) { message.SecondaryContent = message.Content; From 03837c1e8348c6c5124ebe0cc0a6981be82cbe11 Mon Sep 17 00:00:00 2001 From: "C. Oceania" Date: Wed, 15 May 2024 14:34:57 -0500 Subject: [PATCH 136/201] Update ChatCompletionProvider.cs --- .../Providers/ChatCompletionProvider.cs | 92 +++++++++++-------- 1 file changed, 54 insertions(+), 38 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs index d884e386..990a74ab 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs @@ -295,51 +295,64 @@ public class ChatCompletionProvider : IChatCompletion else if (message.Role == ChatRole.User) { var text = !string.IsNullOrWhiteSpace(message.Payload) ? message.Payload : message.Content; - var chatItems = new List() - { - new ChatMessageTextContentItem(text) - }; - var files = chatFiles.Where(x => x.MessageId == message.MessageId).ToList(); - if (!files.IsNullOrEmpty()) + ChatRequestUserMessage userMessage = null; + if (allowMultiModal) { - foreach (var file in files) + var chatItems = new List() { - using var stream = File.OpenRead(file.FileStorageUrl); - chatItems.Add(new ChatMessageImageContentItem(stream, file.ContentType, ChatMessageImageDetailLevel.Low)); - } - } - - if (allowMultiModal && !message.Files.IsNullOrEmpty()) - { - foreach (var file in message.Files) + new ChatMessageTextContentItem(text) + }; + + var files = chatFiles.Where(x => x.MessageId == message.MessageId).ToList(); + if (!files.IsNullOrEmpty()) { - if (!string.IsNullOrEmpty(file.FileUrl)) + foreach (var file in files) { - var uri = new Uri(file.FileUrl); - chatItems.Add(new ChatMessageImageContentItem(uri, ChatMessageImageDetailLevel.Low)); - } - else if (!string.IsNullOrEmpty(file.FileData)) - { - var (contentType, bytes) = fileService.GetFileInfoFromData(file.FileData); - using var stream = new MemoryStream(bytes, 0, bytes.Length); - chatItems.Add(new ChatMessageImageContentItem(stream, contentType, ChatMessageImageDetailLevel.Low)); + using var stream = File.OpenRead(file.FileStorageUrl); + chatItems.Add(new ChatMessageImageContentItem(stream, file.ContentType, ChatMessageImageDetailLevel.Low)); } } + + if (!message.Files.IsNullOrEmpty()) + { + foreach (var file in message.Files) + { + if (!string.IsNullOrEmpty(file.FileUrl)) + { + var uri = new Uri(file.FileUrl); + chatItems.Add(new ChatMessageImageContentItem(uri, ChatMessageImageDetailLevel.Low)); + } + else if (!string.IsNullOrEmpty(file.FileData)) + { + var (contentType, bytes) = fileService.GetFileInfoFromData(file.FileData); + using var stream = new MemoryStream(bytes, 0, bytes.Length); + chatItems.Add(new ChatMessageImageContentItem(stream, contentType, ChatMessageImageDetailLevel.Low)); + } + } + } + + //if (!string.IsNullOrEmpty(message.ImageUrl)) + //{ + // var uri = new Uri(message.ImageUrl); + // userMessage.MultimodalContentItems.Add( + // new ChatMessageImageContentItem(uri, ChatMessageImageDetailLevel.Low)); + //} + + userMessage = new ChatRequestUserMessage(chatItems) + { + // To display Planner name in log + Name = message.FunctionName, + }; } - - //if (!string.IsNullOrEmpty(message.ImageUrl)) - //{ - // var uri = new Uri(message.ImageUrl); - // userMessage.MultimodalContentItems.Add( - // new ChatMessageImageContentItem(uri, ChatMessageImageDetailLevel.Low)); - //} - - var userMessage = new ChatRequestUserMessage(chatItems) + else { - // To display Planner name in log - Name = message.FunctionName, - }; + userMessage = new ChatRequestUserMessage(text) + { + // To display Planner name in log + Name = message.FunctionName, + }; + } chatCompletionsOptions.Messages.Add(userMessage); } @@ -396,9 +409,12 @@ public class ChatCompletionProvider : IChatCompletion else if (x.Role == ChatRole.User) { var m = x as ChatRequestUserMessage; + var content = m.Content ?? string.Join(", ", m.MultimodalContentItems + .Where(m => m is ChatMessageTextContentItem) + .Select(m => (m as ChatMessageTextContentItem)?.Text)); return !string.IsNullOrEmpty(m.Name) && m.Name != "route_to_agent" ? - $"{m.Name}: {m.Content}" : - $"{m.Role}: {m.Content}"; + $"{m.Name}: {content}" : + $"{m.Role}: {content}"; } else if (x.Role == ChatRole.Assistant) { From 66e233a11c85b6544d63216d03d4351ff150c9e2 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 15 May 2024 15:54:08 -0500 Subject: [PATCH 137/201] add user role filter --- .../FileRepository/FileRepository.User.cs | 2 + .../Controllers/AgentController.cs | 18 +++++++- .../Controllers/ConversationController.cs | 43 +++++++++++++------ .../ViewModels/Agents/AgentViewModel.cs | 2 + 4 files changed, 50 insertions(+), 15 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs index bc992a08..2d299f6d 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.Users.Enums; using BotSharp.Abstraction.Users.Models; using System.IO; @@ -24,6 +25,7 @@ public partial class FileRepository { var userId = Guid.NewGuid().ToString(); user.Id = userId; + user.Role = UserRole.Admin; var dir = Path.Combine(_dbSettings.FileRepository, "users", userId); if (!Directory.Exists(dir)) { diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs index bb67de2c..b10b60e8 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs @@ -1,4 +1,6 @@ using BotSharp.Abstraction.Agents.Models; +using BotSharp.Abstraction.Repositories; +using BotSharp.Abstraction.Users.Enums; namespace BotSharp.OpenAPI.Controllers; @@ -7,11 +9,13 @@ namespace BotSharp.OpenAPI.Controllers; public class AgentController : ControllerBase { private readonly IAgentService _agentService; + private readonly IUserIdentity _user; private readonly IServiceProvider _services; - public AgentController(IAgentService agentService, IServiceProvider services) + public AgentController(IAgentService agentService, IUserIdentity user, IServiceProvider services) { _agentService = agentService; + _user = user; _services = services; } @@ -45,6 +49,18 @@ public class AgentController : ControllerBase rule.RedirectToAgentName = found.Name; } + + var editable = false; + var userService = _services.GetRequiredService(); + var user = await userService.GetUser(_user.Id); + if (user != null && user.Role != UserRole.Admin) + { + var db = _services.GetRequiredService(); + var userAgents = db.GetAgentsByUser(user.Id); + editable = userAgents?.Select(x => x.Id)?.Contains(targetAgent.Id) ?? false; + } + + targetAgent.Editable = editable || user?.Role == UserRole.Admin; return targetAgent; } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index 34095acf..cbcc9028 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -1,6 +1,7 @@ using BotSharp.Abstraction.Routing; using Newtonsoft.Json.Serialization; using Newtonsoft.Json; +using BotSharp.Abstraction.Users.Enums; namespace BotSharp.OpenAPI.Controllers; @@ -38,10 +39,16 @@ public class ConversationController : ControllerBase [HttpPost("/conversations")] public async Task> GetConversations([FromBody] ConversationFilter filter) { - var service = _services.GetRequiredService(); - var conversations = await service.GetConversations(filter); - + var convService = _services.GetRequiredService(); var userService = _services.GetRequiredService(); + var user = await userService.GetUser(_user.Id); + if (user == null) + { + return new PagedItems(); + } + + filter.UserId = user.Role != UserRole.Admin ? user.Id : null; + var conversations = await convService.GetConversations(filter); var agentService = _services.GetRequiredService(); var list = conversations.Items .Select(x => ConversationViewModel.FromSession(x)) @@ -49,9 +56,8 @@ public class ConversationController : ControllerBase foreach (var item in list) { - var user = await userService.GetUser(item.User.Id); + user = await userService.GetUser(item.User.Id); item.User = UserViewModel.FromUser(user); - var agent = await agentService.GetAgent(item.AgentId); item.AgentName = agent?.Name; } @@ -116,21 +122,30 @@ public class ConversationController : ControllerBase } [HttpGet("/conversation/{conversationId}")] - public async Task GetConversation([FromRoute] string conversationId) + public async Task GetConversation([FromRoute] string conversationId) { var service = _services.GetRequiredService(); - var conversations = await service.GetConversations(new ConversationFilter - { - Id = conversationId - }); - var userService = _services.GetRequiredService(); - var result = ConversationViewModel.FromSession(conversations.Items.First()); + var user = await userService.GetUser(_user.Id); + if (user == null) + { + return null; + } + var filter = new ConversationFilter + { + Id = conversationId, + UserId = user.Role != UserRole.Admin ? user.Id : null + }; + var conversations = await service.GetConversations(filter); + if (conversations.Items.IsNullOrEmpty()) + { + return null; + } + + var result = ConversationViewModel.FromSession(conversations.Items.First()); var state = _services.GetRequiredService(); result.States = state.Load(conversationId, isReadOnly: true); - - var user = await userService.GetUser(result.User.Id); result.User = UserViewModel.FromUser(user); return result; diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs index 536f5b5d..9d87a3e2 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs @@ -42,6 +42,8 @@ public class AgentViewModel public PluginDef Plugin { get; set; } + public bool Editable { get; set; } + [JsonPropertyName("created_datetime")] public DateTime CreatedDateTime { get; set; } From 394d6071638fa1311711d0b35b97efeb455305a5 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 15 May 2024 17:30:45 -0500 Subject: [PATCH 138/201] add user role limit --- .../Agents/IAgentService.cs | 2 ++ .../Services/AgentService.UpdateAgent.cs | 5 +++ .../Agents/Services/AgentService.cs | 6 ++++ .../FileRepository/FileRepository.User.cs | 1 - .../Users/Services/UserService.cs | 1 - .../Controllers/AgentController.cs | 9 +++-- .../Controllers/ConversationController.cs | 4 +-- .../Controllers/PluginController.cs | 33 +++++++++++++------ 8 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs index db4db62a..b435a4d1 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs @@ -48,5 +48,7 @@ public interface IAgentService string GetDataDir(); string GetAgentDataDir(string agentId); + List GetAgentsByUser(string userId); + PluginDef GetPlugin(string agentId); } diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs index 154d7398..af6cb65e 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs @@ -1,5 +1,6 @@ using BotSharp.Abstraction.Repositories.Enums; using BotSharp.Abstraction.Routing.Models; +using BotSharp.Abstraction.Users.Enums; using System.IO; namespace BotSharp.Core.Agents.Services; @@ -8,6 +9,10 @@ public partial class AgentService { public async Task UpdateAgent(Agent agent, AgentField updateField) { + var userService = _services.GetRequiredService(); + var user = await userService.GetUser(_user.Id); + if (user?.Role != UserRole.Admin) return; + if (agent == null || string.IsNullOrEmpty(agent.Id)) return; var record = _db.GetAgent(agent.Id); diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs index bd009db0..da1b8cf1 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs @@ -47,4 +47,10 @@ public partial class AgentService : IAgentService } return dir; } + + public List GetAgentsByUser(string userId) + { + var agents = _db.GetAgentsByUser(userId); + return agents; + } } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs index 2d299f6d..8f1103e6 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs @@ -25,7 +25,6 @@ public partial class FileRepository { var userId = Guid.NewGuid().ToString(); user.Id = userId; - user.Role = UserRole.Admin; var dir = Path.Combine(_dbSettings.FileRepository, "users", userId); if (!Directory.Exists(dir)) { diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index 6e856b9f..322b300d 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Repositories; using BotSharp.Abstraction.Users.Models; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs index b10b60e8..d867e782 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs @@ -50,17 +50,16 @@ public class AgentController : ControllerBase rule.RedirectToAgentName = found.Name; } - var editable = false; + var editable = true; var userService = _services.GetRequiredService(); var user = await userService.GetUser(_user.Id); - if (user != null && user.Role != UserRole.Admin) + if (user?.Role != UserRole.Admin) { - var db = _services.GetRequiredService(); - var userAgents = db.GetAgentsByUser(user.Id); + var userAgents = _agentService.GetAgentsByUser(user?.Id); editable = userAgents?.Select(x => x.Id)?.Contains(targetAgent.Id) ?? false; } - targetAgent.Editable = editable || user?.Role == UserRole.Admin; + targetAgent.Editable = editable; return targetAgent; } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index cbcc9028..f62680d2 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -50,9 +50,7 @@ public class ConversationController : ControllerBase filter.UserId = user.Role != UserRole.Admin ? user.Id : null; var conversations = await convService.GetConversations(filter); var agentService = _services.GetRequiredService(); - var list = conversations.Items - .Select(x => ConversationViewModel.FromSession(x)) - .ToList(); + var list = conversations.Items.Select(x => ConversationViewModel.FromSession(x)).ToList(); foreach (var item in list) { diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs index 2c231e16..dc2c2740 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Plugins.Models; +using BotSharp.Abstraction.Users.Enums; using BotSharp.Core.Plugins; namespace BotSharp.OpenAPI.Controllers; @@ -8,38 +9,50 @@ namespace BotSharp.OpenAPI.Controllers; public class PluginController : ControllerBase { private readonly IServiceProvider _services; + private readonly IUserIdentity _user; private readonly PluginSettings _settings; - public PluginController(IServiceProvider services, PluginSettings settings) + public PluginController(IServiceProvider services, IUserIdentity user, PluginSettings settings) { _services = services; + _user = user; _settings = settings; } [HttpGet("/plugins")] - public PagedItems GetPlugins([FromQuery] PluginFilter filter) + public async Task> GetPlugins([FromQuery] PluginFilter filter) { + var userService = _services.GetRequiredService(); + var user = await userService.GetUser(_user.Id); + if (user?.Role != UserRole.Admin) + { + return new PagedItems(); + } + var loader = _services.GetRequiredService(); return loader.GetPagedPlugins(_services, filter); } [HttpGet("/plugin/menu")] - public List GetPluginMenu() + public async Task> GetPluginMenu() { var menu = new List { new PluginMenuDef("Apps", weight: 5) { IsHeader = true, - }, - new PluginMenuDef("System", weight: 30) - { - IsHeader = true - }, - new PluginMenuDef("Plugins", link: "page/plugin", icon: "bx bx-plug", weight: 31), - new PluginMenuDef("Settings", link: "page/setting", icon: "bx bx-cog", weight: 32), + } }; + var userService = _services.GetRequiredService(); + var user = await userService.GetUser(_user.Id); + if (user?.Role == UserRole.Admin) + { + menu.Add(new PluginMenuDef("System", weight: 30) { IsHeader = true }); + menu.Add(new PluginMenuDef("Plugins", link: "page/plugin", icon: "bx bx-plug", weight: 31)); + menu.Add(new PluginMenuDef("Settings", link: "page/setting", icon: "bx bx-cog", weight: 32)); + } + var loader = _services.GetRequiredService(); foreach (var plugin in loader.GetPlugins(_services)) { From ca5407f97c777947dd2f27332934ec4a78f9c644 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 15 May 2024 17:53:39 -0500 Subject: [PATCH 139/201] add user role in menu --- .../Plugins/Models/PluginMenuDef.cs | 3 +++ .../BotSharp.Core/Agents/AgentPlugin.cs | 2 +- .../BotSharp.Core/Plugins/PluginLoader.cs | 16 ++++++++++++ .../BotSharp.Core/Tasks/TaskPlugin.cs | 6 ++++- .../Controllers/PluginController.cs | 26 ++++++++++++------- 5 files changed, 42 insertions(+), 11 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Plugins/Models/PluginMenuDef.cs b/src/Infrastructure/BotSharp.Abstraction/Plugins/Models/PluginMenuDef.cs index bd5a1d88..5f8a59b9 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Plugins/Models/PluginMenuDef.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Plugins/Models/PluginMenuDef.cs @@ -19,6 +19,9 @@ public class PluginMenuDef [JsonIgnore] public int Weight { get; set; } + [JsonIgnore] + public List? Roles { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public List? SubMenu { get; set; } diff --git a/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs b/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs index eb21fb31..4aef8ca4 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs @@ -43,7 +43,7 @@ public class AgentPlugin : IBotSharpPlugin { SubMenu = new List { - new PluginMenuDef("Routing", link: "page/agent/router"), // icon: "bx bx-map-pin" + new PluginMenuDef("Routing", link: "page/agent/router") { Roles = new List { "admin" } }, // icon: "bx bx-map-pin" new PluginMenuDef("Evaluating", link: "page/agent/evaluator"), // icon: "bx bx-task" new PluginMenuDef("Agents", link: "page/agent"), // icon: "bx bx-bot" } diff --git a/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs b/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs index f6de19ac..05867cb0 100644 --- a/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs +++ b/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs @@ -269,4 +269,20 @@ public class PluginLoader } }); } + + public List FilterPluginsByRoles(List plugins, string userRole) + { + if (plugins.IsNullOrEmpty()) return plugins; + + var filtered = new List(); + foreach (var plugin in plugins) + { + if (plugin.Roles.IsNullOrEmpty() || plugin.Roles.Contains(userRole)) + { + plugin.SubMenu = FilterPluginsByRoles(plugin.SubMenu, userRole); + filtered.Add(plugin); + } + } + return filtered; + } } diff --git a/src/Infrastructure/BotSharp.Core/Tasks/TaskPlugin.cs b/src/Infrastructure/BotSharp.Core/Tasks/TaskPlugin.cs index ddf709ec..27c55ab5 100644 --- a/src/Infrastructure/BotSharp.Core/Tasks/TaskPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Tasks/TaskPlugin.cs @@ -1,5 +1,6 @@ using BotSharp.Abstraction.Plugins.Models; using BotSharp.Abstraction.Tasks; +using BotSharp.Abstraction.Users.Enums; using BotSharp.Core.Tasks.Services; using Microsoft.Extensions.Configuration; @@ -19,7 +20,10 @@ public class TaskPlugin : IBotSharpPlugin public bool AttachMenu(List menu) { var section = menu.First(x => x.Label == "Apps"); - menu.Add(new PluginMenuDef("Task", link: "page/task", icon: "bx bx-task", weight: section.Weight + 8)); + menu.Add(new PluginMenuDef("Task", link: "page/task", icon: "bx bx-task", weight: section.Weight + 8) + { + Roles = new List { UserRole.Admin } + }); return true; } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs index dc2c2740..da3de2c8 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs @@ -41,18 +41,22 @@ public class PluginController : ControllerBase new PluginMenuDef("Apps", weight: 5) { IsHeader = true, + }, + new PluginMenuDef("System", weight: 30) + { + IsHeader = true, + Roles = new List { UserRole.Admin } + }, + new PluginMenuDef("Plugins", link: "page/plugin", icon: "bx bx-plug", weight: 31) + { + Roles = new List { UserRole.Admin } + }, + new PluginMenuDef("Settings", link: "page/setting", icon: "bx bx-cog", weight: 32) + { + Roles = new List { UserRole.Admin } } }; - var userService = _services.GetRequiredService(); - var user = await userService.GetUser(_user.Id); - if (user?.Role == UserRole.Admin) - { - menu.Add(new PluginMenuDef("System", weight: 30) { IsHeader = true }); - menu.Add(new PluginMenuDef("Plugins", link: "page/plugin", icon: "bx bx-plug", weight: 31)); - menu.Add(new PluginMenuDef("Settings", link: "page/setting", icon: "bx bx-cog", weight: 32)); - } - var loader = _services.GetRequiredService(); foreach (var plugin in loader.GetPlugins(_services)) { @@ -62,6 +66,10 @@ public class PluginController : ControllerBase } plugin.Module.AttachMenu(menu); } + + var userService = _services.GetRequiredService(); + var user = await userService.GetUser(_user.Id); + menu = loader.FilterPluginsByRoles(menu, user?.Role); menu = menu.OrderBy(x => x.Weight).ToList(); return menu; } From 89c33bafc0e8628e4c02ba652dc6b393bda8af58 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 15 May 2024 18:07:11 -0500 Subject: [PATCH 140/201] check convsation user when delete --- .../Controllers/ConversationController.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index f62680d2..0946084c 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -2,6 +2,7 @@ using BotSharp.Abstraction.Routing; using Newtonsoft.Json.Serialization; using Newtonsoft.Json; using BotSharp.Abstraction.Users.Enums; +using BotSharp.Abstraction.Users.Models; namespace BotSharp.OpenAPI.Controllers; @@ -181,7 +182,22 @@ public class ConversationController : ControllerBase [HttpDelete("/conversation/{conversationId}")] public async Task DeleteConversation([FromRoute] string conversationId) { + var userService = _services.GetRequiredService(); var conversationService = _services.GetRequiredService(); + + var user = await userService.GetUser(_user.Id); + var filter = new ConversationFilter + { + Id = conversationId, + UserId = user.Role != UserRole.Admin ? user.Id : null + }; + var conversations = await conversationService.GetConversations(filter); + + if (conversations.Items.IsNullOrEmpty()) + { + return false; + } + var response = await conversationService.DeleteConversations(new List { conversationId }); return response; } From d4493e0db64ef687e77e1235721fbbe91210c700 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Wed, 15 May 2024 21:45:31 -0500 Subject: [PATCH 141/201] prevent send event if it is not conversation --- .../Hooks/StreamingLogHook.cs | 50 +++++++++++++++---- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs index ab662546..0d578f99 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Agents.Models; +using BotSharp.Abstraction.Conversations.Models; using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Loggers; using BotSharp.Abstraction.Loggers.Enums; @@ -49,6 +50,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR public override async Task OnMessageReceived(RoleDialogModel message) { var conversationId = _state.GetConversationId(); + if (string.IsNullOrEmpty(conversationId)) return; + var log = $"{GetMessageContent(message)}"; var input = new ContentLogInputModel(conversationId, message) @@ -63,6 +66,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR public override async Task OnPostbackMessageReceived(RoleDialogModel message, PostbackMessageModel replyMsg) { var conversationId = _state.GetConversationId(); + if (string.IsNullOrEmpty(conversationId)) return; + var log = $"{GetMessageContent(message)}"; var replyContent = JsonSerializer.Serialize(replyMsg, _options.JsonSerializerOptions); log += $"\r\n```json\r\n{replyContent}\r\n```"; @@ -81,6 +86,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR if (!_convSettings.ShowVerboseLog) return; var conversationId = _state.GetConversationId(); + if (string.IsNullOrEmpty(conversationId)) return; var log = $"{agent.Name} is using template {name}"; var message = new RoleDialogModel(AgentRole.System, log) @@ -104,12 +110,11 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR public override async Task OnFunctionExecuting(RoleDialogModel message) { - if (message.FunctionName == "route_to_agent") - { - return; - } - var conversationId = _state.GetConversationId(); + if (string.IsNullOrEmpty(conversationId)) return; + + if (message.FunctionName == "route_to_agent") return; + var agent = await _agentService.LoadAgent(message.CurrentAgentId); message.FunctionArgs = message.FunctionArgs ?? "{}"; var args = JsonSerializer.Serialize(JsonDocument.Parse(message.FunctionArgs), _options.JsonSerializerOptions); @@ -127,12 +132,11 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR public override async Task OnFunctionExecuted(RoleDialogModel message) { - if (message.FunctionName == "route_to_agent") - { - return; - } - var conversationId = _state.GetConversationId(); + if (string.IsNullOrEmpty(conversationId)) return; + + if (message.FunctionName == "route_to_agent") return; + var agent = await _agentService.LoadAgent(message.CurrentAgentId); message.FunctionArgs = message.FunctionArgs ?? "{}"; // var args = JsonSerializer.Serialize(JsonDocument.Parse(message.FunctionArgs), _options.JsonSerializerOptions); @@ -159,6 +163,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR if (!_convSettings.ShowVerboseLog) return; var conversationId = _state.GetConversationId(); + if (string.IsNullOrEmpty(conversationId)) return; + var agent = await _agentService.LoadAgent(message.CurrentAgentId); var log = tokenStats.Prompt; @@ -180,8 +186,10 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR /// public override async Task OnResponseGenerated(RoleDialogModel message) { - var conv = _services.GetRequiredService(); + var conversationId = _state.GetConversationId(); + if (string.IsNullOrEmpty(conversationId)) return; + var conv = _services.GetRequiredService(); await _chatHub.Clients.User(_user.Id).SendAsync("OnConversateStateLogGenerated", BuildStateLog(conv.ConversationId, _state.GetStates(), message)); if (message.Role == AgentRole.Assistant) @@ -208,6 +216,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR public override async Task OnTaskCompleted(RoleDialogModel message) { var conversationId = _state.GetConversationId(); + if (string.IsNullOrEmpty(conversationId)) return; + var log = $"{GetMessageContent(message)}"; var agent = await _agentService.LoadAgent(message.CurrentAgentId); @@ -223,6 +233,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR public override async Task OnConversationEnding(RoleDialogModel message) { var conversationId = _state.GetConversationId(); + if (string.IsNullOrEmpty(conversationId)) return; + var log = $"Conversation ended"; var agent = await _agentService.LoadAgent(message.CurrentAgentId); @@ -237,6 +249,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR public override async Task OnBreakpointUpdated(string conversationId, bool resetStates) { + if (string.IsNullOrEmpty(conversationId)) return; + var log = $"Conversation breakpoint is updated"; if (resetStates) { @@ -263,6 +277,9 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR public override async Task OnStateChanged(StateChangeModel stateChange) { + var conversationId = _state.GetConversationId(); + if (string.IsNullOrEmpty(conversationId)) return; + if (stateChange == null) return; await _chatHub.Clients.User(_user.Id).SendAsync("OnStateChangeGenerated", BuildStateChangeLog(stateChange)); @@ -273,6 +290,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR public async Task OnAgentEnqueued(string agentId, string preAgentId, string? reason = null) { var conversationId = _state.GetConversationId(); + if (string.IsNullOrEmpty(conversationId)) return; + var agent = await _agentService.LoadAgent(agentId); // Agent queue log @@ -298,6 +317,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR public async Task OnAgentDequeued(string agentId, string currentAgentId, string? reason = null) { var conversationId = _state.GetConversationId(); + if (string.IsNullOrEmpty(conversationId)) return; + var agent = await _agentService.LoadAgent(agentId); var currentAgent = await _agentService.LoadAgent(currentAgentId); @@ -324,6 +345,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR public async Task OnAgentReplaced(string fromAgentId, string toAgentId, string? reason = null) { var conversationId = _state.GetConversationId(); + if (string.IsNullOrEmpty(conversationId)) return; + var fromAgent = await _agentService.LoadAgent(fromAgentId); var toAgent = await _agentService.LoadAgent(toAgentId); @@ -350,6 +373,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR public async Task OnAgentQueueEmptied(string agentId, string? reason = null) { var conversationId = _state.GetConversationId(); + if (string.IsNullOrEmpty(conversationId)) return; // Agent queue log var log = $"Agent queue is empty"; @@ -374,6 +398,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR public async Task OnRoutingInstructionReceived(FunctionCallFromLlm instruct, RoleDialogModel message) { var conversationId = _state.GetConversationId(); + if (string.IsNullOrEmpty(conversationId)) return; + var agent = await _agentService.LoadAgent(message.CurrentAgentId); var log = JsonSerializer.Serialize(instruct, _options.JsonSerializerOptions); log = $"```json\r\n{log}\r\n```"; @@ -391,6 +417,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR public async Task OnRoutingInstructionRevised(FunctionCallFromLlm instruct, RoleDialogModel message) { var conversationId = _state.GetConversationId(); + if (string.IsNullOrEmpty(conversationId)) return; + var agent = await _agentService.LoadAgent(message.CurrentAgentId); var log = $"Revised user goal agent to {instruct.OriginalAgent}"; From 04e90a3664d8064b339fac254373c47fa156c0b3 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 16 May 2024 10:41:59 -0500 Subject: [PATCH 142/201] rename function --- .../BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs | 4 ---- src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs | 4 ++-- .../BotSharp.OpenAPI/Controllers/PluginController.cs | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs index ce6512af..d3754a94 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs @@ -1,8 +1,4 @@ -using BotSharp.Abstraction.Agents.Models; -using BotSharp.Abstraction.Functions.Models; -using BotSharp.Abstraction.Repositories; using BotSharp.Abstraction.Tasks.Models; -using BotSharp.Abstraction.Users.Models; using System.IO; using System.Text.RegularExpressions; diff --git a/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs b/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs index 05867cb0..1b883657 100644 --- a/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs +++ b/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs @@ -270,7 +270,7 @@ public class PluginLoader }); } - public List FilterPluginsByRoles(List plugins, string userRole) + public List GetPluginMenuByRoles(List plugins, string userRole) { if (plugins.IsNullOrEmpty()) return plugins; @@ -279,7 +279,7 @@ public class PluginLoader { if (plugin.Roles.IsNullOrEmpty() || plugin.Roles.Contains(userRole)) { - plugin.SubMenu = FilterPluginsByRoles(plugin.SubMenu, userRole); + plugin.SubMenu = GetPluginMenuByRoles(plugin.SubMenu, userRole); filtered.Add(plugin); } } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs index da3de2c8..342f39fb 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs @@ -69,7 +69,7 @@ public class PluginController : ControllerBase var userService = _services.GetRequiredService(); var user = await userService.GetUser(_user.Id); - menu = loader.FilterPluginsByRoles(menu, user?.Role); + menu = loader.GetPluginMenuByRoles(menu, user?.Role); menu = menu.OrderBy(x => x.Weight).ToList(); return menu; } From 6625b3ec088fa204bf8b414206feea59422954b8 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 16 May 2024 12:58:37 -0500 Subject: [PATCH 143/201] minor change --- src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs b/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs index 4aef8ca4..83ce823c 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs @@ -1,6 +1,7 @@ using BotSharp.Abstraction.MLTasks; using BotSharp.Abstraction.Plugins.Models; using BotSharp.Abstraction.Settings; +using BotSharp.Abstraction.Users.Enums; using Microsoft.Extensions.Configuration; namespace BotSharp.Core.Agents; @@ -43,8 +44,8 @@ public class AgentPlugin : IBotSharpPlugin { SubMenu = new List { - new PluginMenuDef("Routing", link: "page/agent/router") { Roles = new List { "admin" } }, // icon: "bx bx-map-pin" - new PluginMenuDef("Evaluating", link: "page/agent/evaluator"), // icon: "bx bx-task" + new PluginMenuDef("Routing", link: "page/agent/router") { Roles = new List { UserRole.Admin } }, // icon: "bx bx-map-pin" + new PluginMenuDef("Evaluating", link: "page/agent/evaluator") { Roles = new List { UserRole.Admin } }, // icon: "bx bx-task" new PluginMenuDef("Agents", link: "page/agent"), // icon: "bx bx-bot" } }); From 57122f7833cd73ceee1ce24289fe41a5d6497c63 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Thu, 16 May 2024 16:32:58 -0500 Subject: [PATCH 144/201] Fix llm selection bug. --- .../MLTasks/ILlmProviderService.cs | 2 +- .../Infrastructures/CompletionProvider.cs | 6 +++--- .../Infrastructures/LlmProviderService.cs | 10 +++++++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/MLTasks/ILlmProviderService.cs b/src/Infrastructure/BotSharp.Abstraction/MLTasks/ILlmProviderService.cs index 120304d2..20762fe0 100644 --- a/src/Infrastructure/BotSharp.Abstraction/MLTasks/ILlmProviderService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/MLTasks/ILlmProviderService.cs @@ -6,6 +6,6 @@ public interface ILlmProviderService { LlmModelSetting GetSetting(string provider, string model); List GetProviders(); - LlmModelSetting GetProviderModel(string provider, string id, bool multiModal = false); + LlmModelSetting GetProviderModel(string provider, string id, bool? multiModal = null); List GetProviderModels(string provider); } diff --git a/src/Infrastructure/BotSharp.Core/Infrastructures/CompletionProvider.cs b/src/Infrastructure/BotSharp.Core/Infrastructures/CompletionProvider.cs index ace1e664..4655b1de 100644 --- a/src/Infrastructure/BotSharp.Core/Infrastructures/CompletionProvider.cs +++ b/src/Infrastructure/BotSharp.Core/Infrastructures/CompletionProvider.cs @@ -36,7 +36,7 @@ public class CompletionProvider string? provider = null, string? model = null, string? modelId = null, - bool multiModal = false, + bool? multiModal = null, AgentLlmConfig? agentConfig = null) { var completions = services.GetServices(); @@ -59,7 +59,7 @@ public class CompletionProvider string? provider = null, string? model = null, string? modelId = null, - bool multiModal = false, + bool? multiModal = null, AgentLlmConfig? agentConfig = null) { var agentSetting = services.GetRequiredService(); @@ -82,7 +82,7 @@ public class CompletionProvider { var modelIdentity = state.ContainsState("model_id") ? state.GetState("model_id") : modelId; var llmProviderService = services.GetRequiredService(); - model = llmProviderService.GetProviderModel(provider, modelIdentity, multiModal)?.Name; + model = llmProviderService.GetProviderModel(provider, modelIdentity, multiModal: multiModal)?.Name; } } diff --git a/src/Infrastructure/BotSharp.Core/Infrastructures/LlmProviderService.cs b/src/Infrastructure/BotSharp.Core/Infrastructures/LlmProviderService.cs index 7d92a687..8320bdb7 100644 --- a/src/Infrastructure/BotSharp.Core/Infrastructures/LlmProviderService.cs +++ b/src/Infrastructure/BotSharp.Core/Infrastructures/LlmProviderService.cs @@ -44,11 +44,15 @@ public class LlmProviderService : ILlmProviderService ?.Models ?? new List(); } - public LlmModelSetting GetProviderModel(string provider, string id, bool multiModal = false) + public LlmModelSetting GetProviderModel(string provider, string id, bool? multiModal = null) { var models = GetProviderModels(provider) - .Where(x => x.Id == id && x.MultiModal == multiModal) - .ToList(); + .Where(x => x.Id == id); + + if (multiModal.HasValue) + { + models = models.Where(x => x.MultiModal == multiModal); + } var random = new Random(); var index = random.Next(0, models.Count()); From 98592438684900ce8cad133dbcf47b016ddb15c4 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Fri, 17 May 2024 11:35:20 -0500 Subject: [PATCH 145/201] add default model --- .../BotSharp.OpenAPI/Controllers/InstructModeController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs index a884fd7e..45120a26 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs @@ -83,7 +83,8 @@ public class InstructModeController : ControllerBase try { - var completion = CompletionProvider.GetChatCompletion(_services, provider: "openai", modelId: "gpt-4", multiModal: true); + var completion = CompletionProvider.GetChatCompletion(_services, provider: input.Provider ?? "openai", + modelId: input.ModelId ?? "gpt-4", multiModal: true); var message = await completion.GetChatCompletions(new Agent() { Id = Guid.Empty.ToString(), From 8eee1fd1643273e2c4c673367e55e4efc066e705 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Fri, 17 May 2024 18:45:18 -0500 Subject: [PATCH 146/201] Change SendHttpRequest args --- .../Browsing/IWebBrowser.cs | 2 +- .../Browsing/Models/ElementActionArgs.cs | 2 ++ .../Browsing/Models/HttpRequestParams.cs | 2 +- .../PlaywrightDriver/PlaywrightInstance.cs | 5 ++-- .../PlaywrightWebDriver.DoAction.cs | 5 ++++ .../PlaywrightWebDriver.HttpRequest.cs | 4 ++-- .../PlaywrightWebDriver.LocateElement.cs | 23 +++++++++++++++++-- .../SeleniumWebDriver.HttpRequest.cs | 4 ++-- .../Functions/HttpRequestFn.cs | 7 +++++- 9 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs index 80dd6925..fc907351 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs @@ -24,6 +24,6 @@ public interface IWebBrowser Task EvaluateScript(string contextId, string script); Task CloseBrowser(string contextId); Task CloseCurrentPage(string contextId); - Task SendHttpRequest(string contextId, HttpRequestParams actionParams); + Task SendHttpRequest(MessageInfo message, HttpRequestParams actionParams); Task GetAttributeValue(MessageInfo message, ElementLocatingArgs location); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs index 8b644b31..0d44066a 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs @@ -10,6 +10,8 @@ public class ElementActionArgs public ElementPosition? Position { get; set; } + public string? PressKey { get; set; } + /// /// Required for deserialization /// diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/HttpRequestParams.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/HttpRequestParams.cs index 2b8cc052..e6d24a8e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/HttpRequestParams.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/HttpRequestParams.cs @@ -18,7 +18,7 @@ public class HttpRequestParams public HttpRequestParams(string url, HttpMethod method, string? payload = null) { - Method = HttpMethod.Get; + Method = method; Url = url; Payload = payload; } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs index a801400b..e7fd5908 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs @@ -40,11 +40,12 @@ public class PlaywrightInstance : IDisposable Channel = "chrome", IgnoreDefaultArgs = new[] { - "--disable-infobars" + "--enable-automation", }, Args = new[] { "--disable-infobars", + "--no-sandbox", // "--start-maximized" } }); @@ -104,6 +105,6 @@ public class PlaywrightInstance : IDisposable public void Dispose() { _contexts.Clear(); - _playwright.Dispose(); + _playwright?.Dispose(); } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs index dd4600c9..91a99aa8 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs @@ -28,6 +28,11 @@ public partial class PlaywrightWebDriver else if (action.Action == BroswerActionEnum.InputText) { await locator.FillAsync(action.Content); + + if (action.PressKey != null) + { + await locator.PressAsync(action.PressKey); + } } } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs index a5a59334..4a0177a0 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs @@ -4,7 +4,7 @@ namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver { - public async Task SendHttpRequest(string contextId, HttpRequestParams args) + public async Task SendHttpRequest(MessageInfo message, HttpRequestParams args) { var result = new BrowserActionResult(); @@ -27,7 +27,7 @@ public partial class PlaywrightWebDriver try { - var response = await EvaluateScript(contextId, script); + var response = await EvaluateScript(message.ContextId, script); result.IsSuccess = true; result.Body = JsonSerializer.Serialize(response); } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs index 78fa00a3..13b0464c 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs @@ -1,3 +1,5 @@ +using System.Xml.Linq; + namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver @@ -18,12 +20,12 @@ public partial class PlaywrightWebDriver // check if selector is specified if (location.Selector != null) { - locator = page.Locator(location.Selector); + locator = locator.Locator(location.Selector); count = await locator.CountAsync(); } // try attribute - if (count == 0 && !string.IsNullOrEmpty(location.AttributeName)) + if (!string.IsNullOrEmpty(location.AttributeName)) { locator = locator.Locator($"[{location.AttributeName}='{location.AttributeValue}']"); count = await locator.CountAsync(); @@ -65,12 +67,29 @@ public partial class PlaywrightWebDriver else if (count == 1) { result.Selector = locator.ToString().Split('@').Last(); + + // Make sure the element is visible + await locator.EvaluateAsync("element => element.style.height = ''"); + await locator.EvaluateAsync("element => element.style.width = ''"); + await locator.EvaluateAsync("element => element.style.opacity = ''"); + var text = await locator.InnerTextAsync(); result.Body = text; result.IsSuccess = true; } else if (count > 1) { + // Make sure the element is visible + foreach (var element in await locator.AllAsync()) + { + if (!await element.IsVisibleAsync()) + { + await element.EvaluateAsync("element => element.style.height = '10px'"); + await element.EvaluateAsync("element => element.style.width = '10px'"); + await element.EvaluateAsync("element => element.style.opacity = '1.0'"); + } + } + if (location.FailIfMultiple) { result.Message = $"Multiple elements are found by {locator}"; diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.HttpRequest.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.HttpRequest.cs index b88a5d96..c9940318 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.HttpRequest.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.HttpRequest.cs @@ -4,7 +4,7 @@ namespace BotSharp.Plugin.WebDriver.Drivers.SeleniumDriver; public partial class SeleniumWebDriver { - public async Task SendHttpRequest(string contextId, HttpRequestParams args) + public async Task SendHttpRequest(MessageInfo message, HttpRequestParams args) { var result = new BrowserActionResult(); @@ -27,7 +27,7 @@ public partial class SeleniumWebDriver try { - var response = await EvaluateScript(contextId, script); + var response = await EvaluateScript(message.ContextId, script); result.IsSuccess = true; result.Body = JsonSerializer.Serialize(response); } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/HttpRequestFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/HttpRequestFn.cs index c16da29c..fff2c240 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/HttpRequestFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/HttpRequestFn.cs @@ -21,7 +21,12 @@ public class HttpRequestFn : IFunctionCallback var agentService = _services.GetRequiredService(); var agent = await agentService.LoadAgent(message.CurrentAgentId); - var result = await _browser.SendHttpRequest(convService.ConversationId, args); + var result = await _browser.SendHttpRequest(new MessageInfo + { + AgentId = agent.Id, + MessageId = message.MessageId, + ContextId = convService.ConversationId + }, args); message.Content = result.IsSuccess ? result.Body : From b35f0e653656640fda3b375dba9aa2d271194bdc Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 20 May 2024 11:35:45 -0500 Subject: [PATCH 147/201] refine log in --- .../BotSharp.Abstraction/Users/IUserService.cs | 2 +- .../BotSharp.Core/Users/Services/UserService.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs index 35d74aa4..debf68f4 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs @@ -6,6 +6,6 @@ public interface IUserService { Task GetUser(string id); Task CreateUser(User user); - Task GetToken(string authorization); + Task GetToken(string authorization); Task GetMyProfile(); } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index 6e856b9f..9508dd2b 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Repositories; using BotSharp.Abstraction.Users.Models; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; @@ -60,7 +59,7 @@ public class UserService : IUserService return record; } - public async Task GetToken(string authorization) + public async Task GetToken(string authorization) { var base64 = Encoding.UTF8.GetString(Convert.FromBase64String(authorization)); var (id, password) = base64.SplitAsTuple(":"); @@ -72,13 +71,14 @@ public class UserService : IUserService record = db.GetUserByUserName(id); } + User? user = null; var hooks = _services.GetServices(); if (record == null || record.Source != "internal") { // check 3rd party user foreach (var hook in hooks) { - var user = await hook.Authenticate(id, password); + user = await hook.Authenticate(id, password); if (user == null) { continue; @@ -109,7 +109,7 @@ public class UserService : IUserService } } - if (record == null) + if ((!hooks.IsNullOrEmpty() && user == null) || record == null) { return default; } From efdf5a30da9a9f368a4168fb1ba624223adac952 Mon Sep 17 00:00:00 2001 From: Haiping Chen <101423@smsassist.com> Date: Mon, 20 May 2024 12:54:38 -0500 Subject: [PATCH 148/201] Improve translation. --- .../Translation/Models/TranslationInput.cs | 10 ++++++++++ .../Templating/ResponseTemplateService.cs | 2 -- .../BotSharp.Core/Templating/TemplateRender.cs | 2 ++ .../Translation/TranslationService.cs | 17 +++++++++++++---- .../templates/translation_prompt.liquid | 8 ++------ 5 files changed, 27 insertions(+), 12 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Translation/Models/TranslationInput.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Translation/Models/TranslationInput.cs b/src/Infrastructure/BotSharp.Abstraction/Translation/Models/TranslationInput.cs new file mode 100644 index 00000000..6897ca42 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Translation/Models/TranslationInput.cs @@ -0,0 +1,10 @@ +namespace BotSharp.Abstraction.Translation.Models; + +public class TranslationInput +{ + [JsonPropertyName("id")] + public int Id { get; set; } = -1; + + [JsonPropertyName("text")] + public string Text { get; set; } = null!; +} diff --git a/src/Infrastructure/BotSharp.Core/Templating/ResponseTemplateService.cs b/src/Infrastructure/BotSharp.Core/Templating/ResponseTemplateService.cs index 5119bc91..463013bf 100644 --- a/src/Infrastructure/BotSharp.Core/Templating/ResponseTemplateService.cs +++ b/src/Infrastructure/BotSharp.Core/Templating/ResponseTemplateService.cs @@ -1,5 +1,3 @@ -using BotSharp.Abstraction.Repositories; -using BotSharp.Abstraction.Routing.Models; using BotSharp.Abstraction.Templating; using System.Reflection; diff --git a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs index 3bdbf7e1..33b27177 100644 --- a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs +++ b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs @@ -3,6 +3,7 @@ using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Models; using BotSharp.Abstraction.Routing.Models; using BotSharp.Abstraction.Templating; +using BotSharp.Abstraction.Translation.Models; using Fluid; namespace BotSharp.Core.Templating; @@ -30,6 +31,7 @@ public class TemplateRender : ITemplateRender _options.MemberAccessStrategy.Register(); _options.MemberAccessStrategy.Register(); _options.MemberAccessStrategy.Register(); + _options.MemberAccessStrategy.Register(); } public string Render(string template, Dictionary dict) diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index d24fd7d4..ff28b4e6 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -57,8 +57,11 @@ public class TranslationService : ITranslationService var keys = unique.ToArray(); var texts = unique.ToArray() - .Select((text, i) => $"{i + 1}. \"{text}\"") - .ToList(); + .Select((text, i) => new TranslationInput + { + Id = i + 1, + Text = text + }).ToList(); var translatedStringList = await InnerTranslate(texts, language, template); try @@ -297,15 +300,21 @@ public class TranslationService : ITranslationService /// /// /// - private async Task InnerTranslate(List texts, string language, string template) + private async Task InnerTranslate(List texts, string language, string template) { + var jsonString = JsonSerializer.Serialize(texts, new JsonSerializerOptions + { + WriteIndented = true, + }) ; var translator = new Agent { Id = Guid.Empty.ToString(), Name = "Translator", + Instruction = "You are a translation expert.", TemplateDict = new Dictionary { - { "text_list", texts }, + { "text_list", jsonString }, + { "text_list_size", texts.Count }, { StateConst.LANGUAGE, language } } }; diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid index be4f1077..7403130c 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid @@ -1,9 +1,5 @@ -{% for text in text_list %} -{{ text }} -{% endfor %} +{{ text_list }} + ===== Translate the above sentences into {{ language }}. Output the translated text in JSON {"input_lang":"original text language", "output_lang":"{{ language }}", "texts":[""]}. -Do not include the serial number before each sentence. -Do not include double quotes outside the sentence. -The number of output sentences must be {{ text_list | size }}. From d901fff30fb3fcd8659ff0210cfd4ee33cda94fe Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 20 May 2024 16:50:45 -0500 Subject: [PATCH 149/201] add http handler --- .../Agents/Settings/AgentSettings.cs | 1 + .../Http/Settings/HttpSettings.cs | 7 + .../Routing/Hooks/RoutingAgentHook.cs | 49 +++- src/Infrastructure/BotSharp.Core/Using.cs | 1 + .../BotSharp.Plugin.HttpHandler.csproj | 4 + .../Functions/HandleHttpRequest.cs | 210 ++++++++++++++++++ .../HttpHandlerPlugin.cs | 8 +- .../LlmContexts/LlmContextIn.cs | 15 ++ .../BotSharp.Plugin.HttpHandler/Using.cs | 7 +- src/WebStarter/appsettings.json | 5 + 10 files changed, 302 insertions(+), 5 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Http/Settings/HttpSettings.cs create mode 100644 src/Plugins/BotSharp.Plugin.HttpHandler/Functions/HandleHttpRequest.cs create mode 100644 src/Plugins/BotSharp.Plugin.HttpHandler/LlmContexts/LlmContextIn.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Settings/AgentSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Settings/AgentSettings.cs index 232e7b60..fefda240 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Settings/AgentSettings.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Settings/AgentSettings.cs @@ -6,6 +6,7 @@ public class AgentSettings public string TemplateFormat { get; set; } = "liquid"; public string HostAgentId { get; set; } = string.Empty; public bool EnableTranslator { get; set; } = false; + public bool EnableHttpHandler { get; set; } = false; /// /// This is the default LLM config for agent diff --git a/src/Infrastructure/BotSharp.Abstraction/Http/Settings/HttpSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Http/Settings/HttpSettings.cs new file mode 100644 index 00000000..8fc14988 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Http/Settings/HttpSettings.cs @@ -0,0 +1,7 @@ +namespace BotSharp.Abstraction.Http.Settings; + +public class HttpSettings +{ + public string BaseAddress { get; set; } = string.Empty; + public string Origin { get; set; } = string.Empty; +} diff --git a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs index fa8c5db3..4aec88c8 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs @@ -1,8 +1,6 @@ -using BotSharp.Abstraction.Functions.Models; -using BotSharp.Abstraction.Routing; +using BotSharp.Abstraction.Functions; using BotSharp.Abstraction.Routing.Enums; using BotSharp.Abstraction.Routing.Settings; -using System.Diagnostics.Metrics; namespace BotSharp.Core.Routing.Hooks; @@ -106,6 +104,51 @@ public class RoutingAgentHook : AgentHookBase } }); } + + var settings = _services.GetRequiredService(); + if (settings.EnableHttpHandler) + { + var httpHandlerName = "handle_http_request"; + var existHttpHandler = functions.Any(x => x.Name == httpHandlerName); + var funcs = _services.GetServices(); + var httpRequestFunc = funcs.FirstOrDefault(x => x.Name == httpHandlerName); + if (!existHttpHandler && httpRequestFunc != null) + { + var json = JsonSerializer.Serialize(new + { + request_url = new + { + type = "string", + description = $"The http url that is requested. It can be an absolute url that starts with \"http\" or \"https\", or a relative url that starts with \"/\"" + }, + http_method = new + { + type = "string", + description = $"The http method that is requested, e.g., GET, POST, PUT, and DELETE." + }, + request_content = new + { + type = "string", + description = $"The http request content. It must be in json format." + } + }); + functions.Add(new FunctionDef + { + Name = httpRequestFunc.Name, + Description = "If the user requests to send an http request, you need to capture the http method and request content, and then call this function to send the http request.", + Parameters = + { + Properties = JsonSerializer.Deserialize(json), + Required = new List + { + "request_url", + "http_method" + } + } + }); + } + + } } return base.OnFunctionsLoaded(functions); diff --git a/src/Infrastructure/BotSharp.Core/Using.cs b/src/Infrastructure/BotSharp.Core/Using.cs index 0c6fe5e7..a54df964 100644 --- a/src/Infrastructure/BotSharp.Core/Using.cs +++ b/src/Infrastructure/BotSharp.Core/Using.cs @@ -27,6 +27,7 @@ global using BotSharp.Abstraction.Files; global using BotSharp.Abstraction.Files.Models; global using BotSharp.Abstraction.Translation.Attributes; global using BotSharp.Abstraction.Messaging.Enums; +global using BotSharp.Abstraction.Http.Settings; global using BotSharp.Core.Repository; global using BotSharp.Core.Routing; global using BotSharp.Core.Agents.Services; diff --git a/src/Plugins/BotSharp.Plugin.HttpHandler/BotSharp.Plugin.HttpHandler.csproj b/src/Plugins/BotSharp.Plugin.HttpHandler/BotSharp.Plugin.HttpHandler.csproj index 15ceb9d3..2607cb15 100644 --- a/src/Plugins/BotSharp.Plugin.HttpHandler/BotSharp.Plugin.HttpHandler.csproj +++ b/src/Plugins/BotSharp.Plugin.HttpHandler/BotSharp.Plugin.HttpHandler.csproj @@ -28,6 +28,10 @@ + + + + diff --git a/src/Plugins/BotSharp.Plugin.HttpHandler/Functions/HandleHttpRequest.cs b/src/Plugins/BotSharp.Plugin.HttpHandler/Functions/HandleHttpRequest.cs new file mode 100644 index 00000000..fae43abf --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.HttpHandler/Functions/HandleHttpRequest.cs @@ -0,0 +1,210 @@ +using System.Net.Http; +using BotSharp.Plugin.HttpHandler.LlmContexts; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; + +namespace BotSharp.Plugin.HttpHandler.Functions; + +public class HandleHttpRequest : IFunctionCallback +{ + public string Name => "handle_http_request"; + public string Indication => "Handling http request"; + + private readonly IServiceProvider _services; + private readonly ILogger _logger; + private readonly IHttpClientFactory _httpClientFactory; + private readonly IHttpContextAccessor _context; + private readonly BotSharpOptions _options; + + public HandleHttpRequest(IServiceProvider services, + ILogger logger, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor context, + BotSharpOptions options) + { + _services = services; + _logger = logger; + _httpClientFactory = httpClientFactory; + _context = context; + _options = options; + } + + public async Task Execute(RoleDialogModel message) + { + var args = JsonSerializer.Deserialize(message.FunctionArgs, _options.JsonSerializerOptions); + var url = args?.RequestUrl; + var method = args?.HttpMethod; + var content = args?.RequestContent; + + try + { + var response = await SendHttpRequest(url, method, content); + var responseContent = await HandleHttpResponse(response); + message.RichContent = BuildRichContent(responseContent); + return await Task.FromResult(true); + } + catch (Exception ex) + { + var msg = $"Fail when sending http request. Url: {url}, method: {method}, content: {content}"; + _logger.LogWarning($"{msg}\n(Error: {ex.Message})"); + message.RichContent = BuildRichContent($"{msg}"); + return await Task.FromResult(false); + } + } + + private async Task SendHttpRequest(string? url, string? method, string? content) + { + if (string.IsNullOrEmpty(url)) return null; + + var settings = _services.GetRequiredService(); + using var client = _httpClientFactory.CreateClient(); + AddRequestHeaders(client); + + var (uri, request) = BuildHttpRequest(url, method, content); + if (string.IsNullOrEmpty(uri.Host)) + { + client.BaseAddress = new Uri(settings.BaseAddress); + } + + var response = await client.SendAsync(request); + + if (response == null || !response.IsSuccessStatusCode) + { + throw new Exception($"Status code: {response?.StatusCode}"); + } + + return response; + } + + private void AddRequestHeaders(HttpClient client) + { + client.DefaultRequestHeaders.Add("Authorization", $"{_context.HttpContext.Request.Headers["Authorization"]}"); + + var settings = _services.GetRequiredService(); + var origin = !string.IsNullOrEmpty(settings.Origin) ? settings.Origin : $"{_context.HttpContext.Request.Headers["Origin"]}"; + if (!string.IsNullOrEmpty(origin)) + { + client.DefaultRequestHeaders.Add("Origin", origin); + } + } + + private (Uri, HttpRequestMessage) BuildHttpRequest(string url, string? method, string? content) + { + var httpMethod = GetHttpMethod(method); + StringContent httpContent; + + if (httpMethod == HttpMethod.Get) + { + httpContent = BuildHttpContent(string.Empty); + } + else + { + httpContent = BuildHttpContent(content); + } + + var requestUrl = BuildQuery(url, content); + var uri = new Uri(requestUrl); + return (uri, new HttpRequestMessage + { + RequestUri = uri, + Method = httpMethod, + Content = httpContent + }); + } + + private HttpMethod GetHttpMethod(string? method) + { + var localMethod = method?.Trim()?.ToUpper(); + HttpMethod matchMethod; + + switch (localMethod) + { + case "GET": + matchMethod = HttpMethod.Get; + break; + case "DELETE": + matchMethod = HttpMethod.Delete; + break; + case "PUT": + matchMethod = HttpMethod.Put; + break; + case "Patch": + matchMethod = HttpMethod.Patch; + break; + default: + matchMethod = HttpMethod.Post; + break; + } + return matchMethod; + } + + private StringContent BuildHttpContent(string? content) + { + var str = string.Empty; + try + { + var json = JsonSerializer.Deserialize(content ?? string.Empty, _options.JsonSerializerOptions); + str = JsonSerializer.Serialize(json, _options.JsonSerializerOptions); + } + catch (Exception ex) + { + _logger.LogWarning($"Error when build http content: {content}\n(Error: {ex.Message})"); + } + + return new StringContent(str, Encoding.UTF8, "application/json"); + } + + private string BuildQuery(string url, string? content) + { + if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(content)) return url; + + try + { + var queries = new List(); + var json = JsonSerializer.Deserialize(content, _options.JsonSerializerOptions); + var root = json.RootElement; + foreach (var prop in root.EnumerateObject()) + { + var name = prop.Name; + var value = prop.Value.ToString(); + if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(value)) + { + continue; + } + + queries.Add($"{name}={value}"); + } + + if (!queries.IsNullOrEmpty()) + { + url += $"?{string.Join('&', queries)}"; + } + return url; + } + catch (Exception ex) + { + _logger.LogWarning($"Error when building url query. Url: {url}, Content: {content}\n(Error: {ex.Message})"); + return url; + } + } + + private async Task HandleHttpResponse(HttpResponseMessage? response) + { + if (response == null) return string.Empty; + + return await response.Content.ReadAsStringAsync(); + } + + private RichContent BuildRichContent(string? content) + { + var state = _services.GetRequiredService(); + + var text = !string.IsNullOrEmpty(content) ? content : "Cannot get any response from the http request."; + return new RichContent + { + Recipient = new Recipient { Id = state.GetConversationId() }, + Editor = EditorTypeEnum.Text, + Message = new TextMessage(text) + }; + } +} diff --git a/src/Plugins/BotSharp.Plugin.HttpHandler/HttpHandlerPlugin.cs b/src/Plugins/BotSharp.Plugin.HttpHandler/HttpHandlerPlugin.cs index b64a3ad8..3e31c096 100644 --- a/src/Plugins/BotSharp.Plugin.HttpHandler/HttpHandlerPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.HttpHandler/HttpHandlerPlugin.cs @@ -1,3 +1,5 @@ +using BotSharp.Abstraction.Http.Settings; +using BotSharp.Abstraction.Settings; using Microsoft.Extensions.Configuration; namespace BotSharp.Plugin.HttpHandler; @@ -12,6 +14,10 @@ public class HttpHandlerPlugin : IBotSharpPlugin public void RegisterDI(IServiceCollection services, IConfiguration config) { - + services.AddScoped(provider => + { + var settingService = provider.GetRequiredService(); + return settingService.Bind("Http"); + }); } } diff --git a/src/Plugins/BotSharp.Plugin.HttpHandler/LlmContexts/LlmContextIn.cs b/src/Plugins/BotSharp.Plugin.HttpHandler/LlmContexts/LlmContextIn.cs new file mode 100644 index 00000000..8d7f506e --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.HttpHandler/LlmContexts/LlmContextIn.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace BotSharp.Plugin.HttpHandler.LlmContexts; + +public class LlmContextIn +{ + [JsonPropertyName("request_url")] + public string? RequestUrl { get; set; } + + [JsonPropertyName("http_method")] + public string? HttpMethod { get; set; } + + [JsonPropertyName("request_content")] + public string? RequestContent { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.HttpHandler/Using.cs b/src/Plugins/BotSharp.Plugin.HttpHandler/Using.cs index f813420b..4344b430 100644 --- a/src/Plugins/BotSharp.Plugin.HttpHandler/Using.cs +++ b/src/Plugins/BotSharp.Plugin.HttpHandler/Using.cs @@ -11,4 +11,9 @@ global using BotSharp.Abstraction.Agents.Models; global using BotSharp.Abstraction.Templating; global using Microsoft.Extensions.DependencyInjection; global using System.Linq; -global using BotSharp.Abstraction.Utilities; \ No newline at end of file +global using BotSharp.Abstraction.Utilities; +global using BotSharp.Abstraction.Messaging; +global using BotSharp.Abstraction.Messaging.Models.RichContent; +global using BotSharp.Abstraction.Options; +global using BotSharp.Abstraction.Http.Settings; +global using BotSharp.Abstraction.Messaging.Enums; \ No newline at end of file diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index 5482e6b1..e141d502 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -141,6 +141,11 @@ "Driver": "Playwright" }, + "Http": { + "BaseAddress": "", + "Origin": "" + }, + "Statistics": { "DataDir": "stats" }, From e84dd7013b8920ef3d3295bb8dab6a1de4f5ab26 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 20 May 2024 17:07:42 -0500 Subject: [PATCH 150/201] minor change --- .../Functions/HandleHttpRequest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.HttpHandler/Functions/HandleHttpRequest.cs b/src/Plugins/BotSharp.Plugin.HttpHandler/Functions/HandleHttpRequest.cs index fae43abf..db300f83 100644 --- a/src/Plugins/BotSharp.Plugin.HttpHandler/Functions/HandleHttpRequest.cs +++ b/src/Plugins/BotSharp.Plugin.HttpHandler/Functions/HandleHttpRequest.cs @@ -95,7 +95,7 @@ public class HandleHttpRequest : IFunctionCallback if (httpMethod == HttpMethod.Get) { - httpContent = BuildHttpContent(string.Empty); + httpContent = BuildHttpContent("{}"); } else { @@ -143,7 +143,7 @@ public class HandleHttpRequest : IFunctionCallback var str = string.Empty; try { - var json = JsonSerializer.Deserialize(content ?? string.Empty, _options.JsonSerializerOptions); + var json = JsonSerializer.Deserialize(content ?? "{}", _options.JsonSerializerOptions); str = JsonSerializer.Serialize(json, _options.JsonSerializerOptions); } catch (Exception ex) From 2147d69ecf433638cb8c3388ab3ad295a792a7df Mon Sep 17 00:00:00 2001 From: Haiping Chen <101423@smsassist.com> Date: Mon, 20 May 2024 21:13:12 -0500 Subject: [PATCH 151/201] translation improvement. --- .../BotSharp.Core/Translation/TranslationService.cs | 5 +---- .../templates/translation_prompt.liquid | 4 ++-- .../Controllers/ConversationController.cs | 9 +-------- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index ff28b4e6..5c60e108 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -302,10 +302,7 @@ public class TranslationService : ITranslationService /// private async Task InnerTranslate(List texts, string language, string template) { - var jsonString = JsonSerializer.Serialize(texts, new JsonSerializerOptions - { - WriteIndented = true, - }) ; + var jsonString = JsonSerializer.Serialize(texts); var translator = new Agent { Id = Guid.Empty.ToString(), diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid index 7403130c..3d33375b 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid @@ -1,5 +1,5 @@ {{ text_list }} ===== -Translate the above sentences into {{ language }}. -Output the translated text in JSON {"input_lang":"original text language", "output_lang":"{{ language }}", "texts":[""]}. +Translate all the above sentences into {{ language }}. +Output the translated text in JSON {"input_lang":"original text language", "output_count": {{ text_list_size }}, "output_lang":"{{ language }}", "texts":[{"id": 1, "text":""}]}. diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index 34095acf..a1d2ca3e 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -1,6 +1,4 @@ using BotSharp.Abstraction.Routing; -using Newtonsoft.Json.Serialization; -using Newtonsoft.Json; namespace BotSharp.OpenAPI.Controllers; @@ -303,12 +301,7 @@ public class ConversationController : ControllerBase private async Task OnChunkReceived(HttpResponse response, RoleDialogModel message) { - var json = JsonConvert.SerializeObject(message, new JsonSerializerSettings - { - Formatting = Formatting.None, - ContractResolver = new CamelCasePropertyNamesContractResolver(), - NullValueHandling = NullValueHandling.Ignore, - }); + var json = JsonSerializer.Serialize(message); var buffer = Encoding.UTF8.GetBytes($"data:{json}\n"); await response.Body.WriteAsync(buffer, 0, buffer.Length); From 018d889ad33429fb756a8b6b4bce3776eff46c36 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Tue, 21 May 2024 05:51:38 -0500 Subject: [PATCH 152/201] Add UserCreated hook. --- .../BotSharp.Abstraction/Users/IAuthenticationHook.cs | 1 + .../BotSharp.Core/Users/Services/UserService.cs | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IAuthenticationHook.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IAuthenticationHook.cs index 33b8086f..0de10328 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/IAuthenticationHook.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/IAuthenticationHook.cs @@ -8,4 +8,5 @@ public interface IAuthenticationHook Task Authenticate(string id, string password); void AddClaims(List claims); void BeforeSending(Token token); + Task UserCreated(User user); } diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index 6e856b9f..d51b6f4a 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Repositories; using BotSharp.Abstraction.Users.Models; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; @@ -57,6 +56,12 @@ public class UserService : IUserService _logger.LogWarning($"Created new user account: {record.Id} {record.UserName}"); Utilities.ClearCache(); + var hooks = _services.GetServices(); + foreach (var hook in hooks) + { + await hook.UserCreated(record); + } + return record; } From 45a85d2ea95cce0bc3be755e40d944624088d1fb Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 21 May 2024 10:06:29 -0500 Subject: [PATCH 153/201] minor change --- .../Functions/HandleHttpRequest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.HttpHandler/Functions/HandleHttpRequest.cs b/src/Plugins/BotSharp.Plugin.HttpHandler/Functions/HandleHttpRequest.cs index db300f83..238a1904 100644 --- a/src/Plugins/BotSharp.Plugin.HttpHandler/Functions/HandleHttpRequest.cs +++ b/src/Plugins/BotSharp.Plugin.HttpHandler/Functions/HandleHttpRequest.cs @@ -165,8 +165,8 @@ public class HandleHttpRequest : IFunctionCallback var root = json.RootElement; foreach (var prop in root.EnumerateObject()) { - var name = prop.Name; - var value = prop.Value.ToString(); + var name = prop.Name.Trim(); + var value = prop.Value.ToString().Trim(); if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(value)) { continue; From 003de30538ea864e3738b89ee6a7c23441d95dd1 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 21 May 2024 11:20:03 -0500 Subject: [PATCH 154/201] resolve conflict --- .../BotSharp.OpenAPI/Controllers/ConversationController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index 08e4c0a0..6831e74c 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Routing; +using BotSharp.Abstraction.Users.Enums; namespace BotSharp.OpenAPI.Controllers; From 6a0a400500951cfef9cae03bf88533ede37ba008 Mon Sep 17 00:00:00 2001 From: Haiping Chen <101423@smsassist.com> Date: Tue, 21 May 2024 12:02:06 -0500 Subject: [PATCH 155/201] Fix SSE response format. --- .../Translation/Models/TranslationOutput.cs | 2 +- .../Translation/TranslationService.cs | 2 +- .../Controllers/ConversationController.cs | 24 ++++++++++++------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Translation/Models/TranslationOutput.cs b/src/Infrastructure/BotSharp.Abstraction/Translation/Models/TranslationOutput.cs index 52ad54ec..b15bfef4 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Translation/Models/TranslationOutput.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Translation/Models/TranslationOutput.cs @@ -9,5 +9,5 @@ public class TranslationOutput public string OutputLanguage { get; set; } = LanguageType.ENGLISH; [JsonPropertyName("texts")] - public string[] Texts { get; set; } = Array.Empty(); + public TranslationInput[] Texts { get; set; } = Array.Empty(); } diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index 5c60e108..e5149751 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -79,7 +79,7 @@ public class TranslationService : ITranslationService for (var i = 0; i < texts.Count; i++) { - map[keys[i]] = translatedTexts[i]; + map[keys[i]] = translatedTexts[i].Text; } clonedData = Assign(clonedData, map); diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index a1d2ca3e..80374a6d 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -257,7 +257,11 @@ public class ConversationController : ControllerBase conv.SetConversationId(conversationId, input.States); SetStates(conv, input); - var response = new ChatResponseModel(); + var response = new ChatResponseModel + { + ConversationId = conversationId, + MessageId = inputMsg.MessageId, + }; Response.StatusCode = 200; Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.ContentType, "text/event-stream"); @@ -266,6 +270,7 @@ public class ConversationController : ControllerBase await conv.SendMessage(agentId, inputMsg, replyMessage: input.Postback, + // responsed generated async msg => { response.Text = !string.IsNullOrEmpty(msg.SecondaryContent) ? msg.SecondaryContent : msg.Content; @@ -274,18 +279,21 @@ public class ConversationController : ControllerBase response.Instruction = msg.Instruction; response.Data = msg.Data; - await OnChunkReceived(Response, msg); + await OnChunkReceived(Response, response); }, + // executing async msg => { - var message = new RoleDialogModel(AgentRole.Function, msg.Content) + var indicator = new ChatResponseModel { - FunctionArgs = msg.FunctionArgs, - FunctionName = msg.FunctionName, - Indication = msg.Indication + ConversationId = conversationId, + MessageId = msg.MessageId, + Text = msg.Indication, + Function = "indicating", }; - await OnChunkReceived(Response, message); + await OnChunkReceived(Response, indicator); }, + // executed async msg => { @@ -299,7 +307,7 @@ public class ConversationController : ControllerBase // await OnEventCompleted(Response); } - private async Task OnChunkReceived(HttpResponse response, RoleDialogModel message) + private async Task OnChunkReceived(HttpResponse response, ChatResponseModel message) { var json = JsonSerializer.Serialize(message); From effd44eb7c7f6a620f7a50be71fbbd142223cfb8 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 21 May 2024 15:55:12 -0500 Subject: [PATCH 156/201] add agent delete --- .../Services/AgentService.CreateAgent.cs | 23 ++---------- .../Services/AgentService.DeleteAgent.cs | 13 ++++++- .../FileRepository/FileRepository.Agent.cs | 35 ++++++++++++++++++- .../Controllers/AgentController.cs | 11 ++++-- 4 files changed, 57 insertions(+), 25 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs index d3754a94..ae429484 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs @@ -22,32 +22,13 @@ public partial class AgentService var dbSettings = _services.GetRequiredService(); var agentSettings = _services.GetRequiredService(); - var filePath = Path.Combine(dbSettings.FileRepository, agentSettings.DataDir); - var foundAgent = FetchAgentFileByName(agent.Name, filePath); - - if (foundAgent != null) - { - agentRecord.SetId(foundAgent.Id) - .SetName(foundAgent.Name) - .SetDescription(foundAgent.Description) - .SetIsPublic(foundAgent.IsPublic) - .SetDisabled(foundAgent.Disabled) - .SetAgentType(foundAgent.Type) - .SetProfiles(foundAgent.Profiles) - .SetRoutingRules(foundAgent.RoutingRules) - .SetInstruction(foundAgent.Instruction) - .SetTemplates(foundAgent.Templates) - .SetFunctions(foundAgent.Functions) - .SetResponses(foundAgent.Responses) - .SetLlmConfig(foundAgent.LlmConfig); - } var user = _db.GetUserById(_user.Id); var userAgentRecord = new UserAgent { Id = Guid.NewGuid().ToString(), UserId = user.Id, - AgentId = foundAgent?.Id ?? agentRecord.Id, + AgentId = agentRecord.Id, Editable = false, CreatedTime = DateTime.UtcNow, UpdatedTime = DateTime.UtcNow @@ -61,7 +42,7 @@ public partial class AgentService Utilities.ClearCache(); - return agentRecord; + return await Task.FromResult(agentRecord); } private Agent FetchAgentFileByName(string agentName, string filePath) diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.DeleteAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.DeleteAgent.cs index 23111fd2..1fe5c6e1 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.DeleteAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.DeleteAgent.cs @@ -1,9 +1,20 @@ +using BotSharp.Abstraction.Users.Enums; + namespace BotSharp.Core.Agents.Services; public partial class AgentService { public async Task DeleteAgent(string id) { - throw new NotImplementedException(); + var user = _db.GetUserById(_user.Id); + var agent = _db.GetAgentsByUser(_user.Id).FirstOrDefault(x => x.Id.IsEqualTo(id)); + + if (user?.Role != UserRole.Admin && agent == null) + { + return false; + } + + var deleted = _db.DeleteAgent(id); + return await Task.FromResult(deleted); } } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs index a46669a5..b7141a5b 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs @@ -436,7 +436,40 @@ namespace BotSharp.Core.Repository public bool DeleteAgent(string agentId) { - return false; + if (string.IsNullOrEmpty(agentId)) return false; + + try + { + var agentDir = GetAgentDataDir(agentId); + if (string.IsNullOrEmpty(agentDir)) return false; + + // Delete agent user relationships + var usersDir = Path.Combine(_dbSettings.FileRepository, "users"); + if (Directory.Exists(usersDir)) + { + foreach (var userDir in Directory.GetDirectories(usersDir)) + { + var userAgentFile = Directory.GetFiles(userDir).FirstOrDefault(x => Path.GetFileName(x) == USER_AGENT_FILE); + if (string.IsNullOrEmpty(userAgentFile)) continue; + + var text = File.ReadAllText(userAgentFile); + var userAgents = JsonSerializer.Deserialize>(text, _options); + if (userAgents.IsNullOrEmpty()) continue; + + userAgents = userAgents.Where(x => x.AgentId != agentId).ToList(); + File.WriteAllText(userAgentFile, JsonSerializer.Serialize(userAgents, _options)); + } + } + + // Delete agent folder + Directory.Delete(agentDir, true); + + return true; + } + catch + { + return false; + } } } } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs index d867e782..a7b5652d 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs @@ -1,5 +1,4 @@ using BotSharp.Abstraction.Agents.Models; -using BotSharp.Abstraction.Repositories; using BotSharp.Abstraction.Users.Enums; namespace BotSharp.OpenAPI.Controllers; @@ -27,7 +26,7 @@ public class AgentController : ControllerBase } [HttpGet("/agent/{id}")] - public async Task GetAgent([FromRoute] string id) + public async Task GetAgent([FromRoute] string id) { var agents = await GetAgents(new AgentFilter { @@ -35,6 +34,8 @@ public class AgentController : ControllerBase }); var targetAgent = agents.Items.FirstOrDefault(); + if (targetAgent == null) return null; + var redirectAgentIds = targetAgent.RoutingRules .Where(x => !string.IsNullOrEmpty(x.RedirectTo)) .Select(x => x.RedirectTo).ToList(); @@ -133,4 +134,10 @@ public class AgentController : ControllerBase model.Id = agentId; return await _agentService.PatchAgentTemplate(model); } + + [HttpDelete("/agent/{agentId}")] + public async Task DeleteAgent([FromRoute] string agentId) + { + return await _agentService.DeleteAgent(agentId); + } } \ No newline at end of file From 343f3b91abcea27648b6dc62b424733aa36e5d78 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Tue, 21 May 2024 22:07:26 -0500 Subject: [PATCH 157/201] Retry language translation. --- .../BotSharp.Core/Translation/TranslationService.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index e5149751..44bb22a8 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -62,10 +62,18 @@ public class TranslationService : ITranslationService Id = i + 1, Text = text }).ToList(); - var translatedStringList = await InnerTranslate(texts, language, template); try { + var translatedStringList = await InnerTranslate(texts, language, template); + + int retry = 0; + while (translatedStringList.Texts.Length != texts.Count && retry < 3) + { + translatedStringList = await InnerTranslate(texts, language, template); + retry++; + } + // Override language if it's Unknown, it's used to output the corresponding language. var states = _services.GetRequiredService(); if (!states.ContainsState(StateConst.LANGUAGE)) From c93511065a7b50218b02b326dbb39633c9100b94 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 21 May 2024 23:33:31 -0500 Subject: [PATCH 158/201] refine file storage --- .../Files/IBotSharpFileService.cs | 7 +- .../Files/BotSharpFileService.cs | 105 ++++++++++++++---- .../Controllers/FileController.cs | 25 ++++- .../ViewModels/Users/UserViewModel.cs | 4 +- .../WebSocketsMiddleware.cs | 22 +++- 5 files changed, 133 insertions(+), 30 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs b/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs index 272abf0d..16197b9c 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs @@ -5,8 +5,11 @@ public interface IBotSharpFileService string GetDirectory(string conversationId); IEnumerable GetChatImages(string conversationId, List conversations, int offset = 2); IEnumerable GetMessageFiles(string conversationId, IEnumerable messageIds, bool imageOnly = false); - string? GetMessageFile(string conversationId, string messageId, string fileName); - void SaveMessageFiles(string conversationId, string messageId, List files); + string GetMessageFile(string conversationId, string messageId, string fileName); + bool SaveMessageFiles(string conversationId, string messageId, List files); + + string GetUserAvatar(); + bool SaveUserAvatar(BotSharpFile file); /// /// Delete files under messages diff --git a/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs b/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs index d7e961be..178790bc 100644 --- a/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs +++ b/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.StaticFiles; +using System; using System.IO; using System.Threading; @@ -8,21 +9,27 @@ public class BotSharpFileService : IBotSharpFileService { private readonly BotSharpDatabaseSettings _dbSettings; private readonly IServiceProvider _services; + private readonly IUserIdentity _user; private readonly ILogger _logger; private readonly string _baseDir; private readonly IEnumerable _allowedTypes = new List { "image/png", "image/jpeg" }; private const string CONVERSATION_FOLDER = "conversations"; private const string FILE_FOLDER = "files"; + private const string USERS_FOLDER = "users"; + private const string USER_AVATAR_FOLDER = "avatar"; + private const int MIN_OFFSET = 1; private const int MAX_OFFSET = 5; public BotSharpFileService( BotSharpDatabaseSettings dbSettings, + IUserIdentity user, ILogger logger, IServiceProvider services) { _dbSettings = dbSettings; + _user = user; _logger = logger; _services = services; _baseDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, dbSettings.FileRepository); @@ -38,7 +45,7 @@ public class BotSharpFileService : IBotSharpFileService return dir; } - public IEnumerable GetChatImages(string conversationId, List conversations, int offset = 2) + public IEnumerable GetChatImages(string conversationId, List conversations, int offset = 1) { var files = new List(); if (string.IsNullOrEmpty(conversationId) || conversations.IsNullOrEmpty()) @@ -68,7 +75,7 @@ public class BotSharpFileService : IBotSharpFileService foreach (var messageId in messageIds) { var dir = GetConversationFileDirectory(conversationId, messageId); - if (string.IsNullOrEmpty(dir)) + if (!ExistDirectory(dir)) { continue; } @@ -101,24 +108,24 @@ public class BotSharpFileService : IBotSharpFileService return files; } - public string? GetMessageFile(string conversationId, string messageId, string fileName) + public string GetMessageFile(string conversationId, string messageId, string fileName) { var dir = GetConversationFileDirectory(conversationId, messageId); - if (string.IsNullOrEmpty(dir)) + if (!ExistDirectory(dir)) { - return null; + return string.Empty; } var found = Directory.GetFiles(dir).FirstOrDefault(f => Path.GetFileNameWithoutExtension(f).IsEqualTo(fileName)); return found; } - public void SaveMessageFiles(string conversationId, string messageId, List files) + public bool SaveMessageFiles(string conversationId, string messageId, List files) { - if (files.IsNullOrEmpty()) return; + if (files.IsNullOrEmpty()) return false; var dir = GetConversationFileDirectory(conversationId, messageId, createNewDir: true); - if (string.IsNullOrEmpty(dir)) return; + if (!ExistDirectory(dir)) return false; try { @@ -136,10 +143,53 @@ public class BotSharpFileService : IBotSharpFileService Thread.Sleep(100); File.WriteAllBytes(Path.Combine(dir, fileName), bytes); } + return true; } catch (Exception ex) { - _logger.LogError($"Error when saving conversation files: {ex.Message}"); + _logger.LogWarning($"Error when saving conversation files: {ex.Message}"); + return false; + } + } + + public string GetUserAvatar() + { + var db = _services.GetRequiredService(); + var user = db.GetUserById(_user.Id); + var dir = GetUserAvatarDir(user?.Id); + + if (!ExistDirectory(dir)) return string.Empty; + + var found = Directory.GetFiles(dir).FirstOrDefault() ?? string.Empty; + return found; + } + + public bool SaveUserAvatar(BotSharpFile file) + { + if (file == null || string.IsNullOrEmpty(file.FileData)) return false; + + try + { + var db = _services.GetRequiredService(); + var user = db.GetUserById(_user.Id); + var dir = GetUserAvatarDir(user?.Id); + + if (string.IsNullOrEmpty(dir)) return false; + + if (Directory.Exists(dir)) + { + Directory.Delete(dir, true); + } + + dir = GetUserAvatarDir(user?.Id, createNewDir: true); + var (_, bytes) = GetFileInfoFromData(file.FileData); + File.WriteAllBytes(Path.Combine(dir, file.FileName), bytes); + return true; + } + catch (Exception ex) + { + _logger.LogWarning($"Error when saving user avatar: {ex.Message}"); + return false; } } @@ -152,9 +202,9 @@ public class BotSharpFileService : IBotSharpFileService var prevDir = GetConversationFileDirectory(conversationId, targetMessageId); var newDir = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, FILE_FOLDER, newMessageId); - if (Directory.Exists(prevDir)) + if (ExistDirectory(prevDir)) { - if (Directory.Exists(newDir)) + if (ExistDirectory(newDir)) { Directory.Delete(newDir, true); } @@ -182,7 +232,7 @@ public class BotSharpFileService : IBotSharpFileService foreach (var conversationId in conversationIds) { var convDir = FindConversationDirectory(conversationId); - if (string.IsNullOrEmpty(convDir)) continue; + if (!ExistDirectory(convDir)) continue; Directory.Delete(convDir, true); } @@ -215,16 +265,9 @@ public class BotSharpFileService : IBotSharpFileService } var dir = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, FILE_FOLDER, messageId); - if (!Directory.Exists(dir)) + if (!Directory.Exists(dir) && createNewDir) { - if (createNewDir) - { - Directory.CreateDirectory(dir); - } - else - { - return string.Empty; - } + Directory.CreateDirectory(dir); } return dir; } @@ -234,8 +277,21 @@ public class BotSharpFileService : IBotSharpFileService if (string.IsNullOrEmpty(conversationId)) return null; var dir = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId); - if (!Directory.Exists(dir)) return null; + return dir; + } + private string GetUserAvatarDir(string? userId, bool createNewDir = false) + { + if (string.IsNullOrEmpty(userId)) + { + return string.Empty; + } + + var dir = Path.Combine(_baseDir, USERS_FOLDER, userId, USER_AVATAR_FOLDER); + if (!Directory.Exists(dir) && createNewDir) + { + Directory.CreateDirectory(dir); + } return dir; } @@ -250,5 +306,10 @@ public class BotSharpFileService : IBotSharpFileService return contentType; } + + private bool ExistDirectory(string? dir) + { + return !string.IsNullOrEmpty(dir) && Directory.Exists(dir); + } #endregion } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs index cfae602c..0c7fa255 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs @@ -46,7 +46,7 @@ public class FileController : ControllerBase } [HttpGet("/conversation/{conversationId}/message/{messageId}/file/{fileName}")] - public async Task GetMessageFile([FromRoute] string conversationId, [FromRoute] string messageId, [FromRoute] string fileName) + public IActionResult GetMessageFile([FromRoute] string conversationId, [FromRoute] string messageId, [FromRoute] string fileName) { var fileService = _services.GetRequiredService(); var file = fileService.GetMessageFile(conversationId, messageId, fileName); @@ -54,7 +54,30 @@ public class FileController : ControllerBase { return NotFound(); } + return BuildFileResult(file); + } + [HttpPost("/user/avatar")] + public bool UploadUserAvatar([FromBody] BotSharpFile file) + { + var fileService = _services.GetRequiredService(); + return fileService.SaveUserAvatar(file); + } + + [HttpGet("/user/avatar")] + public IActionResult GetUserAvatar() + { + var fileService = _services.GetRequiredService(); + var file = fileService.GetUserAvatar(); + if (string.IsNullOrEmpty(file)) + { + return NotFound(); + } + return BuildFileResult(file); + } + + private FileContentResult BuildFileResult(string file) + { using Stream stream = System.IO.File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read); var bytes = new byte[stream.Length]; stream.Read(bytes, 0, (int)stream.Length); diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserViewModel.cs index eeb1a8a2..8d28cf03 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserViewModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserViewModel.cs @@ -19,6 +19,7 @@ public class UserViewModel public string Source { get; set; } [JsonPropertyName("external_id")] public string? ExternalId { get; set; } + public string Avatar { get; set; } = "/user/avatar"; [JsonPropertyName("create_date")] public DateTime CreateDate { get; set; } [JsonPropertyName("update_date")] @@ -47,7 +48,8 @@ public class UserViewModel Source = user.Source, ExternalId = user.ExternalId, CreateDate = user.CreatedTime, - UpdateDate = user.UpdatedTime + UpdateDate = user.UpdatedTime, + Avatar = "/user/avatar" }; } } diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/WebSocketsMiddleware.cs b/src/Plugins/BotSharp.Plugin.ChatHub/WebSocketsMiddleware.cs index e20f8602..79cb803a 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/WebSocketsMiddleware.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/WebSocketsMiddleware.cs @@ -14,13 +14,11 @@ public class WebSocketsMiddleware public async Task Invoke(HttpContext httpContext) { - var request = httpContext.Request;; - var messageFileRegex = new Regex(@"/conversation/[a-z0-9-]+/message/[a-z0-9-]+/file/[a-z0-9-]+", RegexOptions.IgnoreCase); + var request = httpContext.Request; // web sockets cannot pass headers so we must take the access token from query param and // add it to the header before authentication middleware runs - if ((request.Path.StartsWithSegments("/chatHub", StringComparison.OrdinalIgnoreCase) - || messageFileRegex.IsMatch(request.Path.Value ?? string.Empty)) && + if ((VerifyChatHubRequest(request) || VerifyGetRequest(request)) && request.Query.TryGetValue("access_token", out var accessToken)) { request.Headers["Authorization"] = $"Bearer {accessToken}"; @@ -28,4 +26,20 @@ public class WebSocketsMiddleware await _next(httpContext); } + + private bool VerifyChatHubRequest(HttpRequest request) + { + return request.Path.StartsWithSegments("/chatHub", StringComparison.OrdinalIgnoreCase); + } + + private bool VerifyGetRequest(HttpRequest request) + { + var regexes = new List + { + new Regex(@"/conversation/[a-z0-9-]+/message/[a-z0-9-]+/file/[a-z0-9-]+", RegexOptions.IgnoreCase), + new Regex(@"/user/avatar", RegexOptions.IgnoreCase) + }; + + return request.Method.IsEqualTo("GET") && regexes.Any(x => x.IsMatch(request.Path.Value ?? string.Empty)); + } } From adc6486345fe53bd20d8d40b565fe32d9140f1a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=93=E7=A3=8A?= Date: Wed, 22 May 2024 14:53:36 +0800 Subject: [PATCH 159/201] Update TranslationService.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hotfix json escaped question error: [ERR] [] '<' is not a hex digit following '\u' within a JSON string. The string should be correctly escaped. Path: $.texts[0].text | LineNumber: 0 | BytePositionInLine: 411. json: {"input_lang":"English", "output_count":3, "output_lang":"Spanish", "texts":[{"id":1,"text":"Aquí hay un resumen de los horarios que ha indicado que está disponible. Las fechas no están confirmadas. El técnico revisará y se pondrá en contacto para programar.\u003Cp\u003E\u003Cb\u003EJueves, 23 de mayo de 2024\u003C/b\u003E\u003C/br\u003ETodo el día (08:00 AM - 08:00 PM)\u003C/p\u003E\u003Cp\u003E\u003Viernes, 24 de mayo de 2024\u003C/b\u003E\u003C/br\u003EMañana (08:00 AM - 01:00 PM)\u003C/p\u003E\u003Cp\u003E\u003Lunes, 27 de mayo de 2024\u003C/b\u003E\u003C/br\u003ETarde (01:00 PM - 08:00 PM)\u003C/p\u003E"},{"id":2,"text":"Se ve bien"},{"id":3,"text":"Empezar de nuevo"}]} --- .../BotSharp.Core/Translation/TranslationService.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index 44bb22a8..7067eb7e 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -310,7 +310,8 @@ public class TranslationService : ITranslationService /// private async Task InnerTranslate(List texts, string language, string template) { - var jsonString = JsonSerializer.Serialize(texts); + var options = new JsonSerializerOptions() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; + var jsonString = JsonSerializer.Serialize(texts, options); var translator = new Agent { Id = Guid.Empty.ToString(), From 6f8c840dcd273ded0c03d237efef2c0a4369602d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=93=E7=A3=8A?= Date: Wed, 22 May 2024 15:08:13 +0800 Subject: [PATCH 160/201] Update translation_prompt.liquid optimize translation prompt 1.Enrich the translation examples. 2.Add necessary validation conditions for "output_count". --- .../templates/translation_prompt.liquid | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid index 3d33375b..6b3a8677 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid @@ -2,4 +2,5 @@ ===== Translate all the above sentences into {{ language }}. -Output the translated text in JSON {"input_lang":"original text language", "output_count": {{ text_list_size }}, "output_lang":"{{ language }}", "texts":[{"id": 1, "text":""}]}. +Output the translated text in JSON {"input_lang":"original text language", "output_count": {{ text_list_size }}, "output_lang":"{{ language }}", "texts":[{"id": 1, "text":""},{"id": 2, "text":""}]}. +The "output_count" must equal the length of the "texts" array in the output. From 80a60b23eb98a570e73d3296f95b1f8fa2477840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=93=E7=A3=8A?= Date: Wed, 22 May 2024 20:12:15 +0800 Subject: [PATCH 161/201] Update TranslationService.cs add using --- .../BotSharp.Core/Translation/TranslationService.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index 7067eb7e..97e997c7 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -6,6 +6,7 @@ using BotSharp.Abstraction.Templating; using BotSharp.Abstraction.Translation.Models; using System.Collections; using System.Reflection; +using System.Text.Encodings.Web; namespace BotSharp.Core.Translation; From 9dad9ae80be85d3615995173d561470dee1e2c61 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 22 May 2024 10:39:04 -0500 Subject: [PATCH 162/201] split file service --- .../Files/BotSharpFileService.Conversation.cs | 188 ++++++++++++++ .../Files/BotSharpFileService.User.cs | 65 +++++ .../Files/BotSharpFileService.cs | 234 +----------------- 3 files changed, 254 insertions(+), 233 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.Conversation.cs create mode 100644 src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.User.cs diff --git a/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.Conversation.cs b/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.Conversation.cs new file mode 100644 index 00000000..f12ecb60 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.Conversation.cs @@ -0,0 +1,188 @@ +using Microsoft.AspNetCore.StaticFiles; +using System.IO; +using System.Threading; + +namespace BotSharp.Core.Files; + +public partial class BotSharpFileService +{ + public IEnumerable GetChatImages(string conversationId, List conversations, int offset = 1) + { + var files = new List(); + if (string.IsNullOrEmpty(conversationId) || conversations.IsNullOrEmpty()) + { + return files; + } + + if (offset <= 0) + { + offset = MIN_OFFSET; + } + else if (offset > MAX_OFFSET) + { + offset = MAX_OFFSET; + } + + var messageIds = conversations.Select(x => x.MessageId).Distinct().TakeLast(offset).ToList(); + files = GetMessageFiles(conversationId, messageIds, imageOnly: true).ToList(); + return files; + } + + public IEnumerable GetMessageFiles(string conversationId, IEnumerable messageIds, bool imageOnly = false) + { + var files = new List(); + if (messageIds.IsNullOrEmpty()) return files; + + foreach (var messageId in messageIds) + { + var dir = GetConversationFileDirectory(conversationId, messageId); + if (!ExistDirectory(dir)) + { + continue; + } + + foreach (var file in Directory.GetFiles(dir)) + { + var contentType = GetFileContentType(file); + if (imageOnly && !_allowedTypes.Contains(contentType)) + { + continue; + } + + var fileName = Path.GetFileNameWithoutExtension(file); + var extension = Path.GetExtension(file); + var fileType = extension.Substring(1); + + var model = new MessageFileModel() + { + MessageId = messageId, + FileUrl = $"/conversation/{conversationId}/message/{messageId}/file/{fileName}", + FileStorageUrl = file, + FileName = fileName, + FileType = fileType, + ContentType = contentType + }; + files.Add(model); + } + } + + return files; + } + + public string GetMessageFile(string conversationId, string messageId, string fileName) + { + var dir = GetConversationFileDirectory(conversationId, messageId); + if (!ExistDirectory(dir)) + { + return string.Empty; + } + + var found = Directory.GetFiles(dir).FirstOrDefault(f => Path.GetFileNameWithoutExtension(f).IsEqualTo(fileName)); + return found; + } + + public bool SaveMessageFiles(string conversationId, string messageId, List files) + { + if (files.IsNullOrEmpty()) return false; + + var dir = GetConversationFileDirectory(conversationId, messageId, createNewDir: true); + if (!ExistDirectory(dir)) return false; + + try + { + for (int i = 0; i < files.Count; i++) + { + var file = files[i]; + if (string.IsNullOrEmpty(file.FileData)) + { + continue; + } + + var (_, bytes) = GetFileInfoFromData(file.FileData); + var fileType = Path.GetExtension(file.FileName); + var fileName = $"{i + 1}{fileType}"; + Thread.Sleep(100); + File.WriteAllBytes(Path.Combine(dir, fileName), bytes); + } + return true; + } + catch (Exception ex) + { + _logger.LogWarning($"Error when saving conversation files: {ex.Message}"); + return false; + } + } + + + + public bool DeleteMessageFiles(string conversationId, IEnumerable messageIds, string targetMessageId, string? newMessageId = null) + { + if (string.IsNullOrEmpty(conversationId) || messageIds == null) return false; + + if (!string.IsNullOrEmpty(targetMessageId) && !string.IsNullOrEmpty(newMessageId)) + { + var prevDir = GetConversationFileDirectory(conversationId, targetMessageId); + var newDir = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, FILE_FOLDER, newMessageId); + + if (ExistDirectory(prevDir)) + { + if (ExistDirectory(newDir)) + { + Directory.Delete(newDir, true); + } + + Directory.Move(prevDir, newDir); + } + } + + foreach (var messageId in messageIds) + { + var dir = GetConversationFileDirectory(conversationId, messageId); + if (string.IsNullOrEmpty(dir)) continue; + + Thread.Sleep(100); + Directory.Delete(dir, true); + } + + return true; + } + + public bool DeleteConversationFiles(IEnumerable conversationIds) + { + if (conversationIds.IsNullOrEmpty()) return false; + + foreach (var conversationId in conversationIds) + { + var convDir = FindConversationDirectory(conversationId); + if (!ExistDirectory(convDir)) continue; + + Directory.Delete(convDir, true); + } + return true; + } + + #region Private methods + private string GetConversationFileDirectory(string? conversationId, string? messageId, bool createNewDir = false) + { + if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId)) + { + return string.Empty; + } + + var dir = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, FILE_FOLDER, messageId); + if (!Directory.Exists(dir) && createNewDir) + { + Directory.CreateDirectory(dir); + } + return dir; + } + + private string? FindConversationDirectory(string conversationId) + { + if (string.IsNullOrEmpty(conversationId)) return null; + + var dir = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId); + return dir; + } + #endregion +} diff --git a/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.User.cs b/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.User.cs new file mode 100644 index 00000000..b6a87993 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.User.cs @@ -0,0 +1,65 @@ +using System.IO; + +namespace BotSharp.Core.Files; + +public partial class BotSharpFileService +{ + public string GetUserAvatar() + { + var db = _services.GetRequiredService(); + var user = db.GetUserById(_user.Id); + var dir = GetUserAvatarDir(user?.Id); + + if (!ExistDirectory(dir)) return string.Empty; + + var found = Directory.GetFiles(dir).FirstOrDefault() ?? string.Empty; + return found; + } + + public bool SaveUserAvatar(BotSharpFile file) + { + if (file == null || string.IsNullOrEmpty(file.FileData)) return false; + + try + { + var db = _services.GetRequiredService(); + var user = db.GetUserById(_user.Id); + var dir = GetUserAvatarDir(user?.Id); + + if (string.IsNullOrEmpty(dir)) return false; + + if (Directory.Exists(dir)) + { + Directory.Delete(dir, true); + } + + dir = GetUserAvatarDir(user?.Id, createNewDir: true); + var (_, bytes) = GetFileInfoFromData(file.FileData); + File.WriteAllBytes(Path.Combine(dir, file.FileName), bytes); + return true; + } + catch (Exception ex) + { + _logger.LogWarning($"Error when saving user avatar: {ex.Message}"); + return false; + } + } + + + #region Private methods + private string GetUserAvatarDir(string? userId, bool createNewDir = false) + { + if (string.IsNullOrEmpty(userId)) + { + return string.Empty; + } + + var dir = Path.Combine(_baseDir, USERS_FOLDER, userId, USER_AVATAR_FOLDER); + if (!Directory.Exists(dir) && createNewDir) + { + Directory.CreateDirectory(dir); + } + return dir; + } + #endregion +} diff --git a/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs b/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs index 178790bc..76d26dbc 100644 --- a/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs +++ b/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs @@ -5,7 +5,7 @@ using System.Threading; namespace BotSharp.Core.Files; -public class BotSharpFileService : IBotSharpFileService +public partial class BotSharpFileService : IBotSharpFileService { private readonly BotSharpDatabaseSettings _dbSettings; private readonly IServiceProvider _services; @@ -45,200 +45,6 @@ public class BotSharpFileService : IBotSharpFileService return dir; } - public IEnumerable GetChatImages(string conversationId, List conversations, int offset = 1) - { - var files = new List(); - if (string.IsNullOrEmpty(conversationId) || conversations.IsNullOrEmpty()) - { - return files; - } - - if (offset <= 0) - { - offset = MIN_OFFSET; - } - else if (offset > MAX_OFFSET) - { - offset = MAX_OFFSET; - } - - var messageIds = conversations.Select(x => x.MessageId).Distinct().TakeLast(offset).ToList(); - files = GetMessageFiles(conversationId, messageIds, imageOnly: true).ToList(); - return files; - } - - public IEnumerable GetMessageFiles(string conversationId, IEnumerable messageIds, bool imageOnly = false) - { - var files = new List(); - if (messageIds.IsNullOrEmpty()) return files; - - foreach (var messageId in messageIds) - { - var dir = GetConversationFileDirectory(conversationId, messageId); - if (!ExistDirectory(dir)) - { - continue; - } - - foreach (var file in Directory.GetFiles(dir)) - { - var contentType = GetFileContentType(file); - if (imageOnly && !_allowedTypes.Contains(contentType)) - { - continue; - } - - var fileName = Path.GetFileNameWithoutExtension(file); - var extension = Path.GetExtension(file); - var fileType = extension.Substring(1); - - var model = new MessageFileModel() - { - MessageId = messageId, - FileUrl = $"/conversation/{conversationId}/message/{messageId}/file/{fileName}", - FileStorageUrl = file, - FileName = fileName, - FileType = fileType, - ContentType = contentType - }; - files.Add(model); - } - } - - return files; - } - - public string GetMessageFile(string conversationId, string messageId, string fileName) - { - var dir = GetConversationFileDirectory(conversationId, messageId); - if (!ExistDirectory(dir)) - { - return string.Empty; - } - - var found = Directory.GetFiles(dir).FirstOrDefault(f => Path.GetFileNameWithoutExtension(f).IsEqualTo(fileName)); - return found; - } - - public bool SaveMessageFiles(string conversationId, string messageId, List files) - { - if (files.IsNullOrEmpty()) return false; - - var dir = GetConversationFileDirectory(conversationId, messageId, createNewDir: true); - if (!ExistDirectory(dir)) return false; - - try - { - for (int i = 0; i < files.Count; i++) - { - var file = files[i]; - if (string.IsNullOrEmpty(file.FileData)) - { - continue; - } - - var (_, bytes) = GetFileInfoFromData(file.FileData); - var fileType = Path.GetExtension(file.FileName); - var fileName = $"{i + 1}{fileType}"; - Thread.Sleep(100); - File.WriteAllBytes(Path.Combine(dir, fileName), bytes); - } - return true; - } - catch (Exception ex) - { - _logger.LogWarning($"Error when saving conversation files: {ex.Message}"); - return false; - } - } - - public string GetUserAvatar() - { - var db = _services.GetRequiredService(); - var user = db.GetUserById(_user.Id); - var dir = GetUserAvatarDir(user?.Id); - - if (!ExistDirectory(dir)) return string.Empty; - - var found = Directory.GetFiles(dir).FirstOrDefault() ?? string.Empty; - return found; - } - - public bool SaveUserAvatar(BotSharpFile file) - { - if (file == null || string.IsNullOrEmpty(file.FileData)) return false; - - try - { - var db = _services.GetRequiredService(); - var user = db.GetUserById(_user.Id); - var dir = GetUserAvatarDir(user?.Id); - - if (string.IsNullOrEmpty(dir)) return false; - - if (Directory.Exists(dir)) - { - Directory.Delete(dir, true); - } - - dir = GetUserAvatarDir(user?.Id, createNewDir: true); - var (_, bytes) = GetFileInfoFromData(file.FileData); - File.WriteAllBytes(Path.Combine(dir, file.FileName), bytes); - return true; - } - catch (Exception ex) - { - _logger.LogWarning($"Error when saving user avatar: {ex.Message}"); - return false; - } - } - - public bool DeleteMessageFiles(string conversationId, IEnumerable messageIds, string targetMessageId, string? newMessageId = null) - { - if (string.IsNullOrEmpty(conversationId) || messageIds == null) return false; - - if (!string.IsNullOrEmpty(targetMessageId) && !string.IsNullOrEmpty(newMessageId)) - { - var prevDir = GetConversationFileDirectory(conversationId, targetMessageId); - var newDir = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, FILE_FOLDER, newMessageId); - - if (ExistDirectory(prevDir)) - { - if (ExistDirectory(newDir)) - { - Directory.Delete(newDir, true); - } - - Directory.Move(prevDir, newDir); - } - } - - foreach ( var messageId in messageIds) - { - var dir = GetConversationFileDirectory(conversationId, messageId); - if (string.IsNullOrEmpty(dir)) continue; - - Thread.Sleep(100); - Directory.Delete(dir, true); - } - - return true; - } - - public bool DeleteConversationFiles(IEnumerable conversationIds) - { - if (conversationIds.IsNullOrEmpty()) return false; - - foreach (var conversationId in conversationIds) - { - var convDir = FindConversationDirectory(conversationId); - if (!ExistDirectory(convDir)) continue; - - Directory.Delete(convDir, true); - } - return true; - } - public (string, byte[]) GetFileInfoFromData(string data) { if (string.IsNullOrEmpty(data)) @@ -257,44 +63,6 @@ public class BotSharpFileService : IBotSharpFileService } #region Private methods - private string GetConversationFileDirectory(string? conversationId, string? messageId, bool createNewDir = false) - { - if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId)) - { - return string.Empty; - } - - var dir = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, FILE_FOLDER, messageId); - if (!Directory.Exists(dir) && createNewDir) - { - Directory.CreateDirectory(dir); - } - return dir; - } - - private string? FindConversationDirectory(string conversationId) - { - if (string.IsNullOrEmpty(conversationId)) return null; - - var dir = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId); - return dir; - } - - private string GetUserAvatarDir(string? userId, bool createNewDir = false) - { - if (string.IsNullOrEmpty(userId)) - { - return string.Empty; - } - - var dir = Path.Combine(_baseDir, USERS_FOLDER, userId, USER_AVATAR_FOLDER); - if (!Directory.Exists(dir) && createNewDir) - { - Directory.CreateDirectory(dir); - } - return dir; - } - private string GetFileContentType(string filePath) { string contentType; From d9fd6525dc0eb61185615911a46dc0d62e5f97ff Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Wed, 22 May 2024 11:02:14 -0500 Subject: [PATCH 163/201] Fix SSE response format. --- .../Controllers/ConversationController.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index 80374a6d..fd66b225 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -251,6 +251,8 @@ public class ConversationController : ControllerBase await conv.TruncateConversation(conversationId, input.TruncateMessageId, inputMsg.MessageId); } + var state = _services.GetRequiredService(); + var routing = _services.GetRequiredService(); routing.Context.SetMessageId(conversationId, inputMsg.MessageId); @@ -278,6 +280,7 @@ public class ConversationController : ControllerBase response.RichContent = msg.SecondaryRichContent ?? msg.RichContent; response.Instruction = msg.Instruction; response.Data = msg.Data; + response.States = state.GetStates(); await OnChunkReceived(Response, response); }, @@ -290,6 +293,7 @@ public class ConversationController : ControllerBase MessageId = msg.MessageId, Text = msg.Indication, Function = "indicating", + States = new Dictionary() }; await OnChunkReceived(Response, indicator); }, @@ -299,7 +303,6 @@ public class ConversationController : ControllerBase }); - var state = _services.GetRequiredService(); response.States = state.GetStates(); response.MessageId = inputMsg.MessageId; response.ConversationId = conversationId; @@ -309,7 +312,10 @@ public class ConversationController : ControllerBase private async Task OnChunkReceived(HttpResponse response, ChatResponseModel message) { - var json = JsonSerializer.Serialize(message); + var json = JsonSerializer.Serialize(message, new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + }); var buffer = Encoding.UTF8.GetBytes($"data:{json}\n"); await response.Body.WriteAsync(buffer, 0, buffer.Length); From f584c3190fc723c4c4d0e95b19d71b72b04e09a5 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Thu, 23 May 2024 07:05:36 -0500 Subject: [PATCH 164/201] Fix UserService. --- .../Browsing/Models/BrowserActionResult.cs | 5 +++++ .../Browsing/Models/ElementLocatingArgs.cs | 5 +++++ .../Users/Services/UserService.cs | 2 +- .../PlaywrightWebDriver.LocateElement.cs | 20 ++++++++++++++++--- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionResult.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionResult.cs index a634cccd..b3576b90 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionResult.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/BrowserActionResult.cs @@ -8,4 +8,9 @@ public class BrowserActionResult public string Selector { get; set; } public string Body { get; set; } public bool IsHighlighted { get; set; } + + public override string ToString() + { + return $"{IsSuccess} - {Selector}"; + } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs index 6d6411d8..3b96c760 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs @@ -5,6 +5,9 @@ public class ElementLocatingArgs [JsonPropertyName("match_rule")] public string MatchRule { get; set; } = string.Empty; + [JsonPropertyName("tag")] + public string? Tag { get; set; } = null!; + [JsonPropertyName("text")] public string? Text { get; set; } @@ -20,6 +23,8 @@ public class ElementLocatingArgs [JsonPropertyName("selector")] public string? Selector { get; set; } + public bool Parent { get; set; } + public bool FailIfMultiple { get; set; } /// diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index de4c12c8..4324b6f7 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -77,7 +77,7 @@ public class UserService : IUserService record = db.GetUserByUserName(id); } - User? user = null; + User? user = record; var hooks = _services.GetServices(); if (record == null || record.Source != "internal") { diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs index 13b0464c..df6e0a7d 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs @@ -24,6 +24,12 @@ public partial class PlaywrightWebDriver count = await locator.CountAsync(); } + if (location.Tag != null) + { + locator = page.Locator(location.Tag); + count = await locator.CountAsync(); + } + // try attribute if (!string.IsNullOrEmpty(location.AttributeName)) { @@ -66,12 +72,20 @@ public partial class PlaywrightWebDriver } else if (count == 1) { + if (location.Parent) + { + locator = locator.Locator(".."); + } + result.Selector = locator.ToString().Split('@').Last(); // Make sure the element is visible - await locator.EvaluateAsync("element => element.style.height = ''"); - await locator.EvaluateAsync("element => element.style.width = ''"); - await locator.EvaluateAsync("element => element.style.opacity = ''"); + /*if (!await locator.IsVisibleAsync()) + { + await locator.EvaluateAsync("element => element.style.height = '15px'"); + await locator.EvaluateAsync("element => element.style.width = '15px'"); + await locator.EvaluateAsync("element => element.style.opacity = '1.0'"); + }*/ var text = await locator.InnerTextAsync(); result.Body = text; From e8656a99c01d88fc4a76456cabc189d5986c7483 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Sat, 25 May 2024 20:34:29 -0500 Subject: [PATCH 165/201] Add new user verification code. --- .../Repositories/IBotSharpRepository.cs | 9 +-- .../Users/IUserService.cs | 2 + .../BotSharp.Abstraction/Users/Models/User.cs | 2 + .../Users/Models/UserActivationModel.cs | 7 +++ .../Users/Settings/AccountSetting.cs | 9 +++ .../BotSharp.Core/BotSharpCoreExtensions.cs | 5 ++ .../Repository/BotSharpDbContext.cs | 15 ----- .../FileRepository/FileRepository.User.cs | 9 +++ .../Users/Services/UserService.cs | 59 ++++++++++++++++++- .../Controllers/UserController.cs | 12 ++++ .../Collections/UserDocument.cs | 7 ++- .../Repository/MongoRepository.User.cs | 10 ++++ .../PlaywrightWebDriver.HttpRequest.cs | 1 + 13 files changed, 125 insertions(+), 22 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Users/Models/UserActivationModel.cs create mode 100644 src/Infrastructure/BotSharp.Abstraction/Users/Settings/AccountSetting.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index a451071e..6adcc8ad 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -17,10 +17,11 @@ public interface IBotSharpRepository #endregion #region User - User? GetUserByEmail(string email); - User? GetUserById(string id); - User? GetUserByUserName(string userName); - void CreateUser(User user); + User? GetUserByEmail(string email) => throw new NotImplementedException(); + User? GetUserById(string id) => throw new NotImplementedException(); + User? GetUserByUserName(string userName) => throw new NotImplementedException(); + void CreateUser(User user) => throw new NotImplementedException(); + void UpdateUserVerified(string userId) => throw new NotImplementedException(); #endregion #region Agent diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs index debf68f4..8f314ffa 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Users.Models; +using BotSharp.OpenAPI.ViewModels.Users; namespace BotSharp.Abstraction.Users; @@ -6,6 +7,7 @@ public interface IUserService { Task GetUser(string id); Task CreateUser(User user); + Task ActiveUser(UserActivationModel model); Task GetToken(string authorization); Task GetMyProfile(); } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs b/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs index 7ca441fe..4e6ca265 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs @@ -14,6 +14,8 @@ public class User public string Source { get; set; } = "internal"; public string? ExternalId { get; set; } public string Role { get; set; } = UserRole.Client; + public string? VerificationCode { get; set; } + public bool Verified { get; set; } public DateTime UpdatedTime { get; set; } = DateTime.UtcNow; public DateTime CreatedTime { get; set; } = DateTime.UtcNow; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/Models/UserActivationModel.cs b/src/Infrastructure/BotSharp.Abstraction/Users/Models/UserActivationModel.cs new file mode 100644 index 00000000..904bd936 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Users/Models/UserActivationModel.cs @@ -0,0 +1,7 @@ +namespace BotSharp.OpenAPI.ViewModels.Users; + +public class UserActivationModel +{ + public string UserName { get; set; } + public string VerificationCode { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/Settings/AccountSetting.cs b/src/Infrastructure/BotSharp.Abstraction/Users/Settings/AccountSetting.cs new file mode 100644 index 00000000..06176ce6 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Users/Settings/AccountSetting.cs @@ -0,0 +1,9 @@ +namespace BotSharp.Abstraction.Users.Settings; + +public class AccountSetting +{ + /// + /// Whether to enable verification code to verify the authenticity of new users + /// + public bool NewUserVerification { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs index 51f3eb1d..2dd2dc7f 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs +++ b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs @@ -5,6 +5,7 @@ using BotSharp.Core.Plugins; using BotSharp.Abstraction.Settings; using BotSharp.Abstraction.Options; using BotSharp.Abstraction.Messaging.JsonConverters; +using BotSharp.Abstraction.Users.Settings; namespace BotSharp.Core; @@ -84,6 +85,10 @@ public static class BotSharpCoreExtensions return settingService.Bind("PluginLoader"); }); + var accountSettings = new AccountSetting(); + config.Bind("Account", accountSettings); + services.AddScoped(x => accountSettings); + var loader = new PluginLoader(services, config, pluginSettings); loader.Load(assembly => { diff --git a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs index 28aac06b..6bba6c2a 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs @@ -1,6 +1,5 @@ using BotSharp.Abstraction.Loggers.Models; using BotSharp.Abstraction.Plugins.Models; -using BotSharp.Abstraction.Repositories.Models; using BotSharp.Abstraction.Tasks.Models; using BotSharp.Abstraction.Users.Models; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -176,20 +175,6 @@ public class BotSharpDbContext : Database, IBotSharpRepository => throw new NotImplementedException(); #endregion - #region User - public User? GetUserByEmail(string email) - => throw new NotImplementedException(); - - public User? GetUserById(string id) - => throw new NotImplementedException(); - - public User? GetUserByUserName(string userName) - => throw new NotImplementedException(); - - public void CreateUser(User user) - => throw new NotImplementedException(); - #endregion - #region Execution Log public void AddExecutionLogs(string conversationId, List logs) { diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs index bc992a08..36f17b85 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs @@ -32,4 +32,13 @@ public partial class FileRepository var path = Path.Combine(dir, "user.json"); File.WriteAllText(path, JsonSerializer.Serialize(user, _options)); } + + public void UpdateUserVerified(string userId) + { + var user = GetUserById(userId); + user.Verified = true; + var dir = Path.Combine(_dbSettings.FileRepository, "users", user.Id); + var path = Path.Combine(dir, "user.json"); + File.WriteAllText(path, JsonSerializer.Serialize(user, _options)); + } } diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index 4324b6f7..ae5220b9 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -1,4 +1,6 @@ using BotSharp.Abstraction.Users.Models; +using BotSharp.Abstraction.Users.Settings; +using BotSharp.OpenAPI.ViewModels.Users; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; using NanoidDotNet; @@ -12,12 +14,17 @@ public class UserService : IUserService private readonly IServiceProvider _services; private readonly IUserIdentity _user; private readonly ILogger _logger; + private readonly AccountSetting _setting; - public UserService(IServiceProvider services, IUserIdentity user, ILogger logger) + public UserService(IServiceProvider services, + IUserIdentity user, + ILogger logger, + AccountSetting setting) { _services = services; _user = user; _logger = logger; + _setting = setting; } public async Task CreateUser(User user) @@ -51,6 +58,12 @@ public class UserService : IUserService record.Salt = Guid.NewGuid().ToString("N"); record.Password = Utilities.HashText(user.Password, record.Salt); + if (_setting.NewUserVerification) + { + record.VerificationCode = Nanoid.Generate(alphabet: "0123456789", size: 6); + record.Verified = false; + } + db.CreateUser(record); _logger.LogWarning($"Created new user account: {record.Id} {record.UserName}"); @@ -120,6 +133,11 @@ public class UserService : IUserService return default; } + if (_setting.NewUserVerification && !record.Verified) + { + return default; + } + #if !DEBUG if (Utilities.HashText(password, record.Salt) != record.Password) { @@ -206,4 +224,43 @@ public class UserService : IUserService var user = db.GetUserById(id); return user; } + + public async Task ActiveUser(UserActivationModel model) + { + var id = model.UserName; + var db = _services.GetRequiredService(); + var record = id.Contains("@") ? db.GetUserByEmail(id) : db.GetUserByUserName(id); + if (record == null) + { + record = db.GetUserByUserName(id); + } + + if (record == null) + { + return default; + } + + if (record.VerificationCode != model.VerificationCode) + { + return default; + } + + if (record.Verified) + { + return default; + } + + db.UpdateUserVerified(record.Id); + + var accessToken = GenerateJwtToken(record); + var jwt = new JwtSecurityTokenHandler().ReadJwtToken(accessToken); + var token = new Token + { + AccessToken = accessToken, + ExpireTime = jwt.Payload.Exp.Value, + TokenType = "Bearer", + Scope = "api" + }; + return token; + } } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs index e4201aa8..317d1fc9 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs @@ -61,6 +61,18 @@ public class UserController : ControllerBase return UserViewModel.FromUser(createdUser); } + [AllowAnonymous] + [HttpPost("/user/activate")] + public async Task> ActivateUser(UserActivationModel model) + { + var token = await _userService.ActiveUser(model); + if (token == null) + { + return Unauthorized(); + } + return Ok(token); + } + [HttpGet("/user/me")] public async Task GetMyUserProfile() { diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs index c21a6e08..c277b9f3 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs @@ -13,7 +13,8 @@ public class UserDocument : MongoBase public string Source { get; set; } = "internal"; public string? ExternalId { get; set; } public string Role { get; set; } - + public string? VerificationCode { get; set; } + public bool Verified { get; set; } public DateTime CreatedTime { get; set; } public DateTime UpdatedTime { get; set; } @@ -30,7 +31,9 @@ public class UserDocument : MongoBase Salt = Salt, Source = Source, ExternalId = ExternalId, - Role = Role + Role = Role, + VerificationCode = VerificationCode, + Verified = Verified, }; } } \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs index 1b6b8b77..1a5211f4 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs @@ -40,10 +40,20 @@ public partial class MongoRepository Source = user.Source, ExternalId = user.ExternalId, Role = user.Role, + VerificationCode = user.VerificationCode, + Verified = user.Verified, CreatedTime = DateTime.UtcNow, UpdatedTime = DateTime.UtcNow }; _dc.Users.InsertOne(userCollection); } + + public void UpdateUserVerified(string userId) + { + var filter = Builders.Filter.Eq(x => x.Id, userId); + var update = Builders.Update.Set(x => x.Verified, true) + .Set(x => x.UpdatedTime, DateTime.UtcNow); + _dc.Users.UpdateOne(filter, update); + } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs index 4a0177a0..a769332b 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.HttpRequest.cs @@ -27,6 +27,7 @@ public partial class PlaywrightWebDriver try { + _logger.LogInformation($"SendHttpRequest: {args.Url}"); var response = await EvaluateScript(message.ContextId, script); result.IsSuccess = true; result.Body = JsonSerializer.Serialize(response); From abf519481022925af6ad499ff394c366dd30d6d2 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Sun, 26 May 2024 20:05:46 -0500 Subject: [PATCH 166/201] fix api controller json serilizer --- .../Controllers/ConversationController.cs | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index fd66b225..6ed27b32 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.Options; using BotSharp.Abstraction.Routing; namespace BotSharp.OpenAPI.Controllers; @@ -8,12 +9,16 @@ public class ConversationController : ControllerBase { private readonly IServiceProvider _services; private readonly IUserIdentity _user; + private readonly JsonSerializerOptions _jsonOptions; public ConversationController(IServiceProvider services, - IUserIdentity user) + IUserIdentity user, + BotSharpOptions options) { _services = services; _user = user; + _jsonOptions = InitJsonOptions(options); + } [HttpPost("/conversation/{agentId}")] @@ -312,10 +317,7 @@ public class ConversationController : ControllerBase private async Task OnChunkReceived(HttpResponse response, ChatResponseModel message) { - var json = JsonSerializer.Serialize(message, new JsonSerializerOptions - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - }); + var json = JsonSerializer.Serialize(message, _jsonOptions); var buffer = Encoding.UTF8.GetBytes($"data:{json}\n"); await response.Body.WriteAsync(buffer, 0, buffer.Length); @@ -333,4 +335,24 @@ public class ConversationController : ControllerBase buffer = Encoding.UTF8.GetBytes("\n"); await response.Body.WriteAsync(buffer, 0, buffer.Length); } + + private JsonSerializerOptions InitJsonOptions(BotSharpOptions options) + { + var jsonOption = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + AllowTrailingCommas = true + }; + + if (options?.JsonSerializerOptions != null) + { + foreach (var option in options.JsonSerializerOptions.Converters) + { + jsonOption.Converters.Add(option); + } + } + + return jsonOption; + } } From f355aa3f319c91741977f85b7e135786b6e7d1de Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Sun, 26 May 2024 20:08:53 -0500 Subject: [PATCH 167/201] move google api setting --- .../BotSharp.OpenAPI/BotSharpOpenApiExtensions.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Infrastructure/BotSharp.OpenAPI/BotSharpOpenApiExtensions.cs b/src/Infrastructure/BotSharp.OpenAPI/BotSharpOpenApiExtensions.cs index eabdba84..a1a6dad2 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/BotSharpOpenApiExtensions.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/BotSharpOpenApiExtensions.cs @@ -11,8 +11,6 @@ using Microsoft.Net.Http.Headers; using Microsoft.OpenApi.Models; using Microsoft.IdentityModel.JsonWebTokens; using BotSharp.OpenAPI.BackgroundServices; -using BotSharp.Abstraction.Settings; -using BotSharp.Abstraction.Google.Settings; namespace BotSharp.OpenAPI; @@ -34,12 +32,6 @@ public static class BotSharpOpenApiExtensions services.AddScoped(); services.AddHostedService(); - services.AddScoped(provider => - { - var settingService = provider.GetRequiredService(); - return settingService.Bind("GoogleApi"); - }); - // Add bearer authentication var schema = "MIXED_SCHEME"; var builder = services.AddAuthentication(options => From c49fcc5dcf2b477d1506a6f1f5dc083b911e1a31 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Sun, 26 May 2024 20:09:18 -0500 Subject: [PATCH 168/201] minor change --- .../BotSharp.Core/Conversations/ConversationPlugin.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs index 6887622b..90eb3298 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Files; +using BotSharp.Abstraction.Google.Settings; using BotSharp.Abstraction.Instructs; using BotSharp.Abstraction.Messaging; using BotSharp.Abstraction.Plugins.Models; @@ -34,6 +35,12 @@ public class ConversationPlugin : IBotSharpPlugin return settingService.Bind("Conversation"); }); + services.AddScoped(provider => + { + var settingService = provider.GetRequiredService(); + return settingService.Bind("GoogleApi"); + }); + services.AddScoped(); services.AddScoped(); services.AddScoped(); From 2a456df8465140f1c4026c4a795deb4c3cfc8b65 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Sun, 26 May 2024 20:40:24 -0500 Subject: [PATCH 169/201] temp save --- .../Conversations/IConversationService.cs | 2 ++ .../BotSharp.Core/BotSharp.Core.csproj | 6 +++- .../Services/ConversationService.Summary.cs | 32 +++++++++++++++++++ .../templates/conversation.summary.liquid | 6 ++++ .../Controllers/ConversationController.cs | 7 ++++ 5 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs create mode 100644 src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs index 9df6ae7f..18cee8fc 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs @@ -53,4 +53,6 @@ public interface IConversationService /// Append user init words /// Task UpdateBreakpoint(bool resetStates = false, string? reason = null); + + Task GetConversationSummary(string conversationId); } diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index 0ddc52e8..10e21b3c 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 @@ -56,6 +56,7 @@ + @@ -142,6 +143,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs new file mode 100644 index 00000000..53c6470a --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs @@ -0,0 +1,32 @@ +using BotSharp.Abstraction.Infrastructures.Enums; +using BotSharp.Abstraction.Templating; + +namespace BotSharp.Core.Conversations.Services; + +public partial class ConversationService +{ + public async Task GetConversationSummary(string conversationId) + { + if (string.IsNullOrEmpty(conversationId)) return string.Empty; + + var dialogs = _storage.GetDialogs(conversationId); + + return string.Empty; + } + + private IEnumerable BuildConversationContent(List dialogs) + { + + } + + private string GetPrompt(Agent router, List dialogs) + { + var template = router.Templates.First(x => x.Name == "conversation.summary").Content; + + var render = _services.GetRequiredService(); + return render.Render(template, new Dictionary + { + { "conversation", } + }); + } +} diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid new file mode 100644 index 00000000..3b6bb53f --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid @@ -0,0 +1,6 @@ +Please follow these steps to summarize the conversation: +1. Read the [CONVERSATION] content. +2. Summarize the conversation in one sentence. + +[CONVERSATION] +{{ conversation }} \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index fd66b225..ef44472a 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -134,6 +134,13 @@ public class ConversationController : ControllerBase return result; } + [HttpGet("/conversation/{conversationId}/summary")] + public async Task GetConversationSummary([FromRoute] string conversationId) + { + var service = _services.GetRequiredService(); + return await service.GetConversationSummary(conversationId); + } + [HttpGet("/conversation/{conversationId}/user")] public async Task GetConversationUser([FromRoute] string conversationId) { From 6b5d77604e635b614f836106817c25c4d3957f8c Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Sun, 26 May 2024 21:30:57 -0500 Subject: [PATCH 170/201] add conversation summary --- .../Services/ConversationService.Summary.cs | 54 ++++++++++++++----- .../Services/ConversationService.cs | 2 + .../templates/conversation.summary.liquid | 7 +-- 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs index 53c6470a..00924b93 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs @@ -1,4 +1,4 @@ -using BotSharp.Abstraction.Infrastructures.Enums; +using BotSharp.Abstraction.Conversations.Enums; using BotSharp.Abstraction.Templating; namespace BotSharp.Core.Conversations.Services; @@ -9,24 +9,52 @@ public partial class ConversationService { if (string.IsNullOrEmpty(conversationId)) return string.Empty; + var routing = _services.GetRequiredService(); + var agentService = _services.GetRequiredService(); + var dialogs = _storage.GetDialogs(conversationId); + if (dialogs.IsNullOrEmpty()) return string.Empty; - return string.Empty; + var router = await agentService.LoadAgent(AIAssistant); + var content = await routing.GetConversationContent(dialogs); + var prompt = GetPrompt(router, content); + var summary = await Summarize(router, prompt, dialogs); + + return summary; } - private IEnumerable BuildConversationContent(List dialogs) + private string GetPrompt(Agent agent, string content) { - - } - - private string GetPrompt(Agent router, List dialogs) - { - var template = router.Templates.First(x => x.Name == "conversation.summary").Content; - + var template = agent.Templates.First(x => x.Name == "conversation.summary").Content; var render = _services.GetRequiredService(); - return render.Render(template, new Dictionary + return render.Render(template, new Dictionary { }); + } + + private async Task Summarize(Agent agent, string prompt, List dialogs) + { + var provider = agent.LlmConfig.Provider; + var model = agent.LlmConfig.Model; + + if (provider == null || model == null) { - { "conversation", } - }); + var agentSettings = _services.GetRequiredService(); + provider = agentSettings.LlmConfig.Provider; + model = agentSettings.LlmConfig.Model; + } + + var chatCompletion = CompletionProvider.GetChatCompletion(_services, provider: provider, model: model); + var response = await chatCompletion.GetChatCompletions(new Agent + { + Id = agent.Id, + Name = agent.Name, + Instruction = prompt + }, dialogs); + + return response.Content; + } + + private void SaveState(string summary) + { + _state.SetState("conversation_summary", summary, source: StateSource.Application); } } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs index 4de36b9d..8e113226 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs @@ -12,6 +12,8 @@ public partial class ConversationService : IConversationService private readonly IConversationStorage _storage; private readonly IConversationStateService _state; private string _conversationId; + private const string AIAssistant = "01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a"; + public string ConversationId => _conversationId; public IConversationStateService States => _state; diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid index 3b6bb53f..a935e497 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid @@ -1,6 +1 @@ -Please follow these steps to summarize the conversation: -1. Read the [CONVERSATION] content. -2. Summarize the conversation in one sentence. - -[CONVERSATION] -{{ conversation }} \ No newline at end of file +Please summarize the conversation. \ No newline at end of file From 953a53c4bc00eb798b48efb418bb39d6a9ea65aa Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Sun, 26 May 2024 22:19:06 -0500 Subject: [PATCH 171/201] clean code --- .../Conversations/Services/ConversationService.Summary.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs index 00924b93..252d1e7f 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Conversations.Enums; using BotSharp.Abstraction.Templating; namespace BotSharp.Core.Conversations.Services; @@ -52,9 +51,4 @@ public partial class ConversationService return response.Content; } - - private void SaveState(string summary) - { - _state.SetState("conversation_summary", summary, source: StateSource.Application); - } } From 6c5be6da631595e83338873ff602d1edd6badad5 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Sun, 26 May 2024 22:20:27 -0500 Subject: [PATCH 172/201] clean code --- .../Conversations/Services/ConversationService.Summary.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs index 252d1e7f..d76678cc 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs @@ -15,14 +15,13 @@ public partial class ConversationService if (dialogs.IsNullOrEmpty()) return string.Empty; var router = await agentService.LoadAgent(AIAssistant); - var content = await routing.GetConversationContent(dialogs); - var prompt = GetPrompt(router, content); + var prompt = GetPrompt(router); var summary = await Summarize(router, prompt, dialogs); return summary; } - private string GetPrompt(Agent agent, string content) + private string GetPrompt(Agent agent) { var template = agent.Templates.First(x => x.Name == "conversation.summary").Content; var render = _services.GetRequiredService(); From b8becca94542c0fabb384df4712f0b72551b67d7 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Sun, 26 May 2024 22:20:56 -0500 Subject: [PATCH 173/201] minor change --- .../Conversations/Services/ConversationService.Summary.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs index d76678cc..4a547204 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs @@ -30,8 +30,8 @@ public partial class ConversationService private async Task Summarize(Agent agent, string prompt, List dialogs) { - var provider = agent.LlmConfig.Provider; - var model = agent.LlmConfig.Model; + var provider = agent?.LlmConfig?.Provider; + var model = agent?.LlmConfig?.Model; if (provider == null || model == null) { From 245384e8d253fd014ed44d273be9aef7c6830cf0 Mon Sep 17 00:00:00 2001 From: "LAPTOP-3CFGGVOS\\rabbit" Date: Mon, 27 May 2024 20:44:01 +0800 Subject: [PATCH 174/201] optimize UpdateBreakPoint --- .../Services/ConversationService.UpdateBreakpoint.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.UpdateBreakpoint.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.UpdateBreakpoint.cs index 7453a202..1bee7b89 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.UpdateBreakpoint.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.UpdateBreakpoint.cs @@ -4,7 +4,7 @@ namespace BotSharp.Core.Conversations.Services; public partial class ConversationService : IConversationService { - public async Task UpdateBreakpoint(bool resetStates = false, string? reason = null) + public async Task UpdateBreakpoint(bool resetStates = false, string? reason = null, params string[] excludedStates) { var db = _services.GetRequiredService(); var routingCtx = _services.GetRequiredService(); @@ -22,7 +22,8 @@ public partial class ConversationService : IConversationService { var states = _services.GetRequiredService(); // keep language state - states.CleanStates(StateConst.LANGUAGE); + if(excludedStates.IsNullOrEmpty()) excludedStates = new string[] { StateConst.LANGUAGE }; + states.CleanStates(excludedStates); } var hooks = _services.GetServices() From ad7b3a41eb43212da1b5dd8abb91f637ee16ea86 Mon Sep 17 00:00:00 2001 From: "LAPTOP-3CFGGVOS\\rabbit" Date: Mon, 27 May 2024 21:17:50 +0800 Subject: [PATCH 175/201] optimize UpdateBreakpoint --- .../Conversations/IConversationService.cs | 3 ++- .../Services/ConversationService.UpdateBreakpoint.cs | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs index 9df6ae7f..055db3cc 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs @@ -51,6 +51,7 @@ public interface IConversationService /// /// Whether to reset all states /// Append user init words + /// /// - Task UpdateBreakpoint(bool resetStates = false, string? reason = null); + Task UpdateBreakpoint(bool resetStates = false, string? reason = null, params string[] excludedStates); } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.UpdateBreakpoint.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.UpdateBreakpoint.cs index 1bee7b89..8f88f44f 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.UpdateBreakpoint.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.UpdateBreakpoint.cs @@ -22,7 +22,12 @@ public partial class ConversationService : IConversationService { var states = _services.GetRequiredService(); // keep language state - if(excludedStates.IsNullOrEmpty()) excludedStates = new string[] { StateConst.LANGUAGE }; + if (excludedStates == null) excludedStates = new string[] { }; + if (!excludedStates.Contains(StateConst.LANGUAGE)) + { + excludedStates = excludedStates.Append(StateConst.LANGUAGE).ToArray(); + } + states.CleanStates(excludedStates); } From a5dad4dee48b54b369299b205b8e329d6b79a5b5 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Mon, 27 May 2024 12:12:39 -0500 Subject: [PATCH 176/201] refine summary prompt --- .../Services/ConversationService.Summary.cs | 28 ++++++++++++++----- .../templates/conversation.summary.liquid | 9 +++++- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs index 4a547204..25d754d6 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.MLTasks; using BotSharp.Abstraction.Templating; namespace BotSharp.Core.Conversations.Services; @@ -30,17 +31,30 @@ public partial class ConversationService private async Task Summarize(Agent agent, string prompt, List dialogs) { - var provider = agent?.LlmConfig?.Provider; - var model = agent?.LlmConfig?.Model; + var provider = "openai"; + string? model; - if (provider == null || model == null) + var providerService = _services.GetRequiredService(); + var modelSettings = providerService.GetProviderModels(provider); + var modelSetting = modelSettings.FirstOrDefault(x => x.Name.IsEqualTo("gpt4-turbo") || x.Name.IsEqualTo("gpt-4o")); + + if (modelSetting != null) { - var agentSettings = _services.GetRequiredService(); - provider = agentSettings.LlmConfig.Provider; - model = agentSettings.LlmConfig.Model; + model = modelSetting.Name; + } + else + { + provider = agent?.LlmConfig?.Provider; + model = agent?.LlmConfig?.Model; + if (provider == null || model == null) + { + var agentSettings = _services.GetRequiredService(); + provider = agentSettings.LlmConfig.Provider; + model = agentSettings.LlmConfig.Model; + } } - var chatCompletion = CompletionProvider.GetChatCompletion(_services, provider: provider, model: model); + var chatCompletion = CompletionProvider.GetChatCompletion(_services, provider, model); var response = await chatCompletion.GetChatCompletions(new Agent { Id = agent.Id, diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid index a935e497..08792af3 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid @@ -1 +1,8 @@ -Please summarize the conversation. \ No newline at end of file +Please summarize the conversation. + +*** Super Important! Please consider the entire conversation. Do not only consider the recent sentences. *** +** Please do not respond to the latest conversation. +** If there are different topics in the conversation, please summarize each topic in different sentences and list them in bullets. +* Please use concise sentences to summarize each topic. +* Please do not include excessive details in the summaries. +* Please use 'user' instead of 'you', 'he' or 'she'. \ No newline at end of file From 520b91507c4e5964aab0c5a2bf7abee157061608 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Mon, 27 May 2024 12:51:00 -0500 Subject: [PATCH 177/201] minor change --- .../templates/conversation.summary.liquid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid index 08792af3..e2ffe5db 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid @@ -2,7 +2,7 @@ Please summarize the conversation. *** Super Important! Please consider the entire conversation. Do not only consider the recent sentences. *** ** Please do not respond to the latest conversation. -** If there are different topics in the conversation, please summarize each topic in different sentences and list them in bullets. +** If there are different topics in the conversation, please summarize each topic in different sentences and list them with bullets. * Please use concise sentences to summarize each topic. * Please do not include excessive details in the summaries. * Please use 'user' instead of 'you', 'he' or 'she'. \ No newline at end of file From c3c28e282b745dc5a88180389e06135d20affccc Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Mon, 27 May 2024 19:26:58 -0500 Subject: [PATCH 178/201] Return Instruction for SSE. --- .../BotSharp.OpenAPI/Controllers/ConversationController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index 6ed27b32..71d824b3 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -298,6 +298,7 @@ public class ConversationController : ControllerBase MessageId = msg.MessageId, Text = msg.Indication, Function = "indicating", + Instruction = msg.Instruction, States = new Dictionary() }; await OnChunkReceived(Response, indicator); From d6ac3c09ea47ccae8298cfc2dded62d135e56e47 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Mon, 27 May 2024 21:06:05 -0500 Subject: [PATCH 179/201] Fix current agent id for routing rule. --- src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs index 6b126a37..31caa932 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs @@ -118,6 +118,7 @@ public class RoutingContext : IRoutingContext var message = new RoleDialogModel(AgentRole.User, $"Try to route to agent {agent.Name}") { + CurrentAgentId = currentAgentId, FunctionName = "route_to_agent", FunctionArgs = JsonSerializer.Serialize(new FunctionCallFromLlm { From 0ffabd35aa09364697536979618b2ed87d669a15 Mon Sep 17 00:00:00 2001 From: YouWeiDH Date: Tue, 28 May 2024 19:47:48 +0800 Subject: [PATCH 180/201] hdong: #65. --- .../Users/IUserService.cs | 2 ++ .../Users/Services/UserService.cs | 26 +++++++++++++++++++ .../Controllers/UserController.cs | 14 ++++++++++ 3 files changed, 42 insertions(+) diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs index 8f314ffa..4071790c 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs @@ -10,4 +10,6 @@ public interface IUserService Task ActiveUser(UserActivationModel model); Task GetToken(string authorization); Task GetMyProfile(); + Task VerifyUserUnique(string userName); + Task VerifyEmailUnique(string email); } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index ae5220b9..19242e39 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -263,4 +263,30 @@ public class UserService : IUserService }; return token; } + + public async Task VerifyUserUnique(string userName) + { + if (string.IsNullOrEmpty(userName)) + return false; + + var db = _services.GetRequiredService(); + var user = db.GetUserByUserName(userName); + if (user == null) + return true; + + return false; + } + + public async Task VerifyEmailUnique(string email) + { + if (string.IsNullOrEmpty(email)) + return false; + + var db = _services.GetRequiredService(); + var emailName = db.GetUserByEmail(email); + if (emailName == null) + return true; + + return false; + } } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs index 317d1fc9..8228e594 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs @@ -94,4 +94,18 @@ public class UserController : ControllerBase } return UserViewModel.FromUser(user); } + + [AllowAnonymous] + [HttpPost("/user/unique/{username}")] + public async Task VerifyUserUnique([FromRoute] string userName) + { + return await _userService.VerifyUserUnique(userName); + } + + [AllowAnonymous] + [HttpPost("/email/unique/{email}")] + public async Task VerifyEmailUnique([FromRoute]string email) + { + return await _userService.VerifyEmailUnique(email); + } } From b4e7c1ac48fe60f1833a3519bfa5ae6514196352 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Tue, 28 May 2024 10:45:13 -0500 Subject: [PATCH 181/201] Upgrade library, release v1.5-sse --- Directory.Build.props | 2 +- .../BotSharp.OpenAPI/BotSharp.OpenAPI.csproj | 8 ++++---- .../BotSharp.Plugin.MongoStorage.csproj | 2 +- .../BotSharp.Plugin.PaddleSharp.csproj | 8 ++++---- .../BotSharp.Plugin.Qdrant.csproj | 2 +- .../BotSharp.Plugin.SemanticKernel.csproj | 6 +++--- .../SemanticKernelMemoryStoreProvider.cs | 12 ++++++------ .../BotSharp.Plugin.SqlDriver.csproj | 2 +- .../BotSharp.Plugin.Twilio.csproj | 2 +- .../BotSharp.Plugin.WeChat.csproj | 2 +- .../BotSharp.Plugin.WebDriver.csproj | 4 ++-- src/WebStarter/appsettings.json | 1 + .../SemanticKernelPluginTests.cs | 4 ++-- 13 files changed, 28 insertions(+), 27 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 068b6e13..4ca87cf3 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,7 +2,7 @@ net8.0 10.0 - 1.4.0 + 1.5.1 true false diff --git a/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj b/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj index 22fcc9e0..8f0e9fcc 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj +++ b/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj @@ -18,16 +18,16 @@ - + - + - - + + diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/BotSharp.Plugin.MongoStorage.csproj b/src/Plugins/BotSharp.Plugin.MongoStorage/BotSharp.Plugin.MongoStorage.csproj index 41c63ad9..9b1d2178 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/BotSharp.Plugin.MongoStorage.csproj +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/BotSharp.Plugin.MongoStorage.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/Plugins/BotSharp.Plugin.PaddleSharp/BotSharp.Plugin.PaddleSharp.csproj b/src/Plugins/BotSharp.Plugin.PaddleSharp/BotSharp.Plugin.PaddleSharp.csproj index 6dcaf95e..ef637413 100644 --- a/src/Plugins/BotSharp.Plugin.PaddleSharp/BotSharp.Plugin.PaddleSharp.csproj +++ b/src/Plugins/BotSharp.Plugin.PaddleSharp/BotSharp.Plugin.PaddleSharp.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 @@ -12,14 +12,14 @@ - - + + - + diff --git a/src/Plugins/BotSharp.Plugin.Qdrant/BotSharp.Plugin.Qdrant.csproj b/src/Plugins/BotSharp.Plugin.Qdrant/BotSharp.Plugin.Qdrant.csproj index 693c7885..bdcd40d5 100644 --- a/src/Plugins/BotSharp.Plugin.Qdrant/BotSharp.Plugin.Qdrant.csproj +++ b/src/Plugins/BotSharp.Plugin.Qdrant/BotSharp.Plugin.Qdrant.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/Plugins/BotSharp.Plugin.SemanticKernel/BotSharp.Plugin.SemanticKernel.csproj b/src/Plugins/BotSharp.Plugin.SemanticKernel/BotSharp.Plugin.SemanticKernel.csproj index 9d790284..c4051422 100644 --- a/src/Plugins/BotSharp.Plugin.SemanticKernel/BotSharp.Plugin.SemanticKernel.csproj +++ b/src/Plugins/BotSharp.Plugin.SemanticKernel/BotSharp.Plugin.SemanticKernel.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 @@ -11,8 +11,8 @@ - - + + diff --git a/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelMemoryStoreProvider.cs b/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelMemoryStoreProvider.cs index 444cab1e..7df2c0cd 100644 --- a/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelMemoryStoreProvider.cs +++ b/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelMemoryStoreProvider.cs @@ -9,13 +9,13 @@ namespace BotSharp.Plugin.SemanticKernel { internal class SemanticKernelMemoryStoreProvider : IVectorDb { -#pragma warning disable SKEXP0003 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +#pragma warning disable SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. private readonly IMemoryStore _memoryStore; -#pragma warning restore SKEXP0003 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +#pragma warning restore SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. -#pragma warning disable SKEXP0003 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +#pragma warning disable SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. public SemanticKernelMemoryStoreProvider(IMemoryStore memoryStore) -#pragma warning restore SKEXP0003 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +#pragma warning restore SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. { this._memoryStore = memoryStore; } @@ -50,9 +50,9 @@ namespace BotSharp.Plugin.SemanticKernel public async Task Upsert(string collectionName, int id, float[] vector, string text) { -#pragma warning disable SKEXP0003 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +#pragma warning disable SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. await _memoryStore.UpsertAsync(collectionName, MemoryRecord.LocalRecord(id.ToString(), text, null, vector)); -#pragma warning restore SKEXP0003 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +#pragma warning restore SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. } } } diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj b/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj index 383a9f4e..afb53ae8 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj @@ -33,7 +33,7 @@ - + diff --git a/src/Plugins/BotSharp.Plugin.Twilio/BotSharp.Plugin.Twilio.csproj b/src/Plugins/BotSharp.Plugin.Twilio/BotSharp.Plugin.Twilio.csproj index c8f1a6f5..ed99a926 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/BotSharp.Plugin.Twilio.csproj +++ b/src/Plugins/BotSharp.Plugin.Twilio/BotSharp.Plugin.Twilio.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Plugins/BotSharp.Plugin.WeChat/BotSharp.Plugin.WeChat.csproj b/src/Plugins/BotSharp.Plugin.WeChat/BotSharp.Plugin.WeChat.csproj index b2c19ae1..85c04581 100644 --- a/src/Plugins/BotSharp.Plugin.WeChat/BotSharp.Plugin.WeChat.csproj +++ b/src/Plugins/BotSharp.Plugin.WeChat/BotSharp.Plugin.WeChat.csproj @@ -27,7 +27,7 @@ - + diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj b/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj index b03f585d..96f97705 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj +++ b/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj @@ -17,8 +17,8 @@ - - + + diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index 5482e6b1..84e402dc 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -111,6 +111,7 @@ "DataDir": "agents", "TemplateFormat": "liquid", "HostAgentId": "01e2fc5c-2c89-4ec7-8470-7688608b496c", + "EnableTranslator": false, "LlmConfig": { "Provider": "azure-openai", "Model": "gpt-35-turbo" diff --git a/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelPluginTests.cs b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelPluginTests.cs index 99d41024..62646015 100644 --- a/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelPluginTests.cs +++ b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelPluginTests.cs @@ -22,9 +22,9 @@ namespace BotSharp.Plugin.SemanticKernel.Tests var plugin = new SemanticKernelPlugin(); services.AddScoped(x => Mock.Of()); services.AddScoped(x => Mock.Of()); -#pragma warning disable SKEXP0003 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +#pragma warning disable SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. services.AddScoped(x => Mock.Of()); -#pragma warning restore SKEXP0003 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +#pragma warning restore SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. #pragma warning disable SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. services.AddScoped(x => Mock.Of()); #pragma warning restore SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. From c9428cd965f472edb291282a2a0a1fdf4ddfd42c Mon Sep 17 00:00:00 2001 From: YouWeiDH Date: Wed, 29 May 2024 10:20:38 +0800 Subject: [PATCH 182/201] hdong: clean up code for Pagination size. --- .../BotSharp.Abstraction/Utilities/Pagination.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs index 660c8e26..382bd55d 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs @@ -3,7 +3,7 @@ namespace BotSharp.Abstraction.Utilities; public class Pagination { private int _page; - private int _size => 10; + private int _size; public int Page { @@ -19,11 +19,11 @@ public class Pagination if (_size > 100) return 100; return _size; - } - //set - //{ - // _size = value; - //} + } + set + { + _size = value; + } } /// From 8a9b6f319bde02552990c266da84fcb817c887f0 Mon Sep 17 00:00:00 2001 From: YouWeiDH Date: Wed, 29 May 2024 10:36:27 +0800 Subject: [PATCH 183/201] hdong: remove the limit. --- .../BotSharp.Abstraction/Utilities/Pagination.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs index 382bd55d..512d27c4 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs @@ -13,11 +13,8 @@ public class Pagination public int Size { - get + get { - if (_size <= 0) return 20; - if (_size > 100) return 100; - return _size; } set From 0b56a18aa682ee237b59c7ca7b168dab7757466a Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Tue, 28 May 2024 21:50:16 -0500 Subject: [PATCH 184/201] summarize multiple conversations --- .../Conversations/IConversationService.cs | 2 +- .../Services/ConversationService.Summary.cs | 61 ++++++++++++++++--- .../templates/conversation.summary.liquid | 14 +++-- .../Controllers/ConversationController.cs | 6 +- .../Conversations/ConversationSummaryModel.cs | 9 +++ 5 files changed, 74 insertions(+), 18 deletions(-) create mode 100644 src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationSummaryModel.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs index 2aa8a72f..c8b997ec 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs @@ -55,5 +55,5 @@ public interface IConversationService /// Task UpdateBreakpoint(bool resetStates = false, string? reason = null, params string[] excludedStates); - Task GetConversationSummary(string conversationId); + Task GetConversationSummary(IEnumerable conversationId); } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs index 25d754d6..533020f0 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs @@ -5,31 +5,51 @@ namespace BotSharp.Core.Conversations.Services; public partial class ConversationService { - public async Task GetConversationSummary(string conversationId) + public async Task GetConversationSummary(IEnumerable conversationIds) { - if (string.IsNullOrEmpty(conversationId)) return string.Empty; + if (conversationIds.IsNullOrEmpty()) return string.Empty; var routing = _services.GetRequiredService(); var agentService = _services.GetRequiredService(); - var dialogs = _storage.GetDialogs(conversationId); - if (dialogs.IsNullOrEmpty()) return string.Empty; + var contents = new List(); + foreach ( var conversationId in conversationIds) + { + if (string.IsNullOrEmpty(conversationId)) continue; + + var dialogs = _storage.GetDialogs(conversationId); + + if (dialogs.IsNullOrEmpty()) continue; + + var content = GetConversationContent(dialogs); + contents.Add(content); + } var router = await agentService.LoadAgent(AIAssistant); - var prompt = GetPrompt(router); - var summary = await Summarize(router, prompt, dialogs); + var prompt = GetPrompt(router, contents); + var summary = await Summarize(router, prompt); return summary; } - private string GetPrompt(Agent agent) + private string GetPrompt(Agent agent, List contents) { var template = agent.Templates.First(x => x.Name == "conversation.summary").Content; var render = _services.GetRequiredService(); - return render.Render(template, new Dictionary { }); + + var texts = string.Empty; + for (int i = 0; i < contents.Count; i++) + { + texts += $"[Conversation {i+1}]\r\n{contents[i]}"; + } + + return render.Render(template, new Dictionary + { + { "texts", texts } + }); } - private async Task Summarize(Agent agent, string prompt, List dialogs) + private async Task Summarize(Agent agent, string prompt) { var provider = "openai"; string? model; @@ -60,8 +80,29 @@ public partial class ConversationService Id = agent.Id, Name = agent.Name, Instruction = prompt - }, dialogs); + }, new List + { + new RoleDialogModel(AgentRole.User, "Please summarize the conversations.") + }); return response.Content; } + + private string GetConversationContent(List dialogs, int maxDialogCount = 50) + { + var conversation = ""; + + foreach (var dialog in dialogs.TakeLast(maxDialogCount)) + { + var role = dialog.Role; + if (role != AgentRole.User) + { + role = AgentRole.Assistant; + } + + conversation += $"{role}: {dialog.Payload ?? dialog.Content}\r\n"; + } + + return conversation + "\r\n"; + } } diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid index e2ffe5db..d7ffae60 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid @@ -1,8 +1,14 @@ -Please summarize the conversation. +Please read each conversation in the [CONVERSATION] section and provide a summary. -*** Super Important! Please consider the entire conversation. Do not only consider the recent sentences. *** +*** Super Important! Please consider every conversation. Do not only consider the recent sentences. *** ** Please do not respond to the latest conversation. -** If there are different topics in the conversation, please summarize each topic in different sentences and list them with bullets. +** If there are different topics in the conversations, please summarize each topic in different sentences and list them in bullets. * Please use concise sentences to summarize each topic. * Please do not include excessive details in the summaries. -* Please use 'user' instead of 'you', 'he' or 'she'. \ No newline at end of file +* Please use 'user' instead of 'you', 'he' or 'she'. + +[CONVERSATIONS] + +{% for text in texts -%} +{{ text }}{{ "\r\n\r\n" }} +{%- endfor %} \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index a2c2b3b0..adc37636 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -152,11 +152,11 @@ public class ConversationController : ControllerBase return result; } - [HttpGet("/conversation/{conversationId}/summary")] - public async Task GetConversationSummary([FromRoute] string conversationId) + [HttpPost("/conversation/summary")] + public async Task GetConversationSummary([FromBody] ConversationSummaryModel input) { var service = _services.GetRequiredService(); - return await service.GetConversationSummary(conversationId); + return await service.GetConversationSummary(input.ConversationIds); } [HttpGet("/conversation/{conversationId}/user")] diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationSummaryModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationSummaryModel.cs new file mode 100644 index 00000000..0854ab2a --- /dev/null +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationSummaryModel.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace BotSharp.OpenAPI.ViewModels.Conversations; + +public class ConversationSummaryModel +{ + [JsonPropertyName("conversation_ids")] + public List ConversationIds { get; set; } = new List(); +} From 9fc7f1c3b0a2cbd3d34d6f8ae2ac08b661e9d2f5 Mon Sep 17 00:00:00 2001 From: YouWeiDH Date: Wed, 29 May 2024 10:50:41 +0800 Subject: [PATCH 185/201] hdong:change request method and API name. --- .../BotSharp.OpenAPI/Controllers/UserController.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs index 8228e594..e413724b 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs @@ -95,16 +95,14 @@ public class UserController : ControllerBase return UserViewModel.FromUser(user); } - [AllowAnonymous] - [HttpPost("/user/unique/{username}")] - public async Task VerifyUserUnique([FromRoute] string userName) + [HttpGet("/user/name/existing")] + public async Task VerifyUserUnique([FromQuery] string userName) { return await _userService.VerifyUserUnique(userName); } - [AllowAnonymous] - [HttpPost("/email/unique/{email}")] - public async Task VerifyEmailUnique([FromRoute]string email) + [HttpGet("/user/email/existing")] + public async Task VerifyEmailUnique([FromQuery] string email) { return await _userService.VerifyEmailUnique(email); } From 11fa2513b1303f4e6508a17e6587adf2bca612a1 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Tue, 28 May 2024 21:51:13 -0500 Subject: [PATCH 186/201] minor change --- .../templates/conversation.summary.liquid | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid index d7ffae60..bb4e764f 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid @@ -1,4 +1,4 @@ -Please read each conversation in the [CONVERSATION] section and provide a summary. +Please read each conversation in the [CONVERSATIONS] section and provide a summary. *** Super Important! Please consider every conversation. Do not only consider the recent sentences. *** ** Please do not respond to the latest conversation. @@ -10,5 +10,5 @@ Please read each conversation in the [CONVERSATION] section and provide a summar [CONVERSATIONS] {% for text in texts -%} -{{ text }}{{ "\r\n\r\n" }} +{{ text }}{{ "\r\n" }} {%- endfor %} \ No newline at end of file From e9ae471a6571ab622fe9fee4df6226e82b86568c Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Tue, 28 May 2024 22:44:49 -0500 Subject: [PATCH 187/201] Remove openIfNotExist from LaunchBrowser --- .../Browsing/IWebBrowser.cs | 2 +- .../PlaywrightWebDriver.GoToPage.cs | 18 +++++++++++++++++- .../PlaywrightWebDriver.LaunchBrowser.cs | 2 +- .../PlaywrightWebDriver.LocateElement.cs | 4 ++-- .../SeleniumWebDriver.LaunchBrowser.cs | 2 +- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs index fc907351..e2581ba9 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs @@ -4,7 +4,7 @@ namespace BotSharp.Abstraction.Browsing; public interface IWebBrowser { - Task LaunchBrowser(string contextId, string? url, bool openIfNotExist = true); + Task LaunchBrowser(string contextId, string? url); Task ScreenshotAsync(string contextId, string path); Task ScrollPageAsync(BrowserActionParams actionParams); diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs index 5513e89f..f830bc9f 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs @@ -5,13 +5,29 @@ public partial class PlaywrightWebDriver public async Task GoToPage(string contextId, string url, bool openNewTab = false) { var result = new BrowserActionResult(); + var context = await _instance.InitInstance(contextId); try { + // Check if the page is already open + foreach (var p in context.Pages) + { + if (p.Url == url) + { + result.Body = await p.ContentAsync(); + result.IsSuccess = true; + await p.BringToFrontAsync(); + return result; + } + } + var page = openNewTab ? await _instance.NewPage(contextId) : _instance.GetPage(contextId); var response = await page.GotoAsync(url); await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded); - await page.WaitForLoadStateAsync(LoadState.NetworkIdle); + await page.WaitForLoadStateAsync(LoadState.NetworkIdle, new PageWaitForLoadStateOptions + { + Timeout = 1000 * 60 * 5 + }); if (response.Status == 200) { diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs index b3afd940..df844828 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs @@ -2,7 +2,7 @@ namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver { - public async Task LaunchBrowser(string contextId, string? url, bool openIfNotExist = true) + public async Task LaunchBrowser(string contextId, string? url) { var result = new BrowserActionResult() { diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs index df6e0a7d..60e0ce95 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs @@ -87,8 +87,8 @@ public partial class PlaywrightWebDriver await locator.EvaluateAsync("element => element.style.opacity = '1.0'"); }*/ - var text = await locator.InnerTextAsync(); - result.Body = text; + var html = await locator.InnerHTMLAsync(); + result.Body = html; result.IsSuccess = true; } else if (count > 1) diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.LaunchBrowser.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.LaunchBrowser.cs index 3203b617..73cfa9ee 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.LaunchBrowser.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/SeleniumDriver/SeleniumWebDriver.LaunchBrowser.cs @@ -2,7 +2,7 @@ namespace BotSharp.Plugin.WebDriver.Drivers.SeleniumDriver; public partial class SeleniumWebDriver { - public async Task LaunchBrowser(string contextId, string? url, bool openIfNotExist = true) + public async Task LaunchBrowser(string contextId, string? url) { var result = new BrowserActionResult() { From 89eb725f971ebf8634f0c46e9ad2f0a0e89d7951 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Wed, 29 May 2024 19:27:38 -0500 Subject: [PATCH 188/201] clean file controller --- .../Controllers/ConversationController.cs | 78 ++++++++++++++++--- .../Controllers/FileController.cs | 73 ----------------- .../Controllers/UserController.cs | 32 ++++++++ 3 files changed, 101 insertions(+), 82 deletions(-) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index adc37636..aba09f52 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -219,15 +219,7 @@ public class ConversationController : ControllerBase return response; } - private void SetStates(IConversationService conv, NewMessageModel input) - { - conv.States.SetState("channel", input.Channel, source: StateSource.External) - .SetState("provider", input.Provider, source: StateSource.External) - .SetState("model", input.Model, source: StateSource.External) - .SetState("temperature", input.Temperature, source: StateSource.External) - .SetState("sampling_factor", input.SamplingFactor, source: StateSource.External); - } - + #region Send message [HttpPost("/conversation/{agentId}/{conversationId}")] public async Task SendMessage([FromRoute] string agentId, [FromRoute] string conversationId, @@ -350,6 +342,73 @@ public class ConversationController : ControllerBase // await OnEventCompleted(Response); } + #endregion + + #region Files and attachments + [HttpPost("/conversation/{conversationId}/attachments")] + public IActionResult UploadAttachments([FromRoute] string conversationId, + IFormFile[] files) + { + if (files != null && files.Length > 0) + { + var fileService = _services.GetRequiredService(); + var dir = fileService.GetDirectory(conversationId); + foreach (var file in files) + { + // Save the file, process it, etc. + var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"'); + var filePath = Path.Combine(dir, fileName); + + using (var stream = new FileStream(filePath, FileMode.Create)) + { + file.CopyTo(stream); + } + } + + return Ok(new { message = "File uploaded successfully." }); + } + + return BadRequest(new { message = "Invalid file." }); + } + + [HttpGet("/conversation/{conversationId}/files/{messageId}")] + public IEnumerable GetMessageFiles([FromRoute] string conversationId, [FromRoute] string messageId) + { + var fileService = _services.GetRequiredService(); + var files = fileService.GetMessageFiles(conversationId, new List { messageId }); + return files?.Select(x => MessageFileViewModel.Transform(x))?.ToList() ?? new List(); + } + + [HttpGet("/conversation/{conversationId}/message/{messageId}/file/{fileName}")] + public IActionResult GetMessageFile([FromRoute] string conversationId, [FromRoute] string messageId, [FromRoute] string fileName) + { + var fileService = _services.GetRequiredService(); + var file = fileService.GetMessageFile(conversationId, messageId, fileName); + if (string.IsNullOrEmpty(file)) + { + return NotFound(); + } + return BuildFileResult(file); + } + #endregion + + #region Private methods + private void SetStates(IConversationService conv, NewMessageModel input) + { + conv.States.SetState("channel", input.Channel, source: StateSource.External) + .SetState("provider", input.Provider, source: StateSource.External) + .SetState("model", input.Model, source: StateSource.External) + .SetState("temperature", input.Temperature, source: StateSource.External) + .SetState("sampling_factor", input.SamplingFactor, source: StateSource.External); + } + + private FileContentResult BuildFileResult(string file) + { + using Stream stream = System.IO.File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read); + var bytes = new byte[stream.Length]; + stream.Read(bytes, 0, (int)stream.Length); + return File(bytes, "application/octet-stream", Path.GetFileName(file)); + } private async Task OnChunkReceived(HttpResponse response, ChatResponseModel message) { @@ -391,4 +450,5 @@ public class ConversationController : ControllerBase return jsonOption; } + #endregion } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs index 0c7fa255..36dddebd 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs @@ -10,77 +10,4 @@ public class FileController : ControllerBase { _services = services; } - - [HttpPost("/conversation/{conversationId}/attachments")] - public IActionResult UploadAttachments([FromRoute] string conversationId, - IFormFile[] files) - { - if (files != null && files.Length > 0) - { - var fileService = _services.GetRequiredService(); - var dir = fileService.GetDirectory(conversationId); - foreach (var file in files) - { - // Save the file, process it, etc. - var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"'); - var filePath = Path.Combine(dir, fileName); - - using (var stream = new FileStream(filePath, FileMode.Create)) - { - file.CopyTo(stream); - } - } - - return Ok(new { message = "File uploaded successfully." }); - } - - return BadRequest(new { message = "Invalid file." }); - } - - [HttpGet("/conversation/{conversationId}/files/{messageId}")] - public IEnumerable GetMessageFiles([FromRoute] string conversationId, [FromRoute] string messageId) - { - var fileService = _services.GetRequiredService(); - var files = fileService.GetMessageFiles(conversationId, new List { messageId }); - return files?.Select(x => MessageFileViewModel.Transform(x))?.ToList() ?? new List(); - } - - [HttpGet("/conversation/{conversationId}/message/{messageId}/file/{fileName}")] - public IActionResult GetMessageFile([FromRoute] string conversationId, [FromRoute] string messageId, [FromRoute] string fileName) - { - var fileService = _services.GetRequiredService(); - var file = fileService.GetMessageFile(conversationId, messageId, fileName); - if (string.IsNullOrEmpty(file)) - { - return NotFound(); - } - return BuildFileResult(file); - } - - [HttpPost("/user/avatar")] - public bool UploadUserAvatar([FromBody] BotSharpFile file) - { - var fileService = _services.GetRequiredService(); - return fileService.SaveUserAvatar(file); - } - - [HttpGet("/user/avatar")] - public IActionResult GetUserAvatar() - { - var fileService = _services.GetRequiredService(); - var file = fileService.GetUserAvatar(); - if (string.IsNullOrEmpty(file)) - { - return NotFound(); - } - return BuildFileResult(file); - } - - private FileContentResult BuildFileResult(string file) - { - using Stream stream = System.IO.File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read); - var bytes = new byte[stream.Length]; - stream.Read(bytes, 0, (int)stream.Length); - return File(bytes, "application/octet-stream", Path.GetFileName(file)); - } } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs index e413724b..682b963b 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs @@ -106,4 +106,36 @@ public class UserController : ControllerBase { return await _userService.VerifyEmailUnique(email); } + + #region Avatar + [HttpPost("/user/avatar")] + public bool UploadUserAvatar([FromBody] BotSharpFile file) + { + var fileService = _services.GetRequiredService(); + return fileService.SaveUserAvatar(file); + } + + [HttpGet("/user/avatar")] + public IActionResult GetUserAvatar() + { + var fileService = _services.GetRequiredService(); + var file = fileService.GetUserAvatar(); + if (string.IsNullOrEmpty(file)) + { + return NotFound(); + } + return BuildFileResult(file); + } + #endregion + + + #region Private methods + private FileContentResult BuildFileResult(string file) + { + using Stream stream = System.IO.File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read); + var bytes = new byte[stream.Length]; + stream.Read(bytes, 0, (int)stream.Length); + return File(bytes, "application/octet-stream", Path.GetFileName(file)); + } + #endregion } From f3bdc73a4ae35d0321ecaff0f9a264b9076806c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=93=E7=A3=8A?= Date: Thu, 30 May 2024 16:49:55 +0800 Subject: [PATCH 189/201] add Pagination size default value --- src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs index 512d27c4..9ada5f63 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs @@ -15,7 +15,7 @@ public class Pagination { get { - return _size; + return _size > 0 ? _size : 1; } set { From 20bd008ceb6392668812c1641939e764b02f509e Mon Sep 17 00:00:00 2001 From: YouWeiDH Date: Thu, 30 May 2024 17:11:42 +0800 Subject: [PATCH 190/201] hdong:keep same logic with name. --- .../Users/IUserService.cs | 4 ++-- .../Users/Services/UserService.cs | 20 +++++++++---------- .../Controllers/UserController.cs | 10 +++++----- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs index 4071790c..722917cc 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs @@ -10,6 +10,6 @@ public interface IUserService Task ActiveUser(UserActivationModel model); Task GetToken(string authorization); Task GetMyProfile(); - Task VerifyUserUnique(string userName); - Task VerifyEmailUnique(string email); + Task VerifyUserNameExisting(string userName); + Task VerifyEmailExisting(string email); } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index 19242e39..c1ee7920 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -16,8 +16,8 @@ public class UserService : IUserService private readonly ILogger _logger; private readonly AccountSetting _setting; - public UserService(IServiceProvider services, - IUserIdentity user, + public UserService(IServiceProvider services, + IUserIdentity user, ILogger logger, AccountSetting setting) { @@ -92,7 +92,7 @@ public class UserService : IUserService User? user = record; var hooks = _services.GetServices(); - if (record == null || record.Source != "internal") + if (record == null || record.Source != "internal") { // check 3rd party user foreach (var hook in hooks) @@ -264,27 +264,27 @@ public class UserService : IUserService return token; } - public async Task VerifyUserUnique(string userName) + public async Task VerifyUserNameExisting(string userName) { if (string.IsNullOrEmpty(userName)) - return false; + return true; var db = _services.GetRequiredService(); var user = db.GetUserByUserName(userName); - if (user == null) + if (user != null) return true; - + return false; } - public async Task VerifyEmailUnique(string email) + public async Task VerifyEmailExisting(string email) { if (string.IsNullOrEmpty(email)) - return false; + return true; var db = _services.GetRequiredService(); var emailName = db.GetUserByEmail(email); - if (emailName == null) + if (emailName != null) return true; return false; diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs index e413724b..9955eeb0 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs @@ -36,7 +36,7 @@ public class UserController : ControllerBase [AllowAnonymous] [HttpGet("/sso/{provider}")] - public async Task Authorize([FromRoute] string provider,string redirectUrl) + public async Task Authorize([FromRoute] string provider, string redirectUrl) { return Challenge(new AuthenticationProperties { RedirectUri = redirectUrl }, provider); } @@ -96,14 +96,14 @@ public class UserController : ControllerBase } [HttpGet("/user/name/existing")] - public async Task VerifyUserUnique([FromQuery] string userName) + public async Task VerifyUserNameExisting([FromQuery] string userName) { - return await _userService.VerifyUserUnique(userName); + return await _userService.VerifyUserNameExisting(userName); } [HttpGet("/user/email/existing")] - public async Task VerifyEmailUnique([FromQuery] string email) + public async Task VerifyEmailExisting([FromQuery] string email) { - return await _userService.VerifyEmailUnique(email); + return await _userService.VerifyEmailExisting(email); } } From aaf192be427ede4e240066cca193a10bfddd9562 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 30 May 2024 09:51:57 -0500 Subject: [PATCH 191/201] remove setting --- .../Routing/Hooks/RoutingAgentHook.cs | 45 ------------------- 1 file changed, 45 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs index 4aec88c8..a847f078 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs @@ -104,51 +104,6 @@ public class RoutingAgentHook : AgentHookBase } }); } - - var settings = _services.GetRequiredService(); - if (settings.EnableHttpHandler) - { - var httpHandlerName = "handle_http_request"; - var existHttpHandler = functions.Any(x => x.Name == httpHandlerName); - var funcs = _services.GetServices(); - var httpRequestFunc = funcs.FirstOrDefault(x => x.Name == httpHandlerName); - if (!existHttpHandler && httpRequestFunc != null) - { - var json = JsonSerializer.Serialize(new - { - request_url = new - { - type = "string", - description = $"The http url that is requested. It can be an absolute url that starts with \"http\" or \"https\", or a relative url that starts with \"/\"" - }, - http_method = new - { - type = "string", - description = $"The http method that is requested, e.g., GET, POST, PUT, and DELETE." - }, - request_content = new - { - type = "string", - description = $"The http request content. It must be in json format." - } - }); - functions.Add(new FunctionDef - { - Name = httpRequestFunc.Name, - Description = "If the user requests to send an http request, you need to capture the http method and request content, and then call this function to send the http request.", - Parameters = - { - Properties = JsonSerializer.Deserialize(json), - Required = new List - { - "request_url", - "http_method" - } - } - }); - } - - } } return base.OnFunctionsLoaded(functions); From 1a554ea15ae4d1e69efa51e98a66fdde06ac2308 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 30 May 2024 11:15:51 -0500 Subject: [PATCH 192/201] fix user issue --- .../Conversations/Services/ConversationService.Summary.cs | 5 +---- .../BotSharp.OpenAPI/Controllers/ConversationController.cs | 1 - .../BotSharp.Plugin.MongoStorage/MongoStoragePlugin.cs | 6 +++++- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs index 533020f0..e71d328d 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs @@ -95,10 +95,7 @@ public partial class ConversationService foreach (var dialog in dialogs.TakeLast(maxDialogCount)) { var role = dialog.Role; - if (role != AgentRole.User) - { - role = AgentRole.Assistant; - } + if (role != AgentRole.User && role != AgentRole.Assistant) continue; conversation += $"{role}: {dialog.Payload ?? dialog.Content}\r\n"; } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index aba09f52..c8f573a6 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -30,7 +30,6 @@ public class ConversationController : ControllerBase { AgentId = agentId, Channel = ConversationChannel.OpenAPI, - UserId = _user.Id, TaskId = config.TaskId }; conv = await service.NewConversation(conv); diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoStoragePlugin.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoStoragePlugin.cs index 8c4c43b9..7d47a98c 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoStoragePlugin.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoStoragePlugin.cs @@ -1,5 +1,6 @@ using BotSharp.Abstraction.Plugins.Models; using BotSharp.Abstraction.Repositories.Enums; +using BotSharp.Abstraction.Users.Enums; using BotSharp.Plugin.MongoStorage.Repository; namespace BotSharp.Plugin.MongoStorage; @@ -34,7 +35,10 @@ public class MongoStoragePlugin : IBotSharpPlugin public bool AttachMenu(List menu) { var section = menu.First(x => x.Label == "Apps"); - menu.Add(new PluginMenuDef("MongoDB", icon: "bx bx-data", link: "page/mongodb", weight: section.Weight + 10)); + menu.Add(new PluginMenuDef("MongoDB", icon: "bx bx-data", link: "page/mongodb", weight: section.Weight + 10) + { + Roles = new List { UserRole.Admin } + }); return true; } } From d8615ead54fc4321f31ccf2dfd0e086a2419cb91 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 30 May 2024 11:17:52 -0500 Subject: [PATCH 193/201] minor change --- .../Conversations/Services/ConversationService.Summary.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs index e71d328d..f5c7d76c 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs @@ -22,6 +22,8 @@ public partial class ConversationService if (dialogs.IsNullOrEmpty()) continue; var content = GetConversationContent(dialogs); + if (string.IsNullOrEmpty(content)) continue; + contents.Add(content); } @@ -100,6 +102,11 @@ public partial class ConversationService conversation += $"{role}: {dialog.Payload ?? dialog.Content}\r\n"; } + if (string.IsNullOrEmpty(conversation)) + { + return string.Empty; + } + return conversation + "\r\n"; } } From c0cd2387a2bd896c26fd8aa491b2ce347c61ab0b Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 30 May 2024 22:47:07 -0500 Subject: [PATCH 194/201] fix user cache --- .../Conversations/Services/ConversationService.Summary.cs | 7 ++++++- .../BotSharp.Core/Users/Services/UserService.cs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs index f5c7d76c..a34a2793 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs @@ -97,7 +97,12 @@ public partial class ConversationService foreach (var dialog in dialogs.TakeLast(maxDialogCount)) { var role = dialog.Role; - if (role != AgentRole.User && role != AgentRole.Assistant) continue; + if (role == AgentRole.Function) continue; + + if (role != AgentRole.User) + { + role = AgentRole.Assistant; + } conversation += $"{role}: {dialog.Payload ?? dialog.Content}\r\n"; } diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index c1ee7920..bc721eab 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -217,7 +217,7 @@ public class UserService : IUserService return user; } - [MemoryCache(10 * 60)] + [MemoryCache(10 * 60, perInstanceCache: true)] public async Task GetUser(string id) { var db = _services.GetRequiredService(); From da850ead9e0346c84a5a321fc262cfb1c8f4128e Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 30 May 2024 23:12:34 -0500 Subject: [PATCH 195/201] minor change --- .../Conversations/Services/ConversationService.Summary.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs index a34a2793..03e098b1 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs @@ -27,6 +27,8 @@ public partial class ConversationService contents.Add(content); } + if (contents.IsNullOrEmpty()) return string.Empty; + var router = await agentService.LoadAgent(AIAssistant); var prompt = GetPrompt(router, contents); var summary = await Summarize(router, prompt); From efb650d92a174cfbd31e634454c937c1bc2022bd Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Fri, 31 May 2024 11:26:23 -0500 Subject: [PATCH 196/201] fix user profile --- .../BotSharp.Core/Users/Services/UserService.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index bc721eab..3afea13f 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -206,7 +206,12 @@ public class UserService : IUserService { var db = _services.GetRequiredService(); User user = default; - if (_user.UserName != null) + + if (_user.Id != null) + { + user = db.GetUserById(_user.Id); + } + else if (_user.UserName != null) { user = db.GetUserByUserName(_user.UserName); } From 65514f5589d3ad2518a83bbcfa5d39d140f8fdf0 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Fri, 31 May 2024 12:02:34 -0500 Subject: [PATCH 197/201] fix conv file delete issue --- .../Conversations/Services/ConversationService.Summary.cs | 2 +- .../BotSharp.Core/Files/BotSharpFileService.Conversation.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs index 03e098b1..de0b00eb 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs @@ -22,7 +22,7 @@ public partial class ConversationService if (dialogs.IsNullOrEmpty()) continue; var content = GetConversationContent(dialogs); - if (string.IsNullOrEmpty(content)) continue; + if (string.IsNullOrWhiteSpace(content)) continue; contents.Add(content); } diff --git a/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.Conversation.cs b/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.Conversation.cs index f12ecb60..71e34549 100644 --- a/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.Conversation.cs +++ b/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.Conversation.cs @@ -138,7 +138,7 @@ public partial class BotSharpFileService foreach (var messageId in messageIds) { var dir = GetConversationFileDirectory(conversationId, messageId); - if (string.IsNullOrEmpty(dir)) continue; + if (!ExistDirectory(dir)) continue; Thread.Sleep(100); Directory.Delete(dir, true); From ac8c9749b45a0a356f835f270f1370c2de4b2143 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 4 Jun 2024 11:33:41 -0500 Subject: [PATCH 198/201] remove conversation header --- .../Conversations/Services/ConversationService.Summary.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs index de0b00eb..2e7657ba 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs @@ -41,10 +41,10 @@ public partial class ConversationService var template = agent.Templates.First(x => x.Name == "conversation.summary").Content; var render = _services.GetRequiredService(); - var texts = string.Empty; + var texts = new List(); for (int i = 0; i < contents.Count; i++) { - texts += $"[Conversation {i+1}]\r\n{contents[i]}"; + texts.Add($"{contents[i]}"); } return render.Render(template, new Dictionary From ae8e7b270a78a772e06e670161f77edea49b79bc Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 4 Jun 2024 11:36:38 -0500 Subject: [PATCH 199/201] update summary prompt --- .../templates/conversation.summary.liquid | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid index bb4e764f..cf22b0e6 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid @@ -4,11 +4,13 @@ Please read each conversation in the [CONVERSATIONS] section and provide a summa ** Please do not respond to the latest conversation. ** If there are different topics in the conversations, please summarize each topic in different sentences and list them in bullets. * Please use concise sentences to summarize each topic. +* Please do not exceed 32 words when summarizing each topic. * Please do not include excessive details in the summaries. * Please use 'user' instead of 'you', 'he' or 'she'. [CONVERSATIONS] {% for text in texts -%} +[CONVERSATION] {{ text }}{{ "\r\n" }} {%- endfor %} \ No newline at end of file From 59475bfa7917cb27c012012d150c095ea80c7f76 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 4 Jun 2024 21:50:44 -0500 Subject: [PATCH 200/201] refine summary prompt --- .../templates/conversation.summary.liquid | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid index cf22b0e6..a8cc4cd4 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid @@ -3,10 +3,12 @@ Please read each conversation in the [CONVERSATIONS] section and provide a summa *** Super Important! Please consider every conversation. Do not only consider the recent sentences. *** ** Please do not respond to the latest conversation. ** If there are different topics in the conversations, please summarize each topic in different sentences and list them in bullets. -* Please use concise sentences to summarize each topic. -* Please do not exceed 32 words when summarizing each topic. -* Please do not include excessive details in the summaries. -* Please use 'user' instead of 'you', 'he' or 'she'. +** Please summarize each conversation separately. +* Please do not exceed 20 words when summarizing each topic. +* Please do not contain general information in each topic summary. +* Please include some but not excessive details in each topic summary. +* Please do not use "user". +* Please use 'you' instead of 'user', 'he' or 'she'. [CONVERSATIONS] From 8d25414d7624d405400590b9fdabebf9abc27566 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 4 Jun 2024 22:03:00 -0500 Subject: [PATCH 201/201] minor change --- .../templates/conversation.summary.liquid | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid index a8cc4cd4..eb626316 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.summary.liquid @@ -7,8 +7,7 @@ Please read each conversation in the [CONVERSATIONS] section and provide a summa * Please do not exceed 20 words when summarizing each topic. * Please do not contain general information in each topic summary. * Please include some but not excessive details in each topic summary. -* Please do not use "user". -* Please use 'you' instead of 'user', 'he' or 'she'. +* Please do not use "user", "I", "you", "he" or "she". [CONVERSATIONS]