namespace BotSharp.Core.SideCar.Services; public class BotSharpConversationSideCar : IConversationSideCar { private readonly IServiceProvider _services; private readonly ILogger _logger; private Stack contextStack = new(); private bool enabled = false; public string Provider => "botsharp"; public BotSharpConversationSideCar( IServiceProvider services, ILogger logger) { _services = services; _logger = logger; } public bool IsEnabled() { return enabled; } public void AppendConversationDialogs(string conversationId, List messages) { if (contextStack.IsNullOrEmpty()) return; var top = contextStack.Peek(); top.Dialogs.AddRange(messages); } public List GetConversationDialogs(string conversationId) { if (contextStack.IsNullOrEmpty()) { return new List(); } return contextStack.Peek().Dialogs; } public void UpdateConversationBreakpoint(string conversationId, ConversationBreakpoint breakpoint) { if (contextStack.IsNullOrEmpty()) return; var top = contextStack.Peek().Breakpoints; top.Add(breakpoint); } public ConversationBreakpoint? GetConversationBreakpoint(string conversationId) { if (contextStack.IsNullOrEmpty()) { return null; } var top = contextStack.Peek().Breakpoints; return top.LastOrDefault(); } public async Task SendMessage(string agentId, string text, PostbackMessageModel? postback = null, List? states = null) { BeforeExecute(); var response = await InnerExecute(agentId, text, postback, states); AfterExecute(); return response; } private async Task InnerExecute(string agentId, string text, PostbackMessageModel? postback = null, List? states = null) { var conv = _services.GetRequiredService(); var routing = _services.GetRequiredService(); var state = _services.GetRequiredService(); var inputMsg = new RoleDialogModel(AgentRole.User, text); routing.Context.SetMessageId(conv.ConversationId, inputMsg.MessageId); states?.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); var response = new RoleDialogModel(AgentRole.Assistant, string.Empty); await conv.SendMessage(agentId, inputMsg, replyMessage: postback, async msg => { response.Content = !string.IsNullOrEmpty(msg.SecondaryContent) ? msg.SecondaryContent : msg.Content; response.FunctionName = msg.FunctionName; response.RichContent = msg.SecondaryRichContent ?? msg.RichContent; response.Instruction = msg.Instruction; response.Data = msg.Data; }); return response; } private void BeforeExecute() { enabled = true; var state = _services.GetRequiredService(); var routing = _services.GetRequiredService(); var node = new ConversationContext { State = state.GetCurrentState(), Dialogs = new(), Breakpoints = new(), RecursiveCounter = routing.Context.GetRecursiveCounter(), RoutingStack = routing.Context.GetAgentStack() }; contextStack.Push(node); // Reset state.ResetCurrentState(); routing.Context.ResetRecursiveCounter(); routing.Context.ResetAgentStack(); } private void AfterExecute() { var state = _services.GetRequiredService(); var routing = _services.GetRequiredService(); var node = contextStack.Pop(); // Recover state.SetCurrentState(node.State); routing.Context.SetRecursiveCounter(node.RecursiveCounter); routing.Context.SetAgentStack(node.RoutingStack); enabled = false; } }