Merge branch 'master' of https://github.com/SciSharp/BotSharp
This commit is contained in:
commit
4509b55aca
|
|
@ -2,7 +2,7 @@
|
|||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>12.0</LangVersion>
|
||||
<BotSharpVersion>5.0.0</BotSharpVersion>
|
||||
<BotSharpVersion>5.1.0</BotSharpVersion>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<GenerateDocumentationFile>false</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
|
|
|||
|
|
@ -19,4 +19,9 @@ public class MessageState
|
|||
Value = value;
|
||||
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)
|
||||
{
|
||||
var sidecar = serviceProvider.GetService<IConversationSideCar>();
|
||||
var argTypes = args.Select(x => x.GetType()).ToArray();
|
||||
var sidecarMethod = sidecar?.GetType()?.GetMethods(BindingFlags.Public | BindingFlags.Instance)
|
||||
.FirstOrDefault(x => x.Name == methodName
|
||||
&& x.ReturnType == retType
|
||||
&& x.GetParameters().Length == argTypes.Length
|
||||
&& x.GetParameters().Select(p => p.ParameterType)
|
||||
.Zip(argTypes, (paramType, argType) => paramType.IsAssignableFrom(argType)).All(y => y));
|
||||
try
|
||||
{
|
||||
var sidecar = serviceProvider.GetService<IConversationSideCar>();
|
||||
var argTypes = args.Select(x => x.GetType()).ToArray();
|
||||
var sidecarMethod = sidecar?.GetType()?.GetMethods(BindingFlags.Public | BindingFlags.Instance)
|
||||
.FirstOrDefault(x => x.Name == methodName
|
||||
&& x.ReturnType == retType
|
||||
&& 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)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ public interface IConversationSideCar
|
|||
List<DialogElement> GetConversationDialogs(string conversationId);
|
||||
void UpdateConversationBreakpoint(string conversationId, ConversationBreakpoint breakpoint);
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,10 @@ public class BotSharpConversationSideCar : IConversationSideCar
|
|||
private readonly IServiceProvider _services;
|
||||
private readonly ILogger<BotSharpConversationSideCar> _logger;
|
||||
|
||||
private Stack<ConversationContext> contextStack = new();
|
||||
private Stack<ConversationContext> _contextStack = new();
|
||||
|
||||
private bool enabled = false;
|
||||
private bool _enabled = false;
|
||||
private string _conversationId = string.Empty;
|
||||
|
||||
public string Provider => "botsharp";
|
||||
|
||||
|
|
@ -39,46 +40,63 @@ public class BotSharpConversationSideCar : IConversationSideCar
|
|||
|
||||
public bool IsEnabled()
|
||||
{
|
||||
return enabled;
|
||||
return _enabled;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public List<DialogElement> GetConversationDialogs(string conversationId)
|
||||
{
|
||||
if (contextStack.IsNullOrEmpty())
|
||||
if (!IsValid(conversationId))
|
||||
{
|
||||
return new List<DialogElement>();
|
||||
}
|
||||
|
||||
return contextStack.Peek().Dialogs;
|
||||
return _contextStack.Peek().Dialogs;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public ConversationBreakpoint? GetConversationBreakpoint(string conversationId)
|
||||
{
|
||||
if (contextStack.IsNullOrEmpty())
|
||||
if (!IsValid(conversationId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var top = contextStack.Peek().Breakpoints;
|
||||
var top = _contextStack.Peek().Breakpoints;
|
||||
return top.LastOrDefault();
|
||||
}
|
||||
|
||||
public void UpdateConversationStates(string conversationId, List<StateKeyValue> states)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
|
@ -94,6 +112,7 @@ public class BotSharpConversationSideCar : IConversationSideCar
|
|||
var conv = _services.GetRequiredService<IConversationService>();
|
||||
var routing = _services.GetRequiredService<IRoutingService>();
|
||||
var state = _services.GetRequiredService<IConversationStateService>();
|
||||
_conversationId = conv.ConversationId;
|
||||
|
||||
var inputMsg = new RoleDialogModel(AgentRole.User, text);
|
||||
routing.Context.SetMessageId(conv.ConversationId, inputMsg.MessageId);
|
||||
|
|
@ -116,7 +135,7 @@ public class BotSharpConversationSideCar : IConversationSideCar
|
|||
|
||||
private void BeforeExecute(List<DialogElement>? dialogs)
|
||||
{
|
||||
enabled = true;
|
||||
_enabled = true;
|
||||
var state = _services.GetRequiredService<IConversationStateService>();
|
||||
var routing = _services.GetRequiredService<IRoutingService>();
|
||||
|
||||
|
|
@ -129,7 +148,7 @@ public class BotSharpConversationSideCar : IConversationSideCar
|
|||
RecursiveCounter = routing.Context.GetRecursiveCounter(),
|
||||
RoutingStack = routing.Context.GetAgentStack()
|
||||
};
|
||||
contextStack.Push(node);
|
||||
_contextStack.Push(node);
|
||||
|
||||
// Reset
|
||||
state.ResetCurrentState();
|
||||
|
|
@ -144,7 +163,7 @@ public class BotSharpConversationSideCar : IConversationSideCar
|
|||
var state = _services.GetRequiredService<IConversationStateService>();
|
||||
var routing = _services.GetRequiredService<IRoutingService>();
|
||||
|
||||
var node = contextStack.Pop();
|
||||
var node = _contextStack.Pop();
|
||||
|
||||
// Recover
|
||||
state.SetCurrentState(node.State);
|
||||
|
|
@ -152,6 +171,14 @@ public class BotSharpConversationSideCar : IConversationSideCar
|
|||
routing.Context.SetAgentStack(node.RoutingStack);
|
||||
routing.Context.SetDialogs(node.RoutingDialogs);
|
||||
Utilities.ClearCache();
|
||||
enabled = false;
|
||||
_enabled = false;
|
||||
}
|
||||
|
||||
private bool IsValid(string conversationId)
|
||||
{
|
||||
return !_contextStack.IsNullOrEmpty()
|
||||
&& _conversationId == conversationId
|
||||
&& !string.IsNullOrEmpty(conversationId)
|
||||
&& !string.IsNullOrEmpty(_conversationId);
|
||||
}
|
||||
}
|
||||
|
|
@ -307,6 +307,7 @@ public partial class FileRepository
|
|||
return new ConversationState(states);
|
||||
}
|
||||
|
||||
[SideCar]
|
||||
public void UpdateConversationStates(string conversationId, List<StateKeyValue> states)
|
||||
{
|
||||
if (states.IsNullOrEmpty()) return;
|
||||
|
|
|
|||
|
|
@ -65,6 +65,12 @@ public static class ReasonerHelper
|
|||
}
|
||||
}
|
||||
|
||||
if (args.AgentName == "response_to_user")
|
||||
{
|
||||
args.AgentName = "";
|
||||
malformed = true;
|
||||
}
|
||||
|
||||
if (malformed)
|
||||
{
|
||||
Console.WriteLine($"Captured LLM malformed response");
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@
|
|||
"content": {
|
||||
"type": "string",
|
||||
"description": "Response content"
|
||||
},
|
||||
"conversation_end": {
|
||||
"type": "boolean",
|
||||
"description": "User is ending the conversation."
|
||||
}
|
||||
},
|
||||
"required": [ "content" ]
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public class KnowledgeBasePlugin : IBotSharpPlugin
|
|||
var section = menu.First(x => x.Label == "Apps");
|
||||
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>
|
||||
{
|
||||
new PluginMenuDef("Q & A", link: "page/knowledge-base/question-answer"),
|
||||
|
|
|
|||
|
|
@ -266,6 +266,7 @@ public partial class MongoRepository
|
|||
return new ConversationState(savedStates);
|
||||
}
|
||||
|
||||
[SideCar]
|
||||
public void UpdateConversationStates(string conversationId, List<StateKeyValue> states)
|
||||
{
|
||||
if (string.IsNullOrEmpty(conversationId) || states == null) return;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
using BotSharp.Abstraction.Files;
|
||||
using BotSharp.Abstraction.Infrastructures.Enums;
|
||||
using BotSharp.Abstraction.Options;
|
||||
using BotSharp.Abstraction.Repositories;
|
||||
using BotSharp.Abstraction.Routing;
|
||||
using BotSharp.Abstraction.Utilities;
|
||||
using BotSharp.Core.Infrastructures;
|
||||
using BotSharp.Plugin.Twilio.Interfaces;
|
||||
using BotSharp.Plugin.Twilio.Models;
|
||||
|
|
@ -134,9 +136,10 @@ public class OutboundPhoneCallFn : IFunctionCallback
|
|||
}
|
||||
}
|
||||
|
||||
private async Task ForkConversation(LlmContextIn args,
|
||||
string entryAgentId,
|
||||
string originConversationId,
|
||||
private async Task ForkConversation(
|
||||
LlmContextIn args,
|
||||
string entryAgentId,
|
||||
string originConversationId,
|
||||
string newConversationId,
|
||||
CallResource call)
|
||||
{
|
||||
|
|
@ -145,6 +148,8 @@ public class OutboundPhoneCallFn : IFunctionCallback
|
|||
var services = scope.ServiceProvider;
|
||||
var convService = services.GetRequiredService<IConversationService>();
|
||||
var convStorage = services.GetRequiredService<IConversationStorage>();
|
||||
var state = _services.GetRequiredService<IConversationStateService>();
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
|
||||
var newConv = await convService.NewConversation(new Conversation
|
||||
{
|
||||
|
|
@ -170,15 +175,45 @@ public class OutboundPhoneCallFn : IFunctionCallback
|
|||
}
|
||||
});
|
||||
|
||||
convService.SetConversationId(newConversationId,
|
||||
[
|
||||
new MessageState(StateConst.ORIGIN_CONVERSATION_ID, originConversationId),
|
||||
new MessageState("channel", "phone"),
|
||||
new MessageState("phone_from", call.From),
|
||||
new MessageState("phone_direction", call.Direction),
|
||||
new MessageState("phone_number", call.To),
|
||||
new MessageState("twilio_call_sid", call.Sid)
|
||||
]);
|
||||
convService.SaveStates();
|
||||
var utcNow = DateTime.UtcNow;
|
||||
var excludStates = new List<string>
|
||||
{
|
||||
"provider",
|
||||
"model",
|
||||
"prompt_total",
|
||||
"completion_total",
|
||||
"llm_total_cost"
|
||||
};
|
||||
|
||||
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