From 8d5b1402d014f03a8e08c4d5b476f3e582b33707 Mon Sep 17 00:00:00 2001 From: "nick.yi" Date: Fri, 27 Jun 2025 14:13:53 +0800 Subject: [PATCH 01/24] Fixed an issue where response_to_user is called when the user wants to end the conversation. It should call route_to_agent with converation_end as true --- .../instructions/instruction.liquid | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid index 775de66c..8d2da12c 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid @@ -5,7 +5,8 @@ Follow these steps to handle user request: 2. Determine which agent is suitable to handle this conversation. Try to minimize the routing of human service. 3. Extract and populate 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. If user is greeting, you can call function response_to_user with a greeting message. +5. If user wants to end the conversation, call route_to_agent function with conversation_end as true. +6. If user is greeting, you can call function response_to_user with a greeting message. {% if routing_requirements and routing_requirements != empty %} [REQUIREMENTS] From 7b64416c9b2c7bf12fe16044fa6c9bfc25572c38 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 3 Jul 2025 12:09:10 -0500 Subject: [PATCH 02/24] fix realtime toolcall --- .../Providers/Chat/ChatCompletionProvider.cs | 2 +- .../Providers/Realtime/RealTimeCompletionProvider.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs index 42ce1ac9..eb14ac99 100644 --- a/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs @@ -270,7 +270,7 @@ public class ChatCompletionProvider : IChatCompletion { messages.Add(new AssistantChatMessage(new List { - ChatToolCall.CreateFunctionToolCall(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.FunctionName, BinaryData.FromString(message.FunctionArgs ?? string.Empty)) + ChatToolCall.CreateFunctionToolCall(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.FunctionName, BinaryData.FromString(message.FunctionArgs ?? "{}")) })); messages.Add(new ToolChatMessage(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.Content)); diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs index 8219bf1c..92fa6f84 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs @@ -615,10 +615,10 @@ public class RealTimeCompletionProvider : IRealTimeCompletion { messages.Add(new AssistantChatMessage(new List { - ChatToolCall.CreateFunctionToolCall(message.ToolCallId, message.FunctionName, BinaryData.FromString(message.FunctionArgs ?? string.Empty)) + ChatToolCall.CreateFunctionToolCall(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.FunctionName, BinaryData.FromString(message.FunctionArgs ?? "{}")) })); - messages.Add(new ToolChatMessage(message.ToolCallId, message.Content)); + messages.Add(new ToolChatMessage(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.Content)); } else if (message.Role == AgentRole.User) { From 3968463a7e9fbc087892e31f6ca0453fe45f39aa Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 7 Jul 2025 12:24:44 -0500 Subject: [PATCH 03/24] refine side car states --- .../SideCar/IConversationSideCar.cs | 7 ++- .../SideCar/Models/SideCarOptions.cs | 12 +++++ .../Services/BotSharpConversationSideCar.cs | 50 ++++++++++++++++++- .../BotSharp.Core.SideCar/Using.cs | 3 +- .../Services/ConversationStateService.cs | 2 +- 5 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/SideCar/Models/SideCarOptions.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/SideCar/IConversationSideCar.cs b/src/Infrastructure/BotSharp.Abstraction/SideCar/IConversationSideCar.cs index 28162a7e..3c9bcfa4 100644 --- a/src/Infrastructure/BotSharp.Abstraction/SideCar/IConversationSideCar.cs +++ b/src/Infrastructure/BotSharp.Abstraction/SideCar/IConversationSideCar.cs @@ -1,3 +1,5 @@ +using BotSharp.Abstraction.SideCar.Models; + namespace BotSharp.Abstraction.SideCar; public interface IConversationSideCar @@ -11,5 +13,8 @@ public interface IConversationSideCar ConversationBreakpoint? GetConversationBreakpoint(string conversationId); void UpdateConversationStates(string conversationId, List states); Task SendMessage(string agentId, string text, - PostbackMessageModel? postback = null, List? states = null, List? dialogs = null); + PostbackMessageModel? postback = null, + List? states = null, + List? dialogs = null, + SideCarOptions? options = null); } diff --git a/src/Infrastructure/BotSharp.Abstraction/SideCar/Models/SideCarOptions.cs b/src/Infrastructure/BotSharp.Abstraction/SideCar/Models/SideCarOptions.cs new file mode 100644 index 00000000..a4858c5c --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/SideCar/Models/SideCarOptions.cs @@ -0,0 +1,12 @@ +namespace BotSharp.Abstraction.SideCar.Models; + +public class SideCarOptions +{ + public bool IsInheritStates { get; set; } + public IEnumerable? InheritStateKeys { get; set; } + + public static SideCarOptions Empty() + { + return new SideCarOptions(); + } +} diff --git a/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs b/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs index a948e5e2..9689d367 100644 --- a/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs +++ b/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs @@ -14,6 +14,7 @@ limitations under the License. ******************************************************************************/ +using BotSharp.Abstraction.SideCar.Models; using BotSharp.Core.Infrastructures; namespace BotSharp.Core.SideCar.Services; @@ -24,6 +25,7 @@ public class BotSharpConversationSideCar : IConversationSideCar private readonly ILogger _logger; private Stack _contextStack = new(); + private SideCarOptions? _sideCarOptions; private bool _enabled = false; private string _conversationId = string.Empty; @@ -98,8 +100,13 @@ public class BotSharpConversationSideCar : IConversationSideCar } public async Task SendMessage(string agentId, string text, - PostbackMessageModel? postback = null, List? states = null, List? dialogs = null) + PostbackMessageModel? postback = null, + List? states = null, + List? dialogs = null, + SideCarOptions? options = null) { + _sideCarOptions = options; + BeforeExecute(dialogs); var response = await InnerExecute(agentId, text, postback, states); AfterExecute(); @@ -166,7 +173,7 @@ public class BotSharpConversationSideCar : IConversationSideCar var node = _contextStack.Pop(); // Recover - state.SetCurrentState(node.State); + RestoreStates(node.State); routing.Context.SetRecursiveCounter(node.RecursiveCounter); routing.Context.SetAgentStack(node.RoutingStack); routing.Context.SetDialogs(node.RoutingDialogs); @@ -181,4 +188,43 @@ public class BotSharpConversationSideCar : IConversationSideCar && !string.IsNullOrEmpty(conversationId) && !string.IsNullOrEmpty(_conversationId); } + + private void RestoreStates(ConversationState prevStates) + { + var innerStates = prevStates; + var state = _services.GetRequiredService(); + + if (_sideCarOptions?.IsInheritStates == true) + { + var curStates = state.GetCurrentState(); + foreach (var pair in curStates) + { + var endNode = pair.Value.Values.LastOrDefault(); + if (endNode == null) continue; + + if (_sideCarOptions?.InheritStateKeys?.Any() == true + && !_sideCarOptions.InheritStateKeys.Contains(pair.Key)) + { + continue; + } + + if (innerStates.ContainsKey(pair.Key)) + { + innerStates[pair.Key].Values.Add(endNode); + } + else + { + innerStates[pair.Key] = new StateKeyValue + { + Key = pair.Key, + Versioning = pair.Value.Versioning, + Readonly = pair.Value.Readonly, + Values = [endNode] + }; + } + } + } + + state.SetCurrentState(innerStates); + } } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core.SideCar/Using.cs b/src/Infrastructure/BotSharp.Core.SideCar/Using.cs index d047ee15..e391d790 100644 --- a/src/Infrastructure/BotSharp.Core.SideCar/Using.cs +++ b/src/Infrastructure/BotSharp.Core.SideCar/Using.cs @@ -16,5 +16,6 @@ global using BotSharp.Abstraction.Conversations.Models; global using BotSharp.Abstraction.Models; global using BotSharp.Abstraction.Routing; global using BotSharp.Abstraction.SideCar; +global using BotSharp.Abstraction.SideCar.Models; global using BotSharp.Abstraction.Utilities; -global using BotSharp.Core.SideCar.Settings; \ No newline at end of file +global using BotSharp.Core.SideCar.Settings; diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index c8665179..ff61fde2 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -443,7 +443,7 @@ public class ConversationStateService : IConversationStateService public void SetCurrentState(ConversationState state) { - var values = _curStates.Values.ToList(); + var values = state.Values.ToList(); var copy = JsonSerializer.Deserialize>(JsonSerializer.Serialize(values)); _curStates = new ConversationState(copy ?? []); } From df7b7bb61d2db7ec4422930fbf4c389242d7240a Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 7 Jul 2025 17:11:18 -0500 Subject: [PATCH 04/24] temp revert --- .../Services/BotSharpConversationSideCar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs b/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs index 9689d367..2d1fec73 100644 --- a/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs +++ b/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs @@ -173,7 +173,7 @@ public class BotSharpConversationSideCar : IConversationSideCar var node = _contextStack.Pop(); // Recover - RestoreStates(node.State); + state.SetCurrentState(node.State); routing.Context.SetRecursiveCounter(node.RecursiveCounter); routing.Context.SetAgentStack(node.RoutingStack); routing.Context.SetDialogs(node.RoutingDialogs); From f43c32b8662baf16b15690ea35ee3a026ceb6a62 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 7 Jul 2025 17:29:25 -0500 Subject: [PATCH 05/24] revert to init --- .../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 ff61fde2..c8665179 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -443,7 +443,7 @@ public class ConversationStateService : IConversationStateService public void SetCurrentState(ConversationState state) { - var values = state.Values.ToList(); + var values = _curStates.Values.ToList(); var copy = JsonSerializer.Deserialize>(JsonSerializer.Serialize(values)); _curStates = new ConversationState(copy ?? []); } From ef1142f62ac11a886860b4284bf4c6066b9a7d48 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 7 Jul 2025 17:41:39 -0500 Subject: [PATCH 06/24] refine --- .../Services/BotSharpConversationSideCar.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs b/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs index 2d1fec73..e4996b06 100644 --- a/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs +++ b/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs @@ -167,13 +167,11 @@ public class BotSharpConversationSideCar : IConversationSideCar private void AfterExecute() { - var state = _services.GetRequiredService(); var routing = _services.GetRequiredService(); - var node = _contextStack.Pop(); // Recover - state.SetCurrentState(node.State); + RestoreStates(node.State); routing.Context.SetRecursiveCounter(node.RecursiveCounter); routing.Context.SetAgentStack(node.RoutingStack); routing.Context.SetDialogs(node.RoutingDialogs); From 9f7aa8f4d9e4674c052b93199398179d13d2862f Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 7 Jul 2025 17:43:17 -0500 Subject: [PATCH 07/24] revert --- .../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 c8665179..ff61fde2 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -443,7 +443,7 @@ public class ConversationStateService : IConversationStateService public void SetCurrentState(ConversationState state) { - var values = _curStates.Values.ToList(); + var values = state.Values.ToList(); var copy = JsonSerializer.Deserialize>(JsonSerializer.Serialize(values)); _curStates = new ConversationState(copy ?? []); } From da097de0538eba291e967d641668cf7afe698f9f Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Tue, 8 Jul 2025 00:16:21 -0500 Subject: [PATCH 08/24] refine chat stream code --- .../BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs | 8 ++++++-- .../Providers/Realtime/RealTimeCompletionProvider.cs | 12 ++++-------- .../Providers/Realtime/RealTimeCompletionProvider.cs | 12 ++++-------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs b/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs index cba35a15..171bf0f2 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs @@ -62,6 +62,7 @@ public class ChatStreamMiddleware var hub = services.GetRequiredService(); var conn = hub.SetHubConnection(conversationId); conn.CurrentAgentId = agentId; + InitEvents(conn); // load conversation and state var convService = services.GetRequiredService(); @@ -128,6 +129,11 @@ public class ChatStreamMiddleware break; } + return (response.Event, data); + } + + private void InitEvents(RealtimeHubConnection conn) + { conn.OnModelMessageReceived = message => JsonSerializer.Serialize(new { @@ -147,7 +153,5 @@ public class ChatStreamMiddleware { @event = "clear" }); - - return (response.Event, data); } } diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs index 2e95cfa2..6838c284 100644 --- a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs @@ -329,17 +329,13 @@ public class GoogleRealTimeProvider : IRealTimeCompletion var words = new List(); HookEmitter.Emit(_services, hook => words.AddRange(hook.OnModelTranscriptPrompt(agent)), agent.Id); - var functions = request.Tools?.SelectMany(s => s.FunctionDeclarations).Select(x => + var functions = request.Tools?.SelectMany(s => s.FunctionDeclarations).Select(x => new FunctionDef { - var fn = new FunctionDef - { - Name = x.Name ?? string.Empty, - Description = x.Description ?? string.Empty, - Parameters = x.Parameters != null + Name = x.Name ?? string.Empty, + Description = x.Description ?? string.Empty, + Parameters = x.Parameters != null ? JsonSerializer.Deserialize(JsonSerializer.Serialize(x.Parameters)) : null - }; - return fn; }).ToArray(); await HookEmitter.Emit(_services, diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs index 92fa6f84..3a0902d6 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs @@ -326,15 +326,11 @@ public class RealTimeCompletionProvider : IRealTimeCompletion var (prompt, messages, options) = PrepareOptions(agent, []); var instruction = messages.FirstOrDefault()?.Content.FirstOrDefault()?.Text ?? agent?.Description ?? string.Empty; - var functions = options.Tools.Select(x => + var functions = options.Tools.Select(x => new FunctionDef { - var fn = new FunctionDef - { - Name = x.FunctionName, - Description = x.FunctionDescription - }; - fn.Parameters = JsonSerializer.Deserialize(x.FunctionParameters); - return fn; + Name = x.FunctionName, + Description = x.FunctionDescription, + Parameters = JsonSerializer.Deserialize(x.FunctionParameters) }).ToArray(); var realtimeModelSettings = _services.GetRequiredService(); From 94c3a86635b8e00ce040e0cdaec2615110164fee Mon Sep 17 00:00:00 2001 From: "nick.yi" Date: Tue, 8 Jul 2025 23:03:54 +0800 Subject: [PATCH 09/24] optimize twilio --- .../Controllers/TwilioInboundController.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs index 511abb45..07c4ad87 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs @@ -63,6 +63,11 @@ public class TwilioInboundController : TwilioController instruction.AgentId = request.AgentId; instruction.ConversationId = request.ConversationId; + await HookEmitter.Emit(_services, async hook => + { + await hook.OnSessionCreated(request); + }, request.AgentId); + if (twilio.MachineDetected(request)) { response = new VoiceResponse(); @@ -114,12 +119,7 @@ public class TwilioInboundController : TwilioController await Task.Delay(1500); await twilio.StartRecording(request.CallSid, request.AgentId, request.ConversationId); }); - } - - await HookEmitter.Emit(_services, async hook => - { - await hook.OnSessionCreated(request); - }, request.AgentId); + } return TwiML(response); } From a5d9da92bc615e802a84ce45a2d7871aa1664607 Mon Sep 17 00:00:00 2001 From: "nick.yi" Date: Tue, 8 Jul 2025 23:09:22 +0800 Subject: [PATCH 10/24] Revert "Fixed an issue where response_to_user is called when the user wants to end the conversation. It should call route_to_agent with converation_end as true" This reverts commit 8d5b1402d014f03a8e08c4d5b476f3e582b33707. --- .../instructions/instruction.liquid | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid index 8d2da12c..775de66c 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid @@ -5,8 +5,7 @@ Follow these steps to handle user request: 2. Determine which agent is suitable to handle this conversation. Try to minimize the routing of human service. 3. Extract and populate 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. If user wants to end the conversation, call route_to_agent function with conversation_end as true. -6. If user is greeting, you can call function response_to_user with a greeting message. +5. If user is greeting, you can call function response_to_user with a greeting message. {% if routing_requirements and routing_requirements != empty %} [REQUIREMENTS] From e8b2bffeb346a59cf184a518b826466dc1a73afa Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 8 Jul 2025 16:41:25 -0500 Subject: [PATCH 11/24] save states --- .../Controllers/TwilioInboundController.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs index 07c4ad87..31d3db52 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs @@ -68,6 +68,9 @@ public class TwilioInboundController : TwilioController await hook.OnSessionCreated(request); }, request.AgentId); + var conv = _services.GetRequiredService(); + conv.SaveStates(); + if (twilio.MachineDetected(request)) { response = new VoiceResponse(); @@ -204,7 +207,7 @@ public class TwilioInboundController : TwilioController storage.Append(conversation.Id, new RoleDialogModel(AgentRole.User, request.Intent) { - CurrentAgentId = conversation.Id, + CurrentAgentId = agent.Id, CreatedAt = DateTime.UtcNow }); } From 0da486a61b0c9d789ccc4cc023a1956dae76b715 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Tue, 8 Jul 2025 20:30:08 -0500 Subject: [PATCH 12/24] detour rate limit --- .../BotSharp.Logger/Hooks/RateLimitConversationHook.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Infrastructure/BotSharp.Logger/Hooks/RateLimitConversationHook.cs b/src/Infrastructure/BotSharp.Logger/Hooks/RateLimitConversationHook.cs index 97308f69..4752f6d6 100644 --- a/src/Infrastructure/BotSharp.Logger/Hooks/RateLimitConversationHook.cs +++ b/src/Infrastructure/BotSharp.Logger/Hooks/RateLimitConversationHook.cs @@ -25,7 +25,10 @@ public class RateLimitConversationHook : ConversationHookBase public override async Task OnMessageReceived(RoleDialogModel message) { var settings = _services.GetRequiredService(); + var states = _services.GetRequiredService(); + var rateLimit = settings.RateLimit; + var channel = states.GetState("channel"); // Check max input length var charCount = message.Content.Length; @@ -45,7 +48,7 @@ public class RateLimitConversationHook : ConversationHookBase var userSents = Dialogs.Where(x => x.Role == AgentRole.User) .TakeLast(2).ToList(); - if (userSents.Count > 1) + if (channel != ConversationChannel.Phone && userSents.Count > 1) { var seconds = (DateTime.UtcNow - userSents.First().CreatedAt).TotalSeconds; if (seconds < rateLimit.MinTimeSecondsBetweenMessages) @@ -56,9 +59,6 @@ public class RateLimitConversationHook : ConversationHookBase } } - var states = _services.GetRequiredService(); - var channel = states.GetState("channel"); - // Check the number of conversations if (channel != ConversationChannel.Phone && channel != ConversationChannel.Email && channel != ConversationChannel.Database) { From 81bb38b9b3137c36d1fa3235177a1059de200b30 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 9 Jul 2025 11:14:47 -0500 Subject: [PATCH 13/24] refine side car --- .../SideCar/Attributes/SideCarAttribute.cs | 4 ++-- .../SideCar/IConversationSideCar.cs | 2 +- .../Services/BotSharpConversationSideCar.cs | 14 +++++++------- .../Services/ConversationStateService.cs | 4 ++-- .../Hooks/ChatHubConversationHook.cs | 2 +- .../Controllers/TwilioInboundController.cs | 9 +++++---- 6 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/SideCar/Attributes/SideCarAttribute.cs b/src/Infrastructure/BotSharp.Abstraction/SideCar/Attributes/SideCarAttribute.cs index 833cb013..4426d71f 100644 --- a/src/Infrastructure/BotSharp.Abstraction/SideCar/Attributes/SideCarAttribute.cs +++ b/src/Infrastructure/BotSharp.Abstraction/SideCar/Attributes/SideCarAttribute.cs @@ -79,7 +79,7 @@ public class SideCarAttribute : AsyncMoAttribute object? res = null; var isHandled = false; - var enabled = instance != null && instance.IsEnabled() && method != null; + var enabled = instance != null && instance.IsEnabled && method != null; if (!enabled) { return (isHandled, value); @@ -112,7 +112,7 @@ public class SideCarAttribute : AsyncMoAttribute object? value = null; var isHandled = false; - var enabled = instance != null && instance.IsEnabled() && method != null; + var enabled = instance != null && instance.IsEnabled && method != null; if (!enabled) { return (isHandled, value); diff --git a/src/Infrastructure/BotSharp.Abstraction/SideCar/IConversationSideCar.cs b/src/Infrastructure/BotSharp.Abstraction/SideCar/IConversationSideCar.cs index 3c9bcfa4..81b0cf9c 100644 --- a/src/Infrastructure/BotSharp.Abstraction/SideCar/IConversationSideCar.cs +++ b/src/Infrastructure/BotSharp.Abstraction/SideCar/IConversationSideCar.cs @@ -5,8 +5,8 @@ namespace BotSharp.Abstraction.SideCar; public interface IConversationSideCar { string Provider { get; } + bool IsEnabled { get; } - bool IsEnabled(); void AppendConversationDialogs(string conversationId, List messages); List GetConversationDialogs(string conversationId); void UpdateConversationBreakpoint(string conversationId, ConversationBreakpoint breakpoint); diff --git a/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs b/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs index e4996b06..d636fa71 100644 --- a/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs +++ b/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs @@ -14,7 +14,6 @@ limitations under the License. ******************************************************************************/ -using BotSharp.Abstraction.SideCar.Models; using BotSharp.Core.Infrastructures; namespace BotSharp.Core.SideCar.Services; @@ -31,6 +30,7 @@ public class BotSharpConversationSideCar : IConversationSideCar private string _conversationId = string.Empty; public string Provider => "botsharp"; + public bool IsEnabled => _enabled; public BotSharpConversationSideCar( IServiceProvider services, @@ -40,11 +40,6 @@ public class BotSharpConversationSideCar : IConversationSideCar _logger = logger; } - public bool IsEnabled() - { - return _enabled; - } - public void AppendConversationDialogs(string conversationId, List messages) { if (!IsValid(conversationId)) @@ -99,17 +94,22 @@ public class BotSharpConversationSideCar : IConversationSideCar top.State = new ConversationState(states); } - public async Task SendMessage(string agentId, string text, + public async Task SendMessage( + string agentId, + string text, PostbackMessageModel? postback = null, List? states = null, List? dialogs = null, SideCarOptions? options = null) { _sideCarOptions = options; + _logger.LogInformation($"Entering side car conversation..."); BeforeExecute(dialogs); var response = await InnerExecute(agentId, text, postback, states); AfterExecute(); + + _logger.LogInformation($"Existing side car conversation..."); return response; } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index ff61fde2..4c8178d8 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -159,7 +159,7 @@ public class ConversationStateService : IConversationStateService Reset(); var endNodes = new Dictionary(); - if (_sidecar?.IsEnabled() == true) + if (_sidecar?.IsEnabled == true) { return endNodes; } @@ -234,7 +234,7 @@ public class ConversationStateService : IConversationStateService public void Save() { - if (_conversationId == null || _sidecar?.IsEnabled() == true) + if (_conversationId == null || _sidecar?.IsEnabled == true) { return; } diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs index a41abe87..3c7a675d 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs @@ -179,7 +179,7 @@ public class ChatHubConversationHook : ConversationHookBase private bool AllowSendingMessage() { var sidecar = _services.GetService(); - return sidecar == null || !sidecar.IsEnabled(); + return sidecar == null || !sidecar.IsEnabled; } private async Task InitClientConversation(string conversationId, ConversationDto conversation) diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs index 31d3db52..de58dca6 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs @@ -53,28 +53,29 @@ public class TwilioInboundController : TwilioController instruction.SpeechPaths.Add(request.InitAudioFile); } + // Before creating session await HookEmitter.Emit(_services, async hook => { await hook.OnSessionCreating(request, instruction); }, request.AgentId); + var (agent, conversationId) = await InitConversation(request); request.ConversationId = conversationId.Id; instruction.AgentId = request.AgentId; instruction.ConversationId = request.ConversationId; + + // After creating session await HookEmitter.Emit(_services, async hook => { await hook.OnSessionCreated(request); }, request.AgentId); - var conv = _services.GetRequiredService(); - conv.SaveStates(); if (twilio.MachineDetected(request)) { response = new VoiceResponse(); - await HookEmitter.Emit(_services, async hook => await hook.OnVoicemailStarting(request), request.AgentId); @@ -122,7 +123,7 @@ public class TwilioInboundController : TwilioController await Task.Delay(1500); await twilio.StartRecording(request.CallSid, request.AgentId, request.ConversationId); }); - } + } return TwiML(response); } From 764bbefbfa36b24394e5d62e31684ed973ac7230 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 9 Jul 2025 15:23:55 -0500 Subject: [PATCH 14/24] refine chat stream with init states --- .../Realtime/IRealtimeHub.cs | 2 +- .../Services/RealtimeHub.cs | 5 ++-- .../ChatStreamMiddleware.cs | 29 ++++++++++++++----- .../Models/Stream/ChatStreamEventResponse.cs | 7 ++--- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHub.cs b/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHub.cs index cad64038..9d64b678 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHub.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHub.cs @@ -13,5 +13,5 @@ public interface IRealtimeHub IRealTimeCompletion Completer { get; } - Task ConnectToModel(Func? responseToUser = null, Func? init = null); + Task ConnectToModel(Func? responseToUser = null, Func? init = null, List? initStates = null); } diff --git a/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs b/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs index baac131c..32b0a112 100644 --- a/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs +++ b/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs @@ -1,5 +1,6 @@ using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Hooks; +using BotSharp.Abstraction.Models; using BotSharp.Abstraction.Options; using BotSharp.Core.Infrastructures; @@ -22,10 +23,10 @@ public class RealtimeHub : IRealtimeHub _logger = logger; } - public async Task ConnectToModel(Func? responseToUser = null, Func? init = null) + public async Task ConnectToModel(Func? responseToUser = null, Func? init = null, List? initStates = null) { var convService = _services.GetRequiredService(); - convService.SetConversationId(_conn.ConversationId, []); + convService.SetConversationId(_conn.ConversationId, initStates ?? []); var conversation = await convService.GetConversation(_conn.ConversationId); var routing = _services.GetRequiredService(); diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs b/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs index 171bf0f2..7079c642 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.Models; using BotSharp.Abstraction.Realtime.Models.Session; using BotSharp.Core.Session; using Microsoft.AspNetCore.Http; @@ -66,6 +67,7 @@ public class ChatStreamMiddleware // load conversation and state var convService = services.GetRequiredService(); + var state = services.GetRequiredService(); convService.SetConversationId(conversationId, []); await convService.GetConversationRecordOrCreateNew(agentId); @@ -80,7 +82,8 @@ public class ChatStreamMiddleware var (eventType, data) = MapEvents(conn, receivedText); if (eventType == "start") { - await ConnectToModel(hub, webSocket); + var states = InitStates(data); + await ConnectToModel(hub, webSocket, states); } else if (eventType == "media") { @@ -96,25 +99,26 @@ public class ChatStreamMiddleware } } + convService.SaveStates(); await _session.DisconnectAsync(); _session.Dispose(); } - private async Task ConnectToModel(IRealtimeHub hub, WebSocket webSocket) + private async Task ConnectToModel(IRealtimeHub hub, WebSocket webSocket, List? states = null) { - await hub.ConnectToModel(async data => + await hub.ConnectToModel(responseToUser: async data => { if (_session != null) { await _session.SendEventAsync(data); } - }); + }, initStates: states); } private (string, string) MapEvents(RealtimeHubConnection conn, string receivedText) { var response = JsonSerializer.Deserialize(receivedText); - string data = string.Empty; + var data = response?.Body?.Payload ?? string.Empty; switch (response.Event) { @@ -122,8 +126,6 @@ public class ChatStreamMiddleware conn.ResetStreamState(); break; case "media": - var mediaResponse = JsonSerializer.Deserialize(receivedText); - data = mediaResponse?.Body?.Payload ?? string.Empty; break; case "disconnect": break; @@ -154,4 +156,17 @@ public class ChatStreamMiddleware @event = "clear" }); } + + private List InitStates(string data) + { + try + { + var states = JsonSerializer.Deserialize>(data, BotSharpOptions.defaultJsonOptions); + return states ?? []; + } + catch + { + return []; + } + } } diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Models/Stream/ChatStreamEventResponse.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Models/Stream/ChatStreamEventResponse.cs index aee742df..022ebdf1 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Models/Stream/ChatStreamEventResponse.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Models/Stream/ChatStreamEventResponse.cs @@ -6,15 +6,12 @@ internal class ChatStreamEventResponse { [JsonPropertyName("event")] public string Event { get; set; } -} -internal class ChatStreamMediaEventResponse : ChatStreamEventResponse -{ [JsonPropertyName("body")] - public MediaEventResponseBody Body { get; set; } + public ChatStreamEventResponseBody Body { get; set; } } -internal class MediaEventResponseBody +internal class ChatStreamEventResponseBody { [JsonPropertyName("payload")] public string Payload { get; set; } From 16300b9a1d855c833d10aceec436d56a31e7e26b Mon Sep 17 00:00:00 2001 From: Visagan Guruparan <103048@smsassist.com> Date: Thu, 10 Jul 2025 14:22:54 -0500 Subject: [PATCH 15/24] Add hook to handle locate element for PlayWright --- .../BotSharp.Abstraction/Browsing/IWebDriverHook.cs | 1 + .../PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs | 5 +++++ .../UtilFunctions/UtilWebLocateElementFn.cs | 1 + 3 files changed, 7 insertions(+) diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebDriverHook.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebDriverHook.cs index ce5d03b6..346f2f23 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebDriverHook.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebDriverHook.cs @@ -5,4 +5,5 @@ namespace BotSharp.Abstraction.Browsing; public interface IWebDriverHook { Task> GetUploadFiles(MessageInfo message); + Task OnLocateElement(MessageInfo message, string content); } 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 e70d7f27..b4b2eb2f 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs @@ -114,6 +114,11 @@ public partial class PlaywrightWebDriver // fix if html has & result.Body = HttpUtility.HtmlDecode(html); result.IsSuccess = true; + var hooks = _services.GetServices(); + foreach (var hook in hooks) + { + await hook.OnLocateElement(message, result.Body); + } } else if (count > 1) { diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebLocateElementFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebLocateElementFn.cs index d30048c8..e2e3d34f 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebLocateElementFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebLocateElementFn.cs @@ -30,6 +30,7 @@ public class UtilWebLocateElementFn : IFunctionCallback MessageId = message.MessageId, ContextId = webDriverService.GetMessageContext(message) }; + browser.SetServiceProvider(_services); var result = await browser.LocateElement(msg, locatorArgs); message.Content = $"Locating element {(result.IsSuccess ? "success" : "failed")}. "; From b9c7f63249ea81e2f6b997dbfd3839c0b13032d3 Mon Sep 17 00:00:00 2001 From: Visagan Guruparan <103048@smsassist.com> Date: Thu, 10 Jul 2025 15:55:55 -0500 Subject: [PATCH 16/24] Update contextId for close browser utility. --- .../UtilFunctions/UtilWebCloseBrowserFn.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebCloseBrowserFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebCloseBrowserFn.cs index ff898232..6c7bfefa 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebCloseBrowserFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebCloseBrowserFn.cs @@ -28,7 +28,7 @@ public class UtilWebCloseBrowserFn : IFunctionCallback ContextId = webDriverService.GetMessageContext(message) }; - await browser.CloseBrowser(message.CurrentAgentId); + await browser.CloseBrowser(msg.ContextId); message.Content = $"Browser closed."; From c80e8ca7a04daf1b7dce003bcd4c29fc45cdb3e3 Mon Sep 17 00:00:00 2001 From: "guoqiang.yu" Date: Fri, 11 Jul 2025 12:59:09 +0800 Subject: [PATCH 17/24] fix #1097 SqlDriver: Database type mssql is not supported --- .../BotSharp.Plugin.SqlDriver/Functions/ExecuteQueryFn.cs | 2 +- .../BotSharp.Plugin.SqlDriver/Functions/GetTableDefinitionFn.cs | 2 +- src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlSelect.cs | 2 +- .../BotSharp.Plugin.SqlDriver/Functions/SqlValidateFn.cs | 2 +- .../UtilFunctions/GetTableDefinitionFn.cs | 2 +- .../BotSharp.Plugin.SqlDriver/UtilFunctions/SqlSelect.cs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/ExecuteQueryFn.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/ExecuteQueryFn.cs index f571fa6c..c2f0682b 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/ExecuteQueryFn.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/ExecuteQueryFn.cs @@ -33,7 +33,7 @@ public class ExecuteQueryFn : IFunctionCallback var results = dbType.ToLower() switch { "mysql" => RunQueryInMySql(args.SqlStatements), - "sqlserver" => RunQueryInSqlServer(args.SqlStatements), + "sqlserver" or "mssql" => RunQueryInSqlServer(args.SqlStatements), "redshift" => RunQueryInRedshift(args.SqlStatements), _ => throw new NotImplementedException($"Database type {dbType} is not supported.") }; diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/GetTableDefinitionFn.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/GetTableDefinitionFn.cs index 283620c9..25ef526a 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/GetTableDefinitionFn.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/GetTableDefinitionFn.cs @@ -37,7 +37,7 @@ public class GetTableDefinitionFn : IFunctionCallback var tableDdls = dbType switch { "mysql" => GetDdlFromMySql(tables), - "sqlserver" => GetDdlFromSqlServer(tables), + "sqlserver" or "mssql" => GetDdlFromSqlServer(tables), "redshift" => GetDdlFromRedshift(tables), _ => throw new NotImplementedException($"Database type {dbType} is not supported.") }; diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlSelect.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlSelect.cs index eb9253e4..3e693f80 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlSelect.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlSelect.cs @@ -32,7 +32,7 @@ public class SqlSelect : IFunctionCallback var result = dbType switch { "mysql" => RunQueryInMySql(args), - "sqlserver" => RunQueryInSqlServer(args), + "sqlserver" or "mssql" => RunQueryInSqlServer(args), "redshift" => RunQueryInRedshift(args), _ => throw new NotImplementedException($"Database type {dbType} is not supported.") }; diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlValidateFn.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlValidateFn.cs index 89b38f93..1489c4a8 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlValidateFn.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlValidateFn.cs @@ -34,7 +34,7 @@ public class SqlValidateFn : IFunctionCallback var validateSql = dbType.ToLower() switch { "mysql" => $"EXPLAIN\r\n{sql.Replace("SET ", "-- SET ", StringComparison.InvariantCultureIgnoreCase).Replace(";", "; EXPLAIN ").TrimEnd("EXPLAIN ".ToCharArray())}", - "sqlserver" => $"SET PARSEONLY ON;\r\n{sql}\r\nSET PARSEONLY OFF;", + "sqlserver" or "mssql" => $"SET PARSEONLY ON;\r\n{sql}\r\nSET PARSEONLY OFF;", "redshift" => $"explain\r\n{sql}", _ => throw new NotImplementedException($"Database type {dbType} is not supported.") }; diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/UtilFunctions/GetTableDefinitionFn.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/UtilFunctions/GetTableDefinitionFn.cs index 20f35572..43005c68 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/UtilFunctions/GetTableDefinitionFn.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/UtilFunctions/GetTableDefinitionFn.cs @@ -31,7 +31,7 @@ public class GetTableDefinitionFn : IFunctionCallback var tableDdls = dbType switch { "mysql" => GetDdlFromMySql(tables), - "sqlserver" => GetDdlFromSqlServer(tables), + "sqlserver" or "mssql" => GetDdlFromSqlServer(tables), "redshift" => GetDdlFromRedshift(tables,schema), _ => throw new NotImplementedException($"Database type {dbType} is not supported.") }; diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/UtilFunctions/SqlSelect.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/UtilFunctions/SqlSelect.cs index 1ae27ac7..72bef238 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/UtilFunctions/SqlSelect.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/UtilFunctions/SqlSelect.cs @@ -30,7 +30,7 @@ public class SqlSelect : IFunctionCallback var result = dbType switch { "mysql" => RunQueryInMySql(args), - "sqlserver" => RunQueryInSqlServer(args), + "sqlserver" or "mssql" => RunQueryInSqlServer(args), "redshift" => RunQueryInRedshift(args), _ => throw new NotImplementedException($"Database type {dbType} is not supported.") }; From 2ec304b83c41f10ada6af3b7378e25677c92092c Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Fri, 11 Jul 2025 10:15:09 -0500 Subject: [PATCH 18/24] refine chat stream request --- .../BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs | 11 +++++------ .../Models/Stream/ChatStreamRequest.cs | 10 ++++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 src/Plugins/BotSharp.Plugin.ChatHub/Models/Stream/ChatStreamRequest.cs diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs b/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs index 7079c642..138d5084 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs @@ -82,8 +82,8 @@ public class ChatStreamMiddleware var (eventType, data) = MapEvents(conn, receivedText); if (eventType == "start") { - var states = InitStates(data); - await ConnectToModel(hub, webSocket, states); + var request = InitRequest(data); + await ConnectToModel(hub, webSocket, request?.States); } else if (eventType == "media") { @@ -157,16 +157,15 @@ public class ChatStreamMiddleware }); } - private List InitStates(string data) + private ChatStreamRequest? InitRequest(string data) { try { - var states = JsonSerializer.Deserialize>(data, BotSharpOptions.defaultJsonOptions); - return states ?? []; + return JsonSerializer.Deserialize(data, BotSharpOptions.defaultJsonOptions); } catch { - return []; + return null; } } } diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Models/Stream/ChatStreamRequest.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Models/Stream/ChatStreamRequest.cs new file mode 100644 index 00000000..1a72d7dc --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Models/Stream/ChatStreamRequest.cs @@ -0,0 +1,10 @@ +using BotSharp.Abstraction.Models; +using System.Text.Json.Serialization; + +namespace BotSharp.Plugin.ChatHub.Models.Stream; + +public class ChatStreamRequest +{ + [JsonPropertyName("states")] + public List States { get; set; } = []; +} From 0d0d808d44e64a070b13fb4b7a5c3e2bfb0baae1 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Fri, 11 Jul 2025 11:34:43 -0500 Subject: [PATCH 19/24] add default result --- .../BotSharp.Abstraction/Browsing/IWebDriverHook.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebDriverHook.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebDriverHook.cs index 346f2f23..30a58bb4 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebDriverHook.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebDriverHook.cs @@ -4,6 +4,6 @@ namespace BotSharp.Abstraction.Browsing; public interface IWebDriverHook { - Task> GetUploadFiles(MessageInfo message); - Task OnLocateElement(MessageInfo message, string content); + Task> GetUploadFiles(MessageInfo message) => Task.FromResult(new List()); + Task OnLocateElement(MessageInfo message, string content) => Task.CompletedTask; } From a4530093940fd25fd7a465acae8ffb390906754d Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 14 Jul 2025 12:40:59 -0500 Subject: [PATCH 20/24] refine message states --- .../Models/MessageState.cs | 8 +++- .../Services/ConversationService.cs | 2 +- .../Hooks/RateLimitConversationHook.cs | 3 -- .../Controllers/TwilioInboundController.cs | 12 +++--- .../Functions/OutboundPhoneCallFn.cs | 39 ++++++++++++------- 5 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Models/MessageState.cs b/src/Infrastructure/BotSharp.Abstraction/Models/MessageState.cs index a8709810..94ab82ea 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Models/MessageState.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Models/MessageState.cs @@ -8,20 +8,24 @@ public class MessageState [JsonPropertyName("active_rounds")] public int ActiveRounds { get; set; } = -1; + [JsonPropertyName("global")] + public bool Global { get; set; } + public MessageState() { } - public MessageState(string key, object value, int activeRounds = -1) + public MessageState(string key, object value, int activeRounds = -1, bool global = false) { Key = key; Value = value; ActiveRounds = activeRounds; + Global = global; } public override string ToString() { - return $"Key: {Key} => Value: {Value}, ActiveRounds: {ActiveRounds}"; + return $"Key: {Key} => Value: {Value}, ActiveRounds: {ActiveRounds}, Global: {Global}"; } } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs index 6eaeb167..676ea362 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs @@ -178,7 +178,7 @@ public partial class ConversationService : IConversationService { _conversationId = conversationId; _state.Load(_conversationId, isReadOnly); - states.ForEach(x => _state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); + states.ForEach(x => _state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, isNeedVersion: !x.Global, source: StateSource.External)); } public async Task GetConversationRecordOrCreateNew(string agentId) diff --git a/src/Infrastructure/BotSharp.Logger/Hooks/RateLimitConversationHook.cs b/src/Infrastructure/BotSharp.Logger/Hooks/RateLimitConversationHook.cs index 4752f6d6..02cafbd0 100644 --- a/src/Infrastructure/BotSharp.Logger/Hooks/RateLimitConversationHook.cs +++ b/src/Infrastructure/BotSharp.Logger/Hooks/RateLimitConversationHook.cs @@ -1,9 +1,6 @@ using BotSharp.Abstraction.Agents.Enums; using BotSharp.Abstraction.Conversations.Enums; using BotSharp.Abstraction.Repositories.Filters; -using BotSharp.Abstraction.Statistics.Enums; -using BotSharp.Abstraction.Statistics.Models; -using BotSharp.Abstraction.Statistics.Services; using BotSharp.Abstraction.Users; namespace BotSharp.Logger.Hooks; diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs index de58dca6..109db566 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs @@ -166,16 +166,16 @@ public class TwilioInboundController : TwilioController var states = new List { - new("channel", ConversationChannel.Phone), - new("calling_phone", request.From), - new("phone_direction", request.Direction), - new("twilio_call_sid", request.CallSid), + new("channel", ConversationChannel.Phone, global: true), + new("calling_phone", request.From, global: true), + new("phone_direction", request.Direction, global: true), + new("twilio_call_sid", request.CallSid, global: true), }; if (request.Direction == "inbound") { - states.Add(new MessageState("calling_phone_from", request.From)); - states.Add(new MessageState("calling_phone_to", request.To)); + states.Add(new MessageState("calling_phone_from", request.From, global: true)); + states.Add(new MessageState("calling_phone_to", request.To, global: true)); } var requestStates = ParseStates(request.States); diff --git a/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/OutboundPhoneCallFn.cs b/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/OutboundPhoneCallFn.cs index 8d34dd22..4f3b2364 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/OutboundPhoneCallFn.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/OutboundPhoneCallFn.cs @@ -9,6 +9,7 @@ using BotSharp.Plugin.Twilio.Interfaces; using BotSharp.Plugin.Twilio.Models; using BotSharp.Plugin.Twilio.OutboundPhoneCallHandler.LlmContexts; using Twilio.Rest.Api.V2010.Account; +using Twilio.TwiML.Messaging; using Twilio.Types; using Conversation = BotSharp.Abstraction.Conversations.Models.Conversation; using Task = System.Threading.Tasks.Task; @@ -176,7 +177,7 @@ public class OutboundPhoneCallFn : IFunctionCallback }); var utcNow = DateTime.UtcNow; - var excludStates = new List + var excludeStates = new List { "provider", "model", @@ -185,22 +186,34 @@ public class OutboundPhoneCallFn : IFunctionCallback "llm_total_cost" }; - var curStates = state.GetStates().Select(x => new MessageState(x.Key, x.Value)).ToList(); + var curConvStates = state.GetStates().Select(x => new MessageState(x.Key, x.Value)).ToList(); var subConvStates = new List { - new(StateConst.ORIGIN_CONVERSATION_ID, originConversationId), - new("channel", "phone"), - new("phone_from", call.From), - new("phone_direction", call.Direction), - new("phone_number", call.To), - new("twilio_call_sid", call.Sid) + new(StateConst.ORIGIN_CONVERSATION_ID, originConversationId, global: true), + new("channel", "phone", global: true), + new("phone_from", call.From, global: true), + new("phone_direction", call.Direction, global: true), + new("phone_number", call.To, global: true), + new("twilio_call_sid", call.Sid, global: true) }; var subStateKeys = subConvStates.Select(x => x.Key).ToList(); - var included = curStates.Where(x => !subStateKeys.Contains(x.Key) && !excludStates.Contains(x.Key)); - var newStates = subConvStates.Concat(included).Select(x => new StateKeyValue + var included = curConvStates.Where(x => !subStateKeys.Contains(x.Key) && !excludeStates.Contains(x.Key)); + + var mappedCurConvStates = MapStates(included, messageId, utcNow); + var mappedSubConvStates = MapStates(subConvStates, messageId, utcNow); + var totalStates = mappedCurConvStates.Concat(mappedSubConvStates).ToList(); + + db.UpdateConversationStates(newConversationId, totalStates); + } + + private IEnumerable MapStates(IEnumerable states, string messageId, DateTime updateTime) + { + if (states.IsNullOrEmpty()) return []; + + return states.Select(x => new StateKeyValue { Key = x.Key, - Versioning = true, + Versioning = !x.Global, Values = [ new StateValue { @@ -209,11 +222,9 @@ public class OutboundPhoneCallFn : IFunctionCallback Active = true, ActiveRounds = x.ActiveRounds, Source = StateSource.Application, - UpdateTime = utcNow + UpdateTime = updateTime } ] }).ToList(); - - db.UpdateConversationStates(newConversationId, newStates); } } From 85a87eda7a0d9ba7ebed26dcd0c413d9c4b9add7 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 14 Jul 2025 12:51:59 -0500 Subject: [PATCH 21/24] rename --- .../BotSharp.Abstraction/Models/MessageState.cs | 4 ++-- .../Controllers/TwilioInboundController.cs | 12 ++++++------ .../Functions/OutboundPhoneCallFn.cs | 12 ++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Models/MessageState.cs b/src/Infrastructure/BotSharp.Abstraction/Models/MessageState.cs index 94ab82ea..074be848 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Models/MessageState.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Models/MessageState.cs @@ -16,12 +16,12 @@ public class MessageState } - public MessageState(string key, object value, int activeRounds = -1, bool global = false) + public MessageState(string key, object value, int activeRounds = -1, bool isGlobal = false) { Key = key; Value = value; ActiveRounds = activeRounds; - Global = global; + Global = isGlobal; } public override string ToString() diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs index 109db566..b5dbfb2e 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs @@ -166,16 +166,16 @@ public class TwilioInboundController : TwilioController var states = new List { - new("channel", ConversationChannel.Phone, global: true), - new("calling_phone", request.From, global: true), - new("phone_direction", request.Direction, global: true), - new("twilio_call_sid", request.CallSid, global: true), + new("channel", ConversationChannel.Phone, isGlobal: true), + new("calling_phone", request.From, isGlobal: true), + new("phone_direction", request.Direction, isGlobal: true), + new("twilio_call_sid", request.CallSid, isGlobal: true), }; if (request.Direction == "inbound") { - states.Add(new MessageState("calling_phone_from", request.From, global: true)); - states.Add(new MessageState("calling_phone_to", request.To, global: true)); + states.Add(new MessageState("calling_phone_from", request.From, isGlobal: true)); + states.Add(new MessageState("calling_phone_to", request.To, isGlobal: true)); } var requestStates = ParseStates(request.States); diff --git a/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/OutboundPhoneCallFn.cs b/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/OutboundPhoneCallFn.cs index 4f3b2364..02f000c2 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/OutboundPhoneCallFn.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/OutboundPhoneCallFn.cs @@ -189,12 +189,12 @@ public class OutboundPhoneCallFn : IFunctionCallback var curConvStates = state.GetStates().Select(x => new MessageState(x.Key, x.Value)).ToList(); var subConvStates = new List { - new(StateConst.ORIGIN_CONVERSATION_ID, originConversationId, global: true), - new("channel", "phone", global: true), - new("phone_from", call.From, global: true), - new("phone_direction", call.Direction, global: true), - new("phone_number", call.To, global: true), - new("twilio_call_sid", call.Sid, global: true) + new(StateConst.ORIGIN_CONVERSATION_ID, originConversationId, isGlobal: true), + new("channel", "phone", isGlobal: true), + new("phone_from", call.From, isGlobal: true), + new("phone_direction", call.Direction, isGlobal: true), + new("phone_number", call.To, isGlobal: true), + new("twilio_call_sid", call.Sid, isGlobal: true) }; var subStateKeys = subConvStates.Select(x => x.Key).ToList(); var included = curConvStates.Where(x => !subStateKeys.Contains(x.Key) && !excludeStates.Contains(x.Key)); From 0198fbccbbae068b30f021a99c96e6591d19c64d Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 14 Jul 2025 12:54:20 -0500 Subject: [PATCH 22/24] minor change --- .../OutboundPhoneCallHandler/Functions/OutboundPhoneCallFn.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/OutboundPhoneCallFn.cs b/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/OutboundPhoneCallFn.cs index 02f000c2..3c5532bd 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/OutboundPhoneCallFn.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/OutboundPhoneCallFn.cs @@ -201,9 +201,9 @@ public class OutboundPhoneCallFn : IFunctionCallback var mappedCurConvStates = MapStates(included, messageId, utcNow); var mappedSubConvStates = MapStates(subConvStates, messageId, utcNow); - var totalStates = mappedCurConvStates.Concat(mappedSubConvStates).ToList(); + var allStates = mappedCurConvStates.Concat(mappedSubConvStates).ToList(); - db.UpdateConversationStates(newConversationId, totalStates); + db.UpdateConversationStates(newConversationId, allStates); } private IEnumerable MapStates(IEnumerable states, string messageId, DateTime updateTime) From 3d1c3c0daa5e055b6230bbce96bd57970873dd6a Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 14 Jul 2025 16:59:09 -0500 Subject: [PATCH 23/24] refine conv filter --- .../Repositories/Filters/ConversationFilter.cs | 1 + .../FileRepository/FileRepository.Conversation.cs | 10 ++++++++-- .../Repository/MongoRepository.Conversation.cs | 10 ++++++++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs index 9ba98c5d..b94876c3 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs @@ -10,6 +10,7 @@ public class ConversationFilter public string? Title { get; set; } public string? TitleAlias { get; set; } public string? AgentId { get; set; } + public List? AgentIds { get; set; } public string? Status { get; set; } public string? Channel { get; set; } public string? ChannelId { get; set; } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs index 3d03e816..f772acb2 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs @@ -396,6 +396,12 @@ public partial class FileRepository Directory.CreateDirectory(dir); } + if (filter?.AgentId != null) + { + filter.AgentIds ??= []; + filter.AgentIds.Add(filter.AgentId); + } + var totalDirs = Directory.GetDirectories(dir); foreach (var d in totalDirs) { @@ -419,9 +425,9 @@ public partial class FileRepository { matched = matched && record.TitleAlias.Contains(filter.TitleAlias); } - if (filter?.AgentId != null) + if (filter?.AgentIds != null && filter.AgentIds.Any()) { - matched = matched && record.AgentId == filter.AgentId; + matched = matched && filter.AgentIds.Contains(record.AgentId); } if (filter?.Status != null) { diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs index edcf3a5f..d0da6fbf 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs @@ -346,6 +346,12 @@ public partial class MongoRepository var convBuilder = Builders.Filter; var convFilters = new List>() { convBuilder.Empty }; + if (filter?.AgentId != null) + { + filter.AgentIds ??= []; + filter.AgentIds.Add(filter.AgentId); + } + // Filter conversations if (!string.IsNullOrEmpty(filter?.Id)) { @@ -359,9 +365,9 @@ public partial class MongoRepository { convFilters.Add(convBuilder.Regex(x => x.Title, new BsonRegularExpression(filter.TitleAlias, "i"))); } - if (!string.IsNullOrEmpty(filter?.AgentId)) + if (filter?.AgentIds != null && filter.AgentIds.Any()) { - convFilters.Add(convBuilder.Eq(x => x.AgentId, filter.AgentId)); + convFilters.Add(convBuilder.In(x => x.AgentId, filter.AgentIds)); } if (!string.IsNullOrEmpty(filter?.Status)) { From 1662dde4196c7f4f7616b25935ed60293d8bd3f8 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 14 Jul 2025 17:31:03 -0500 Subject: [PATCH 24/24] minor change --- .../SideCar/Models/SideCarOptions.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/SideCar/Models/SideCarOptions.cs b/src/Infrastructure/BotSharp.Abstraction/SideCar/Models/SideCarOptions.cs index a4858c5c..f221cab2 100644 --- a/src/Infrastructure/BotSharp.Abstraction/SideCar/Models/SideCarOptions.cs +++ b/src/Infrastructure/BotSharp.Abstraction/SideCar/Models/SideCarOptions.cs @@ -7,6 +7,15 @@ public class SideCarOptions public static SideCarOptions Empty() { - return new SideCarOptions(); + return new(); + } + + public static SideCarOptions InheritStates(IEnumerable? targetStates = null) + { + return new() + { + IsInheritStates = true, + InheritStateKeys = targetStates + }; } }