Check routing rule after poping agent.
This commit is contained in:
parent
7b26a81a70
commit
dccc345bcd
|
|
@ -43,4 +43,6 @@ public interface IRoutingService
|
|||
Task<RoleDialogModel> InstructDirect(Agent agent, RoleDialogModel message);
|
||||
|
||||
Task<string> GetConversationContent(List<RoleDialogModel> dialogs, int maxDialogCount = 50);
|
||||
|
||||
bool HasMissingRequiredField(RoleDialogModel message, out string agentId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
using BotSharp.Abstraction.Functions;
|
||||
using BotSharp.Abstraction.Routing.Models;
|
||||
using System.Drawing;
|
||||
|
||||
namespace BotSharp.Core.Routing;
|
||||
|
||||
/// <summary>
|
||||
/// Router calls this function to set the Active Agent according to the context
|
||||
/// </summary>
|
||||
public class RouteToAgentFn : IFunctionCallback
|
||||
public partial class RouteToAgentFn : IFunctionCallback
|
||||
{
|
||||
public string Name => "route_to_agent";
|
||||
private readonly IServiceProvider _services;
|
||||
|
|
@ -82,7 +81,8 @@ public class RouteToAgentFn : IFunctionCallback
|
|||
return false;
|
||||
}
|
||||
|
||||
var missingfield = HasMissingRequiredField(message, out var agentId);
|
||||
var routing = _services.GetRequiredService<IRoutingService>();
|
||||
var missingfield = routing.HasMissingRequiredField(message, out var agentId);
|
||||
if (missingfield && message.CurrentAgentId != agentId)
|
||||
{
|
||||
// Stack redirection agent
|
||||
|
|
@ -94,97 +94,4 @@ public class RouteToAgentFn : IFunctionCallback
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the target agent needs some required fields but the
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private bool HasMissingRequiredField(RoleDialogModel message, out string agentId)
|
||||
{
|
||||
var args = JsonSerializer.Deserialize<RoutingArgs>(message.FunctionArgs);
|
||||
var routing = _services.GetRequiredService<IRoutingService>();
|
||||
|
||||
var routingRules = routing.GetRulesByAgentName(args.AgentName);
|
||||
|
||||
if (routingRules == null || !routingRules.Any())
|
||||
{
|
||||
agentId = message.CurrentAgentId;
|
||||
return false;
|
||||
}
|
||||
|
||||
agentId = routingRules.First().AgentId;
|
||||
// Add routed agent
|
||||
message.FunctionArgs = AppendPropertyToArgs(message.FunctionArgs, "route_to", agentId);
|
||||
|
||||
// Check required fields
|
||||
var root = JsonSerializer.Deserialize<JsonElement>(message.FunctionArgs);
|
||||
var missingFields = new List<string>();
|
||||
foreach (var field in routingRules.Where(x => x.Required).Select(x => x.Field))
|
||||
{
|
||||
if (!root.EnumerateObject().Any(x => x.Name == field))
|
||||
{
|
||||
missingFields.Add(field);
|
||||
}
|
||||
else if (root.EnumerateObject().Any(x => x.Name == field) &&
|
||||
string.IsNullOrEmpty(root.EnumerateObject().FirstOrDefault(x => x.Name == field).Value.ToString()))
|
||||
{
|
||||
missingFields.Add(field);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if states contains the field according conversation context.
|
||||
var states = _services.GetRequiredService<IConversationStateService>();
|
||||
foreach (var field in missingFields.ToList())
|
||||
{
|
||||
if (!string.IsNullOrEmpty(states.GetState(field)))
|
||||
{
|
||||
var value = states.GetState(field);
|
||||
message.FunctionArgs = AppendPropertyToArgs(message.FunctionArgs, field, value);
|
||||
missingFields.Remove(field);
|
||||
}
|
||||
}
|
||||
|
||||
if (missingFields.Any())
|
||||
{
|
||||
// Add field to args
|
||||
message.FunctionArgs = AppendPropertyToArgs(message.FunctionArgs, "missing_fields", missingFields);
|
||||
message.Content = $"missing some information: {string.Join(", ", missingFields)}";
|
||||
|
||||
// Handle redirect
|
||||
var routingRule = routingRules.FirstOrDefault(x => missingFields.Contains(x.Field));
|
||||
if (!string.IsNullOrEmpty(routingRule.RedirectTo))
|
||||
{
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var record = db.GetAgent(routingRule.RedirectTo);
|
||||
|
||||
// Add redirected agent
|
||||
message.FunctionArgs = AppendPropertyToArgs(message.FunctionArgs, "redirect_to", record.Name);
|
||||
agentId = routingRule.RedirectTo;
|
||||
var logger = _services.GetRequiredService<ILogger<RouteToAgentFn>>();
|
||||
#if DEBUG
|
||||
Console.WriteLine($"*** Routing redirect to {record.Name.ToUpper()} ***", Color.Yellow);
|
||||
#else
|
||||
logger.LogInformation($"*** Routing redirect to {record.Name.ToUpper()} ***");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// back to router
|
||||
agentId = message.CurrentAgentId;
|
||||
}
|
||||
}
|
||||
|
||||
return missingFields.Any();
|
||||
}
|
||||
|
||||
private string AppendPropertyToArgs(string args, string key, string value)
|
||||
{
|
||||
return args.Substring(0, args.Length - 1) + $", \"{key}\": \"{value}\"" + "}";
|
||||
}
|
||||
|
||||
private string AppendPropertyToArgs(string args, string key, IEnumerable<string> values)
|
||||
{
|
||||
string fields = string.Join(",", values.Select(x => $"\"{x}\""));
|
||||
return args.Substring(0, args.Length - 1) + $", \"{key}\": [{fields}]" + "}";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,6 +105,31 @@ public class RoutingContext : IRoutingContext
|
|||
HookEmitter.Emit<IRoutingHook>(_services, async hook =>
|
||||
await hook.OnAgentDequeued(agentId, currentAgentId, reason: reason)
|
||||
).Wait();
|
||||
|
||||
// Run the routing rule
|
||||
var agency = _services.GetRequiredService<IAgentService>();
|
||||
var agent = agency.LoadAgent(currentAgentId).Result;
|
||||
|
||||
var message = new RoleDialogModel(AgentRole.User, $"Try to route to agent {agent.Name}")
|
||||
{
|
||||
FunctionName = "route_to_agent",
|
||||
FunctionArgs = JsonSerializer.Serialize(new FunctionCallFromLlm
|
||||
{
|
||||
Function = "route_to_agent",
|
||||
AgentName = agent.Name,
|
||||
Reason = $"User manually route to agent {agent.Name}"
|
||||
})
|
||||
};
|
||||
|
||||
var routing = _services.GetRequiredService<IRoutingService>();
|
||||
var missingfield = routing.HasMissingRequiredField(message, out agentId);
|
||||
if (missingfield)
|
||||
{
|
||||
if (currentAgentId != agentId)
|
||||
{
|
||||
_stack.Push(agentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string PreviousAgentId()
|
||||
|
|
@ -115,7 +140,7 @@ public class RoutingContext : IRoutingContext
|
|||
}
|
||||
else if (_stack.Count > 1)
|
||||
{
|
||||
return _stack.ToArray()[1];
|
||||
return _stack.ToArray()[_stack.Count - 2];
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
using BotSharp.Abstraction.Routing.Models;
|
||||
using System.Drawing;
|
||||
|
||||
namespace BotSharp.Core.Routing;
|
||||
|
||||
public partial class RoutingService
|
||||
{
|
||||
/// <summary>
|
||||
/// If the target agent needs some required fields but the
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool HasMissingRequiredField(RoleDialogModel message, out string agentId)
|
||||
{
|
||||
var args = JsonSerializer.Deserialize<RoutingArgs>(message.FunctionArgs);
|
||||
var routing = _services.GetRequiredService<IRoutingService>();
|
||||
|
||||
var routingRules = routing.GetRulesByAgentName(args.AgentName);
|
||||
|
||||
if (routingRules == null || !routingRules.Any())
|
||||
{
|
||||
agentId = message.CurrentAgentId;
|
||||
return false;
|
||||
}
|
||||
|
||||
agentId = routingRules.First().AgentId;
|
||||
// Add routed agent
|
||||
message.FunctionArgs = AppendPropertyToArgs(message.FunctionArgs, "route_to", agentId);
|
||||
|
||||
// Check required fields
|
||||
var root = JsonSerializer.Deserialize<JsonElement>(message.FunctionArgs);
|
||||
var missingFields = new List<string>();
|
||||
foreach (var field in routingRules.Where(x => x.Required).Select(x => x.Field))
|
||||
{
|
||||
if (!root.EnumerateObject().Any(x => x.Name == field))
|
||||
{
|
||||
missingFields.Add(field);
|
||||
}
|
||||
else if (root.EnumerateObject().Any(x => x.Name == field) &&
|
||||
string.IsNullOrEmpty(root.EnumerateObject().FirstOrDefault(x => x.Name == field).Value.ToString()))
|
||||
{
|
||||
missingFields.Add(field);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if states contains the field according conversation context.
|
||||
var states = _services.GetRequiredService<IConversationStateService>();
|
||||
foreach (var field in missingFields.ToList())
|
||||
{
|
||||
if (!string.IsNullOrEmpty(states.GetState(field)))
|
||||
{
|
||||
var value = states.GetState(field);
|
||||
message.FunctionArgs = AppendPropertyToArgs(message.FunctionArgs, field, value);
|
||||
missingFields.Remove(field);
|
||||
}
|
||||
}
|
||||
|
||||
if (missingFields.Any())
|
||||
{
|
||||
// Add field to args
|
||||
message.FunctionArgs = AppendPropertyToArgs(message.FunctionArgs, "missing_fields", missingFields);
|
||||
message.Content = $"missing some information: {string.Join(", ", missingFields)}";
|
||||
|
||||
// Handle redirect
|
||||
var routingRule = routingRules.FirstOrDefault(x => missingFields.Contains(x.Field));
|
||||
if (!string.IsNullOrEmpty(routingRule.RedirectTo))
|
||||
{
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var record = db.GetAgent(routingRule.RedirectTo);
|
||||
|
||||
// Add redirected agent
|
||||
message.FunctionArgs = AppendPropertyToArgs(message.FunctionArgs, "redirect_to", record.Name);
|
||||
agentId = routingRule.RedirectTo;
|
||||
var logger = _services.GetRequiredService<ILogger<RouteToAgentFn>>();
|
||||
#if DEBUG
|
||||
Console.WriteLine($"*** Routing redirect to {record.Name.ToUpper()} ***", Color.Yellow);
|
||||
#else
|
||||
logger.LogInformation($"*** Routing redirect to {record.Name.ToUpper()} ***");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// back to router
|
||||
agentId = message.CurrentAgentId;
|
||||
}
|
||||
}
|
||||
|
||||
return missingFields.Any();
|
||||
}
|
||||
|
||||
private string AppendPropertyToArgs(string args, string key, string value)
|
||||
{
|
||||
return args.Substring(0, args.Length - 1) + $", \"{key}\": \"{value}\"" + "}";
|
||||
}
|
||||
|
||||
private string AppendPropertyToArgs(string args, string key, IEnumerable<string> values)
|
||||
{
|
||||
string fields = string.Join(",", values.Select(x => $"\"{x}\""));
|
||||
return args.Substring(0, args.Length - 1) + $", \"{key}\": [{fields}]" + "}";
|
||||
}
|
||||
}
|
||||
|
|
@ -36,6 +36,12 @@ public partial class RoutingService
|
|||
{
|
||||
result = await function.Execute(message);
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
_logger.LogError($"The input does not contain any JSON tokens:\r\n{message.Content}");
|
||||
message.StopCompletion = true;
|
||||
message.Content = ex.Message;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
message.StopCompletion = true;
|
||||
|
|
|
|||
Loading…
Reference in a new issue