diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentRoutingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentRoutingArgs.cs deleted file mode 100644 index 4046cda4..00000000 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentRoutingArgs.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Text.Json.Serialization; - -namespace BotSharp.Abstraction.Agents.Models; - -public class AgentRoutingArgs -{ - [JsonPropertyName("agent_id")] - public string AgentId { get; set; } -} diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/RoutingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/RoutingArgs.cs new file mode 100644 index 00000000..e02b1c7b --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/RoutingArgs.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; + +namespace BotSharp.Abstraction.Agents.Models; + +public class RoutingArgs +{ + [JsonPropertyName("agent_name")] + public string AgentName { get; set; } + + public override string ToString() + { + return AgentName; + } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/RoutingTable.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/RoutingTable.cs new file mode 100644 index 00000000..2feedcf8 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/RoutingTable.cs @@ -0,0 +1,20 @@ +using System.Text.Json.Serialization; + +namespace BotSharp.Abstraction.Agents.Models; + +public class RoutingTable +{ + [JsonPropertyName("agent_id")] + public string AgentId { get; set; } + + [JsonPropertyName("name")] + public string AgentName { get; set; } + + [JsonPropertyName("required")] + public List RequiredFields { get; set; } + + public override string ToString() + { + return AgentName; + } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs index bc460159..b681c2c3 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs @@ -30,11 +30,6 @@ public class RoleDialogModel /// public object ExecutionData { get; set; } - public bool IsConversationEnd { get; set; } - - public bool NeedReloadAgent { get; set; } - public bool StopPropagate { get; set; } - /// /// Channel name /// diff --git a/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs b/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs index ae315146..9081ca05 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs +++ b/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs @@ -37,7 +37,6 @@ public static class BotSharpServiceCollectionExtensions services.AddScoped(); - services.AddScoped(); services.AddScoped(); return services; diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.GetChatCompletionsAsyncRecursively.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.GetChatCompletionsAsyncRecursively.cs index 71698ebd..d4f058f7 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.GetChatCompletionsAsyncRecursively.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.GetChatCompletionsAsyncRecursively.cs @@ -21,12 +21,21 @@ public partial class ConversationService currentRecursiveDepth++; if (currentRecursiveDepth > maxRecursiveDepth) { - _logger.LogError($"Exceed max current recursive depth."); - await HandleAssistantMessage(new RoleDialogModel(AgentRole.Assistant, "System has exception, please try later.") + _logger.LogWarning($"Exceeded max recursive depth."); + + var latestResponse = wholeDialogs.Last(); + var text = latestResponse.Content; + if (latestResponse.Role == AgentRole.Function) + { + text = latestResponse.Content.Split("=>").Last(); + } + + await HandleAssistantMessage(new RoleDialogModel(AgentRole.Assistant, text) { CurrentAgentId = agent.Id, Channel = wholeDialogs.Last().Channel }, onMessageReceived); + return false; } @@ -53,18 +62,19 @@ public partial class ConversationService return; } - fn.Content = fn.ExecutionResult; + fn.Content = fn.FunctionArgs.Replace("\r", " ").Replace("\n", " ").Trim() + " => " + fn.ExecutionResult; // Agent has been transferred + var agentSettings = _services.GetRequiredService(); if (fn.CurrentAgentId != preAgentId) { - var agentSettings = _services.GetRequiredService(); var agentService = _services.GetRequiredService(); agent = await agentService.LoadAgent(fn.CurrentAgentId); } // Add to dialog history - _storage.Append(conversationId, preAgentId, fn); + // The server had an error processing your request. Sorry about that! + // _storage.Append(conversationId, preAgentId, fn); // After function is executed, pass the result to LLM to get a natural response wholeDialogs.Add(fn); diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs index 521f3054..45ede9dc 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -85,7 +85,10 @@ public partial class ConversationService { foreach (JsonProperty property in root.EnumerateObject()) { - stateService.SetState(property.Name, property.Value.ToString()); + if (!string.IsNullOrEmpty(property.Value.ToString())) + { + stateService.SetState(property.Name, property.Value.ToString()); + } } } } diff --git a/src/Infrastructure/BotSharp.Core/Functions/GoToRouterFn.cs b/src/Infrastructure/BotSharp.Core/Functions/GoToRouterFn.cs deleted file mode 100644 index e7a3455a..00000000 --- a/src/Infrastructure/BotSharp.Core/Functions/GoToRouterFn.cs +++ /dev/null @@ -1,27 +0,0 @@ -using BotSharp.Abstraction.Conversations.Models; -using BotSharp.Abstraction.Functions; -using BotSharp.Abstraction.Functions.Models; - -namespace BotSharp.Core.Functions; - -public class GoToRouterFn : IFunctionCallback -{ - public string Name => "go_to_router"; - private readonly IServiceProvider _services; - - public GoToRouterFn(IServiceProvider services) - { - _services = services; - } - - public async Task Execute(RoleDialogModel message) - { - var settings = _services.GetRequiredService(); - message.CurrentAgentId = settings.RouterId; - - var result = new FunctionExecutionValidationResult("true"); - message.ExecutionResult = JsonSerializer.Serialize(result); - - return true; - } -} diff --git a/src/Infrastructure/BotSharp.Core/Functions/RouteToAgentFn.cs b/src/Infrastructure/BotSharp.Core/Functions/RouteToAgentFn.cs index c8cd625d..4c6d1967 100644 --- a/src/Infrastructure/BotSharp.Core/Functions/RouteToAgentFn.cs +++ b/src/Infrastructure/BotSharp.Core/Functions/RouteToAgentFn.cs @@ -1,10 +1,13 @@ using BotSharp.Abstraction.Agents.Models; using BotSharp.Abstraction.Conversations.Models; using BotSharp.Abstraction.Functions; -using BotSharp.Abstraction.Functions.Models; +using System.IO; namespace BotSharp.Core.Functions; +/// +/// Router calls this function to set the Active Agent according to the context +/// public class RouteToAgentFn : IFunctionCallback { public string Name => "route_to_agent"; @@ -17,20 +20,68 @@ public class RouteToAgentFn : IFunctionCallback public async Task Execute(RoleDialogModel message) { - var args = JsonSerializer.Deserialize(message.FunctionArgs); + var args = JsonSerializer.Deserialize(message.FunctionArgs); - if (string.IsNullOrEmpty(args.AgentId)) + if (string.IsNullOrEmpty(args.AgentName)) { - var result = new FunctionExecutionValidationResult("false", "agent_id can't be parsed."); - message.ExecutionResult = JsonSerializer.Serialize(result); + message.ExecutionResult = $"missing agent name"; } else { - var result = new FunctionExecutionValidationResult("true"); - message.ExecutionResult = JsonSerializer.Serialize(result); - message.CurrentAgentId = args.AgentId; + if (!HasMissingRequiredField(message, out var agentId)) + { + message.CurrentAgentId = agentId; + message.ExecutionResult = $"Routed to {args.AgentName}"; + } } return true; } + + /// + /// If the target agent needs some required fields but the + /// + /// + private bool HasMissingRequiredField(RoleDialogModel message, out string agentId) + { + var args = JsonSerializer.Deserialize(message.FunctionArgs); + + var routes = GetRoutingTable(); + var agent = routes.FirstOrDefault(x => x.AgentName.ToLower() == args.AgentName.ToLower()); + + if (agent == null) + { + agentId = message.CurrentAgentId; + message.ExecutionResult = $"Can't find agent {args.AgentName}"; + return true; + } + + agentId = agent.AgentId; + + // Check required fields + var jo = JsonSerializer.Deserialize(message.FunctionArgs); + bool hasMissingField = false; + foreach (var field in agent.RequiredFields) + { + if (jo is JsonElement root) + { + if (!root.EnumerateObject().Any(x => x.Name == field)) + { + message.ExecutionResult = $"missing {field}."; + hasMissingField = true; + break; + } + } + } + + return hasMissingField; + } + + private RoutingTable[] GetRoutingTable() + { + var agentSettings = _services.GetRequiredService(); + var dbSettings = _services.GetRequiredService(); + var filePath = Path.Combine(dbSettings.FileRepository, agentSettings.DataDir, agentSettings.RouterId, "route.json"); + return JsonSerializer.Deserialize(File.ReadAllText(filePath)); + } } diff --git a/src/Plugins/BotSharp.Plugin.MetaMessenger/Controllers/WebhookController.cs b/src/Plugins/BotSharp.Plugin.MetaMessenger/Controllers/WebhookController.cs index 47e87dc3..a83e424a 100644 --- a/src/Plugins/BotSharp.Plugin.MetaMessenger/Controllers/WebhookController.cs +++ b/src/Plugins/BotSharp.Plugin.MetaMessenger/Controllers/WebhookController.cs @@ -124,7 +124,10 @@ public class WebhookController : ControllerBase try { var parsed = JsonSerializer.Deserialize(json, jsonOpt); - reply.QuickReplies = parsed; + if (parsed.Length > 0) + { + reply.QuickReplies = parsed; + } } catch(Exception ex) { diff --git a/src/Plugins/BotSharp.Plugin.MetaMessenger/MessagingModels/QuickReplyMessage.cs b/src/Plugins/BotSharp.Plugin.MetaMessenger/MessagingModels/QuickReplyMessage.cs index e0db6925..9d577143 100644 --- a/src/Plugins/BotSharp.Plugin.MetaMessenger/MessagingModels/QuickReplyMessage.cs +++ b/src/Plugins/BotSharp.Plugin.MetaMessenger/MessagingModels/QuickReplyMessage.cs @@ -13,5 +13,6 @@ public class QuickReplyMessage : IResponseMessage public string Text { get; set; } [JsonPropertyName("quick_replies")] - public QuickReplyMessageItem[] QuickReplies { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public QuickReplyMessageItem[]? QuickReplies { get; set; } }