From 8d5b1402d014f03a8e08c4d5b476f3e582b33707 Mon Sep 17 00:00:00 2001 From: "nick.yi" Date: Fri, 27 Jun 2025 14:13:53 +0800 Subject: [PATCH 1/9] 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 2/9] 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 3/9] 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 4/9] 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 5/9] 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 6/9] 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 7/9] 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 94c3a86635b8e00ce040e0cdaec2615110164fee Mon Sep 17 00:00:00 2001 From: "nick.yi" Date: Tue, 8 Jul 2025 23:03:54 +0800 Subject: [PATCH 8/9] 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 9/9] 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]