Merge branch 'master' of https://github.com/SciSharp/BotSharp into features/add-chart-handler

This commit is contained in:
Jicheng Lu 2025-07-08 17:12:13 -05:00
commit 249fd09675
8 changed files with 78 additions and 16 deletions

View file

@ -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<StateKeyValue> states);
Task<RoleDialogModel> SendMessage(string agentId, string text,
PostbackMessageModel? postback = null, List<MessageState>? states = null, List<DialogElement>? dialogs = null);
PostbackMessageModel? postback = null,
List<MessageState>? states = null,
List<DialogElement>? dialogs = null,
SideCarOptions? options = null);
}

View file

@ -0,0 +1,12 @@
namespace BotSharp.Abstraction.SideCar.Models;
public class SideCarOptions
{
public bool IsInheritStates { get; set; }
public IEnumerable<string>? InheritStateKeys { get; set; }
public static SideCarOptions Empty()
{
return new SideCarOptions();
}
}

View file

@ -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<BotSharpConversationSideCar> _logger;
private Stack<ConversationContext> _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<RoleDialogModel> SendMessage(string agentId, string text,
PostbackMessageModel? postback = null, List<MessageState>? states = null, List<DialogElement>? dialogs = null)
PostbackMessageModel? postback = null,
List<MessageState>? states = null,
List<DialogElement>? dialogs = null,
SideCarOptions? options = null)
{
_sideCarOptions = options;
BeforeExecute(dialogs);
var response = await InnerExecute(agentId, text, postback, states);
AfterExecute();
@ -160,13 +167,11 @@ public class BotSharpConversationSideCar : IConversationSideCar
private void AfterExecute()
{
var state = _services.GetRequiredService<IConversationStateService>();
var routing = _services.GetRequiredService<IRoutingService>();
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 +186,43 @@ public class BotSharpConversationSideCar : IConversationSideCar
&& !string.IsNullOrEmpty(conversationId)
&& !string.IsNullOrEmpty(_conversationId);
}
private void RestoreStates(ConversationState prevStates)
{
var innerStates = prevStates;
var state = _services.GetRequiredService<IConversationStateService>();
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);
}
}

View file

@ -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;
global using BotSharp.Core.SideCar.Settings;

View file

@ -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<List<StateKeyValue>>(JsonSerializer.Serialize(values));
_curStates = new ConversationState(copy ?? []);
}

View file

@ -270,7 +270,7 @@ public class ChatCompletionProvider : IChatCompletion
{
messages.Add(new AssistantChatMessage(new List<ChatToolCall>
{
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));

View file

@ -615,10 +615,10 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
{
messages.Add(new AssistantChatMessage(new List<ChatToolCall>
{
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)
{

View file

@ -63,6 +63,11 @@ public class TwilioInboundController : TwilioController
instruction.AgentId = request.AgentId;
instruction.ConversationId = request.ConversationId;
await HookEmitter.Emit<ITwilioSessionHook>(_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<ITwilioSessionHook>(_services, async hook =>
{
await hook.OnSessionCreated(request);
}, request.AgentId);
}
return TwiML(response);
}