Merge branch 'master' of https://github.com/SciSharp/BotSharp into features/init-reactive-x
This commit is contained in:
commit
d41eb4a7e0
|
|
@ -2,7 +2,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<LangVersion>12.0</LangVersion>
|
<LangVersion>12.0</LangVersion>
|
||||||
<BotSharpVersion>5.0.0</BotSharpVersion>
|
<BotSharpVersion>5.1.0</BotSharpVersion>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
<GenerateDocumentationFile>false</GenerateDocumentationFile>
|
<GenerateDocumentationFile>false</GenerateDocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
||||||
|
|
@ -19,4 +19,9 @@ public class MessageState
|
||||||
Value = value;
|
Value = value;
|
||||||
ActiveRounds = activeRounds;
|
ActiveRounds = activeRounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"Key: {Key} => Value: {Value}, ActiveRounds: {ActiveRounds}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,16 +54,23 @@ public class SideCarAttribute : AsyncMoAttribute
|
||||||
|
|
||||||
private (IConversationSideCar?, MethodInfo?) GetSideCarMethod(IServiceProvider serviceProvider, string methodName, Type retType, object[] args)
|
private (IConversationSideCar?, MethodInfo?) GetSideCarMethod(IServiceProvider serviceProvider, string methodName, Type retType, object[] args)
|
||||||
{
|
{
|
||||||
var sidecar = serviceProvider.GetService<IConversationSideCar>();
|
try
|
||||||
var argTypes = args.Select(x => x.GetType()).ToArray();
|
{
|
||||||
var sidecarMethod = sidecar?.GetType()?.GetMethods(BindingFlags.Public | BindingFlags.Instance)
|
var sidecar = serviceProvider.GetService<IConversationSideCar>();
|
||||||
.FirstOrDefault(x => x.Name == methodName
|
var argTypes = args.Select(x => x.GetType()).ToArray();
|
||||||
&& x.ReturnType == retType
|
var sidecarMethod = sidecar?.GetType()?.GetMethods(BindingFlags.Public | BindingFlags.Instance)
|
||||||
&& x.GetParameters().Length == argTypes.Length
|
.FirstOrDefault(x => x.Name == methodName
|
||||||
&& x.GetParameters().Select(p => p.ParameterType)
|
&& x.ReturnType == retType
|
||||||
.Zip(argTypes, (paramType, argType) => paramType.IsAssignableFrom(argType)).All(y => y));
|
&& x.GetParameters().Length == argTypes.Length
|
||||||
|
&& x.GetParameters().Select(p => p.ParameterType)
|
||||||
|
.Zip(argTypes, (paramType, argType) => paramType.IsAssignableFrom(argType)).All(y => y));
|
||||||
|
|
||||||
return (sidecar, sidecarMethod);
|
return (sidecar, sidecarMethod);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<(bool, object?)> CallAsyncMethod(IConversationSideCar instance, MethodInfo method, Type retType, object[] args)
|
private async Task<(bool, object?)> CallAsyncMethod(IConversationSideCar instance, MethodInfo method, Type retType, object[] args)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
using BotSharp.Abstraction.SideCar.Models;
|
||||||
|
|
||||||
namespace BotSharp.Abstraction.SideCar;
|
namespace BotSharp.Abstraction.SideCar;
|
||||||
|
|
||||||
public interface IConversationSideCar
|
public interface IConversationSideCar
|
||||||
|
|
@ -9,6 +11,10 @@ public interface IConversationSideCar
|
||||||
List<DialogElement> GetConversationDialogs(string conversationId);
|
List<DialogElement> GetConversationDialogs(string conversationId);
|
||||||
void UpdateConversationBreakpoint(string conversationId, ConversationBreakpoint breakpoint);
|
void UpdateConversationBreakpoint(string conversationId, ConversationBreakpoint breakpoint);
|
||||||
ConversationBreakpoint? GetConversationBreakpoint(string conversationId);
|
ConversationBreakpoint? GetConversationBreakpoint(string conversationId);
|
||||||
|
void UpdateConversationStates(string conversationId, List<StateKeyValue> states);
|
||||||
Task<RoleDialogModel> SendMessage(string agentId, string text,
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
|
using BotSharp.Abstraction.SideCar.Models;
|
||||||
using BotSharp.Core.Infrastructures;
|
using BotSharp.Core.Infrastructures;
|
||||||
|
|
||||||
namespace BotSharp.Core.SideCar.Services;
|
namespace BotSharp.Core.SideCar.Services;
|
||||||
|
|
@ -23,9 +24,11 @@ public class BotSharpConversationSideCar : IConversationSideCar
|
||||||
private readonly IServiceProvider _services;
|
private readonly IServiceProvider _services;
|
||||||
private readonly ILogger<BotSharpConversationSideCar> _logger;
|
private readonly ILogger<BotSharpConversationSideCar> _logger;
|
||||||
|
|
||||||
private Stack<ConversationContext> contextStack = new();
|
private Stack<ConversationContext> _contextStack = new();
|
||||||
|
private SideCarOptions? _sideCarOptions;
|
||||||
|
|
||||||
private bool enabled = false;
|
private bool _enabled = false;
|
||||||
|
private string _conversationId = string.Empty;
|
||||||
|
|
||||||
public string Provider => "botsharp";
|
public string Provider => "botsharp";
|
||||||
|
|
||||||
|
|
@ -39,49 +42,71 @@ public class BotSharpConversationSideCar : IConversationSideCar
|
||||||
|
|
||||||
public bool IsEnabled()
|
public bool IsEnabled()
|
||||||
{
|
{
|
||||||
return enabled;
|
return _enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AppendConversationDialogs(string conversationId, List<DialogElement> messages)
|
public void AppendConversationDialogs(string conversationId, List<DialogElement> messages)
|
||||||
{
|
{
|
||||||
if (contextStack.IsNullOrEmpty()) return;
|
if (!IsValid(conversationId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var top = contextStack.Peek();
|
var top = _contextStack.Peek();
|
||||||
top.Dialogs.AddRange(messages);
|
top.Dialogs.AddRange(messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<DialogElement> GetConversationDialogs(string conversationId)
|
public List<DialogElement> GetConversationDialogs(string conversationId)
|
||||||
{
|
{
|
||||||
if (contextStack.IsNullOrEmpty())
|
if (!IsValid(conversationId))
|
||||||
{
|
{
|
||||||
return new List<DialogElement>();
|
return new List<DialogElement>();
|
||||||
}
|
}
|
||||||
|
|
||||||
return contextStack.Peek().Dialogs;
|
return _contextStack.Peek().Dialogs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateConversationBreakpoint(string conversationId, ConversationBreakpoint breakpoint)
|
public void UpdateConversationBreakpoint(string conversationId, ConversationBreakpoint breakpoint)
|
||||||
{
|
{
|
||||||
if (contextStack.IsNullOrEmpty()) return;
|
if (!IsValid(conversationId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var top = contextStack.Peek().Breakpoints;
|
var top = _contextStack.Peek().Breakpoints;
|
||||||
top.Add(breakpoint);
|
top.Add(breakpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConversationBreakpoint? GetConversationBreakpoint(string conversationId)
|
public ConversationBreakpoint? GetConversationBreakpoint(string conversationId)
|
||||||
{
|
{
|
||||||
if (contextStack.IsNullOrEmpty())
|
if (!IsValid(conversationId))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var top = contextStack.Peek().Breakpoints;
|
var top = _contextStack.Peek().Breakpoints;
|
||||||
return top.LastOrDefault();
|
return top.LastOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RoleDialogModel> SendMessage(string agentId, string text,
|
public void UpdateConversationStates(string conversationId, List<StateKeyValue> states)
|
||||||
PostbackMessageModel? postback = null, List<MessageState>? states = null, List<DialogElement>? dialogs = null)
|
|
||||||
{
|
{
|
||||||
|
if (!IsValid(conversationId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var top = _contextStack.Peek();
|
||||||
|
top.State = new ConversationState(states);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<RoleDialogModel> SendMessage(string agentId, string text,
|
||||||
|
PostbackMessageModel? postback = null,
|
||||||
|
List<MessageState>? states = null,
|
||||||
|
List<DialogElement>? dialogs = null,
|
||||||
|
SideCarOptions? options = null)
|
||||||
|
{
|
||||||
|
_sideCarOptions = options;
|
||||||
|
|
||||||
BeforeExecute(dialogs);
|
BeforeExecute(dialogs);
|
||||||
var response = await InnerExecute(agentId, text, postback, states);
|
var response = await InnerExecute(agentId, text, postback, states);
|
||||||
AfterExecute();
|
AfterExecute();
|
||||||
|
|
@ -94,6 +119,7 @@ public class BotSharpConversationSideCar : IConversationSideCar
|
||||||
var conv = _services.GetRequiredService<IConversationService>();
|
var conv = _services.GetRequiredService<IConversationService>();
|
||||||
var routing = _services.GetRequiredService<IRoutingService>();
|
var routing = _services.GetRequiredService<IRoutingService>();
|
||||||
var state = _services.GetRequiredService<IConversationStateService>();
|
var state = _services.GetRequiredService<IConversationStateService>();
|
||||||
|
_conversationId = conv.ConversationId;
|
||||||
|
|
||||||
var inputMsg = new RoleDialogModel(AgentRole.User, text);
|
var inputMsg = new RoleDialogModel(AgentRole.User, text);
|
||||||
routing.Context.SetMessageId(conv.ConversationId, inputMsg.MessageId);
|
routing.Context.SetMessageId(conv.ConversationId, inputMsg.MessageId);
|
||||||
|
|
@ -116,7 +142,7 @@ public class BotSharpConversationSideCar : IConversationSideCar
|
||||||
|
|
||||||
private void BeforeExecute(List<DialogElement>? dialogs)
|
private void BeforeExecute(List<DialogElement>? dialogs)
|
||||||
{
|
{
|
||||||
enabled = true;
|
_enabled = true;
|
||||||
var state = _services.GetRequiredService<IConversationStateService>();
|
var state = _services.GetRequiredService<IConversationStateService>();
|
||||||
var routing = _services.GetRequiredService<IRoutingService>();
|
var routing = _services.GetRequiredService<IRoutingService>();
|
||||||
|
|
||||||
|
|
@ -129,7 +155,7 @@ public class BotSharpConversationSideCar : IConversationSideCar
|
||||||
RecursiveCounter = routing.Context.GetRecursiveCounter(),
|
RecursiveCounter = routing.Context.GetRecursiveCounter(),
|
||||||
RoutingStack = routing.Context.GetAgentStack()
|
RoutingStack = routing.Context.GetAgentStack()
|
||||||
};
|
};
|
||||||
contextStack.Push(node);
|
_contextStack.Push(node);
|
||||||
|
|
||||||
// Reset
|
// Reset
|
||||||
state.ResetCurrentState();
|
state.ResetCurrentState();
|
||||||
|
|
@ -141,17 +167,62 @@ public class BotSharpConversationSideCar : IConversationSideCar
|
||||||
|
|
||||||
private void AfterExecute()
|
private void AfterExecute()
|
||||||
{
|
{
|
||||||
var state = _services.GetRequiredService<IConversationStateService>();
|
|
||||||
var routing = _services.GetRequiredService<IRoutingService>();
|
var routing = _services.GetRequiredService<IRoutingService>();
|
||||||
|
var node = _contextStack.Pop();
|
||||||
var node = contextStack.Pop();
|
|
||||||
|
|
||||||
// Recover
|
// Recover
|
||||||
state.SetCurrentState(node.State);
|
RestoreStates(node.State);
|
||||||
routing.Context.SetRecursiveCounter(node.RecursiveCounter);
|
routing.Context.SetRecursiveCounter(node.RecursiveCounter);
|
||||||
routing.Context.SetAgentStack(node.RoutingStack);
|
routing.Context.SetAgentStack(node.RoutingStack);
|
||||||
routing.Context.SetDialogs(node.RoutingDialogs);
|
routing.Context.SetDialogs(node.RoutingDialogs);
|
||||||
Utilities.ClearCache();
|
Utilities.ClearCache();
|
||||||
enabled = false;
|
_enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsValid(string conversationId)
|
||||||
|
{
|
||||||
|
return !_contextStack.IsNullOrEmpty()
|
||||||
|
&& _conversationId == conversationId
|
||||||
|
&& !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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -16,5 +16,6 @@ global using BotSharp.Abstraction.Conversations.Models;
|
||||||
global using BotSharp.Abstraction.Models;
|
global using BotSharp.Abstraction.Models;
|
||||||
global using BotSharp.Abstraction.Routing;
|
global using BotSharp.Abstraction.Routing;
|
||||||
global using BotSharp.Abstraction.SideCar;
|
global using BotSharp.Abstraction.SideCar;
|
||||||
|
global using BotSharp.Abstraction.SideCar.Models;
|
||||||
global using BotSharp.Abstraction.Utilities;
|
global using BotSharp.Abstraction.Utilities;
|
||||||
global using BotSharp.Core.SideCar.Settings;
|
global using BotSharp.Core.SideCar.Settings;
|
||||||
|
|
@ -443,7 +443,7 @@ public class ConversationStateService : IConversationStateService
|
||||||
|
|
||||||
public void SetCurrentState(ConversationState state)
|
public void SetCurrentState(ConversationState state)
|
||||||
{
|
{
|
||||||
var values = _curStates.Values.ToList();
|
var values = state.Values.ToList();
|
||||||
var copy = JsonSerializer.Deserialize<List<StateKeyValue>>(JsonSerializer.Serialize(values));
|
var copy = JsonSerializer.Deserialize<List<StateKeyValue>>(JsonSerializer.Serialize(values));
|
||||||
_curStates = new ConversationState(copy ?? []);
|
_curStates = new ConversationState(copy ?? []);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -307,6 +307,7 @@ public partial class FileRepository
|
||||||
return new ConversationState(states);
|
return new ConversationState(states);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SideCar]
|
||||||
public void UpdateConversationStates(string conversationId, List<StateKeyValue> states)
|
public void UpdateConversationStates(string conversationId, List<StateKeyValue> states)
|
||||||
{
|
{
|
||||||
if (states.IsNullOrEmpty()) return;
|
if (states.IsNullOrEmpty()) return;
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,12 @@ public static class ReasonerHelper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (args.AgentName == "response_to_user")
|
||||||
|
{
|
||||||
|
args.AgentName = "";
|
||||||
|
malformed = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (malformed)
|
if (malformed)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Captured LLM malformed response");
|
Console.WriteLine($"Captured LLM malformed response");
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@
|
||||||
"content": {
|
"content": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Response content"
|
"description": "Response content"
|
||||||
|
},
|
||||||
|
"conversation_end": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "User is ending the conversation."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [ "content" ]
|
"required": [ "content" ]
|
||||||
|
|
|
||||||
|
|
@ -367,7 +367,7 @@ public class ChatCompletionProvider : IChatCompletion
|
||||||
{
|
{
|
||||||
messages.Add(new AssistantChatMessage(new List<ChatToolCall>
|
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));
|
messages.Add(new ToolChatMessage(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.Content));
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ public class KnowledgeBasePlugin : IBotSharpPlugin
|
||||||
var section = menu.First(x => x.Label == "Apps");
|
var section = menu.First(x => x.Label == "Apps");
|
||||||
menu.Add(new PluginMenuDef("Knowledge Base", icon: "bx bx-book-open", weight: section.Weight + 1)
|
menu.Add(new PluginMenuDef("Knowledge Base", icon: "bx bx-book-open", weight: section.Weight + 1)
|
||||||
{
|
{
|
||||||
Roles = new List<string> { UserRole.Root, UserRole.Admin },
|
Roles = new List<string> { UserRole.Root, UserRole.Admin, UserRole.Engineer },
|
||||||
SubMenu = new List<PluginMenuDef>
|
SubMenu = new List<PluginMenuDef>
|
||||||
{
|
{
|
||||||
new PluginMenuDef("Q & A", link: "page/knowledge-base/question-answer"),
|
new PluginMenuDef("Q & A", link: "page/knowledge-base/question-answer"),
|
||||||
|
|
|
||||||
|
|
@ -266,6 +266,7 @@ public partial class MongoRepository
|
||||||
return new ConversationState(savedStates);
|
return new ConversationState(savedStates);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SideCar]
|
||||||
public void UpdateConversationStates(string conversationId, List<StateKeyValue> states)
|
public void UpdateConversationStates(string conversationId, List<StateKeyValue> states)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(conversationId) || states == null) return;
|
if (string.IsNullOrEmpty(conversationId) || states == null) return;
|
||||||
|
|
|
||||||
|
|
@ -615,10 +615,10 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
|
||||||
{
|
{
|
||||||
messages.Add(new AssistantChatMessage(new List<ChatToolCall>
|
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)
|
else if (message.Role == AgentRole.User)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,11 @@ public class TwilioInboundController : TwilioController
|
||||||
instruction.AgentId = request.AgentId;
|
instruction.AgentId = request.AgentId;
|
||||||
instruction.ConversationId = request.ConversationId;
|
instruction.ConversationId = request.ConversationId;
|
||||||
|
|
||||||
|
await HookEmitter.Emit<ITwilioSessionHook>(_services, async hook =>
|
||||||
|
{
|
||||||
|
await hook.OnSessionCreated(request);
|
||||||
|
}, request.AgentId);
|
||||||
|
|
||||||
if (twilio.MachineDetected(request))
|
if (twilio.MachineDetected(request))
|
||||||
{
|
{
|
||||||
response = new VoiceResponse();
|
response = new VoiceResponse();
|
||||||
|
|
@ -116,11 +121,6 @@ public class TwilioInboundController : TwilioController
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await HookEmitter.Emit<ITwilioSessionHook>(_services, async hook =>
|
|
||||||
{
|
|
||||||
await hook.OnSessionCreated(request);
|
|
||||||
}, request.AgentId);
|
|
||||||
|
|
||||||
return TwiML(response);
|
return TwiML(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
using BotSharp.Abstraction.Files;
|
using BotSharp.Abstraction.Files;
|
||||||
using BotSharp.Abstraction.Infrastructures.Enums;
|
using BotSharp.Abstraction.Infrastructures.Enums;
|
||||||
using BotSharp.Abstraction.Options;
|
using BotSharp.Abstraction.Options;
|
||||||
|
using BotSharp.Abstraction.Repositories;
|
||||||
using BotSharp.Abstraction.Routing;
|
using BotSharp.Abstraction.Routing;
|
||||||
|
using BotSharp.Abstraction.Utilities;
|
||||||
using BotSharp.Core.Infrastructures;
|
using BotSharp.Core.Infrastructures;
|
||||||
using BotSharp.Plugin.Twilio.Interfaces;
|
using BotSharp.Plugin.Twilio.Interfaces;
|
||||||
using BotSharp.Plugin.Twilio.Models;
|
using BotSharp.Plugin.Twilio.Models;
|
||||||
|
|
@ -134,7 +136,8 @@ public class OutboundPhoneCallFn : IFunctionCallback
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ForkConversation(LlmContextIn args,
|
private async Task ForkConversation(
|
||||||
|
LlmContextIn args,
|
||||||
string entryAgentId,
|
string entryAgentId,
|
||||||
string originConversationId,
|
string originConversationId,
|
||||||
string newConversationId,
|
string newConversationId,
|
||||||
|
|
@ -145,6 +148,8 @@ public class OutboundPhoneCallFn : IFunctionCallback
|
||||||
var services = scope.ServiceProvider;
|
var services = scope.ServiceProvider;
|
||||||
var convService = services.GetRequiredService<IConversationService>();
|
var convService = services.GetRequiredService<IConversationService>();
|
||||||
var convStorage = services.GetRequiredService<IConversationStorage>();
|
var convStorage = services.GetRequiredService<IConversationStorage>();
|
||||||
|
var state = _services.GetRequiredService<IConversationStateService>();
|
||||||
|
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||||
|
|
||||||
var newConv = await convService.NewConversation(new Conversation
|
var newConv = await convService.NewConversation(new Conversation
|
||||||
{
|
{
|
||||||
|
|
@ -170,15 +175,45 @@ public class OutboundPhoneCallFn : IFunctionCallback
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
convService.SetConversationId(newConversationId,
|
var utcNow = DateTime.UtcNow;
|
||||||
[
|
var excludStates = new List<string>
|
||||||
new MessageState(StateConst.ORIGIN_CONVERSATION_ID, originConversationId),
|
{
|
||||||
new MessageState("channel", "phone"),
|
"provider",
|
||||||
new MessageState("phone_from", call.From),
|
"model",
|
||||||
new MessageState("phone_direction", call.Direction),
|
"prompt_total",
|
||||||
new MessageState("phone_number", call.To),
|
"completion_total",
|
||||||
new MessageState("twilio_call_sid", call.Sid)
|
"llm_total_cost"
|
||||||
]);
|
};
|
||||||
convService.SaveStates();
|
|
||||||
|
var curStates = state.GetStates().Select(x => new MessageState(x.Key, x.Value)).ToList();
|
||||||
|
var subConvStates = new List<MessageState>
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
};
|
||||||
|
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
|
||||||
|
{
|
||||||
|
Key = x.Key,
|
||||||
|
Versioning = true,
|
||||||
|
Values = [
|
||||||
|
new StateValue
|
||||||
|
{
|
||||||
|
Data = x.Value.ConvertToString(_options.JsonSerializerOptions),
|
||||||
|
MessageId = messageId,
|
||||||
|
Active = true,
|
||||||
|
ActiveRounds = x.ActiveRounds,
|
||||||
|
Source = StateSource.Application,
|
||||||
|
UpdateTime = utcNow
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
db.UpdateConversationStates(newConversationId, newStates);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue