BotSharp/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs

198 lines
7.5 KiB
C#
Raw Normal View History

using BotSharp.Abstraction.Functions;
2023-08-28 03:50:10 +00:00
using BotSharp.Abstraction.Routing.Models;
2023-09-22 20:38:58 +00:00
using System.Drawing;
namespace BotSharp.Core.Routing;
2023-08-23 03:08:14 +00:00
/// <summary>
/// Router calls this function to set the Active Agent according to the context
/// </summary>
public class RouteToAgentFn : IFunctionCallback
{
public string Name => "route_to_agent";
private readonly IServiceProvider _services;
2024-02-28 16:21:14 +00:00
private readonly IRoutingContext _context;
2024-02-28 16:21:14 +00:00
public RouteToAgentFn(IServiceProvider services, IRoutingContext context)
{
_services = services;
_context = context;
}
public async Task<bool> Execute(RoleDialogModel message)
{
var args = JsonSerializer.Deserialize<RoutingArgs>(message.FunctionArgs);
2024-02-28 20:40:59 +00:00
var states = _services.GetRequiredService<IConversationStateService>();
// Push original task agent
if (!string.IsNullOrEmpty(args.OriginalAgent) && args.OriginalAgent.Length < 32)
{
2024-02-28 20:40:59 +00:00
// Correct user goal agent to keep orignal task
var goalAgentInState = states.GetState("user_goal_agent", string.Empty);
2024-03-01 05:44:57 +00:00
bool correctToOriginalAgent = false;
2024-02-28 20:40:59 +00:00
if (goalAgentInState == string.Empty)
{
states.SetState("user_goal_agent", args.OriginalAgent, isNeedVersion: true);
}
else if (args.OriginalAgent == args.AgentName && args.OriginalAgent != goalAgentInState)
{
// Correct to original agent
args.OriginalAgent = goalAgentInState;
2024-03-01 05:44:57 +00:00
correctToOriginalAgent = true;
2024-02-28 20:40:59 +00:00
}
else if (args.OriginalAgent != args.AgentName && args.OriginalAgent != goalAgentInState)
{
// Correct to original agent
states.SetState("user_goal_agent", args.OriginalAgent, isNeedVersion: true);
}
var db = _services.GetRequiredService<IBotSharpRepository>();
2023-11-27 22:20:49 +00:00
var filter = new AgentFilter { AgentName = args.OriginalAgent };
var originalAgent = db.GetAgents(filter).FirstOrDefault();
if (originalAgent != null)
{
2024-03-01 05:44:57 +00:00
_context.Push(originalAgent.Id, $"user goal agent{(correctToOriginalAgent ? " & is corrected" : "")}");
}
}
2024-02-28 20:40:59 +00:00
// Push next action agent
if (!string.IsNullOrEmpty(args.AgentName) && args.AgentName.Length < 32)
{
2024-02-28 20:40:59 +00:00
var db = _services.GetRequiredService<IBotSharpRepository>();
var filter = new AgentFilter { AgentName = args.AgentName };
var actionAgent = db.GetAgents(filter).FirstOrDefault();
if (actionAgent != null)
{
_context.Push(actionAgent.Id, args.Reason);
}
states.SetState("last_action_agent", args.AgentName, isNeedVersion: true);
}
if (string.IsNullOrEmpty(args.AgentName))
{
2023-10-23 00:31:49 +00:00
message.Content = $"missing agent name";
}
else
{
var db = _services.GetRequiredService<IBotSharpRepository>();
2023-11-27 22:20:49 +00:00
var filter = new AgentFilter { AgentName = args.AgentName };
var targetAgent = db.GetAgents(filter).FirstOrDefault();
if (targetAgent == null)
{
2023-10-23 00:31:49 +00:00
message.Data = JsonSerializer.Deserialize<JsonElement>(message.FunctionArgs);
return false;
}
2024-01-13 21:17:40 +00:00
if (targetAgent.Disabled)
{
return false;
}
2023-09-09 15:37:38 +00:00
var missingfield = HasMissingRequiredField(message, out var agentId);
2023-08-23 18:32:13 +00:00
if (missingfield && message.CurrentAgentId != agentId)
{
2024-03-01 05:44:57 +00:00
// Stack redirection agent
_context.Push(agentId, reason: $"redirection: {message.Content}");
}
2023-08-23 03:08:14 +00:00
}
2024-02-28 20:40:59 +00:00
message.CurrentAgentId = _context.GetCurrentAgentId();
2023-08-23 03:08:14 +00:00
return true;
}
/// <summary>
/// If the target agent needs some required fields but the
/// </summary>
/// <returns></returns>
2023-09-09 15:37:38 +00:00
private bool HasMissingRequiredField(RoleDialogModel message, out string agentId)
2023-08-23 03:08:14 +00:00
{
var args = JsonSerializer.Deserialize<RoutingArgs>(message.FunctionArgs);
2023-11-01 01:48:12 +00:00
var routing = _services.GetRequiredService<IRoutingService>();
2023-08-23 03:08:14 +00:00
var routingRules = routing.GetRulesByAgentName(args.AgentName);
2023-09-18 18:13:49 +00:00
if (routingRules == null || !routingRules.Any())
2023-08-23 03:08:14 +00:00
{
agentId = message.CurrentAgentId;
2023-09-18 18:13:49 +00:00
return false;
2023-08-23 03:08:14 +00:00
}
2023-09-18 18:13:49 +00:00
agentId = routingRules.First().AgentId;
// Add routed agent
message.FunctionArgs = AppendPropertyToArgs(message.FunctionArgs, "route_to", agentId);
2023-08-23 03:08:14 +00:00
// Check required fields
var root = JsonSerializer.Deserialize<JsonElement>(message.FunctionArgs);
var missingFields = new List<string>();
2023-09-18 18:13:49 +00:00
foreach (var field in routingRules.Where(x => x.Required).Select(x => x.Field))
2023-08-23 03:08:14 +00:00
{
if (!root.EnumerateObject().Any(x => x.Name == field))
2023-08-23 03:08:14 +00:00
{
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())
2023-08-23 18:32:13 +00:00
{
// Add field to args
message.FunctionArgs = AppendPropertyToArgs(message.FunctionArgs, "missing_fields", missingFields);
2023-10-23 00:31:49 +00:00
message.Content = $"missing some information: {string.Join(',', missingFields)}";
// Handle redirect
2023-09-18 18:13:49 +00:00
var routingRule = routingRules.FirstOrDefault(x => missingFields.Contains(x.Field));
if (!string.IsNullOrEmpty(routingRule.RedirectTo))
{
2023-09-18 18:13:49 +00:00
var db = _services.GetRequiredService<IBotSharpRepository>();
var record = db.GetAgent(routingRule.RedirectTo);
// Add redirected agent
2023-09-18 18:13:49 +00:00
message.FunctionArgs = AppendPropertyToArgs(message.FunctionArgs, "redirect_to", record.Name);
2023-09-20 22:08:14 +00:00
agentId = routingRule.RedirectTo;
2023-09-22 20:38:58 +00:00
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
}
2023-09-15 01:19:32 +00:00
else
{
// back to router
agentId = message.CurrentAgentId;
}
2023-08-23 18:32:13 +00:00
}
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}]" + "}";
2023-08-23 03:08:14 +00:00
}
}