Skip to content

Apply centralized routing by routing table. #110

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace BotSharp.Abstraction.Agents.Models;

public class RoutingResult
{
public string Result { get; set; }

public RoutingResult(string result)
{
Result = result;
}
}
Original file line number Diff line number Diff line change
@@ -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<string> RequiredFields { get; set; }

public override string ToString()
{
return AgentName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,19 @@ await HandleAssistantMessage(new RoleDialogModel(AgentRole.Assistant, fn.Content
return;
}

fn.Content = fn.ExecutionResult;
fn.Content = fn.FunctionArgs.Replace("\r", " ").Replace("\n", " ").Trim() + " => " + fn.ExecutionResult;

// Agent has been transferred
var agentSettings = _services.GetRequiredService<AgentSettings>();
if (fn.CurrentAgentId != preAgentId)
{
var agentSettings = _services.GetRequiredService<AgentSettings>();
var agentService = _services.GetRequiredService<IAgentService>();
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ private void SaveStateByArgs(string args)
{
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());
}
}
}
}
Expand Down
47 changes: 40 additions & 7 deletions src/Infrastructure/BotSharp.Core/Functions/RouteToAgentFn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Functions;
using BotSharp.Abstraction.Functions.Models;
using Microsoft.Extensions.Logging;
using System.IO;

namespace BotSharp.Core.Functions;

Expand All @@ -17,20 +19,51 @@ public RouteToAgentFn(IServiceProvider services)

public async Task<bool> Execute(RoleDialogModel message)
{
var args = JsonSerializer.Deserialize<AgentRoutingArgs>(message.FunctionArgs);
var args = JsonSerializer.Deserialize<RoutingArgs>(message.FunctionArgs);
var result = new RoutingResult($"Routed to {args.AgentName}");

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);
result = new RoutingResult($"Can't find {args.AgentName}");
}
else
{
var result = new FunctionExecutionValidationResult("true");
message.ExecutionResult = JsonSerializer.Serialize(result);
message.CurrentAgentId = args.AgentId;
var agentSettings = _services.GetRequiredService<AgentSettings>();
var dbSettings = _services.GetRequiredService<MyDatabaseSettings>();
var filePath = Path.Combine(dbSettings.FileRepository, agentSettings.DataDir, agentSettings.RouterId, "route.json");
var routes = JsonSerializer.Deserialize<RoutingTable[]>(File.ReadAllText(filePath));

var agent = routes.FirstOrDefault(x => x.AgentName.ToLower() == args.AgentName.ToLower());
if (agent == null)
{
result = new RoutingResult($"Can't find agent {args.AgentName}.");
}
else
{
// Check required fields
var jo = JsonSerializer.Deserialize<object>(message.FunctionArgs);
bool hasMissingField = false;
foreach (var field in agent.RequiredFields)
{
if (jo is JsonElement root)
{
if (!root.EnumerateObject().Any(x => x.Name == field))
{
result = new RoutingResult($"Please provide {field}.");
hasMissingField = true;
break;
}
}
}

if (!hasMissingField)
{
message.CurrentAgentId = agent.AgentId;
}
}
}

message.ExecutionResult = JsonSerializer.Serialize(result);
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,10 @@ public async Task<ActionResult<WebhookResponse>> Messages([FromRoute] string age
try
{
var parsed = JsonSerializer.Deserialize<QuickReplyMessageItem[]>(json, jsonOpt);
reply.QuickReplies = parsed;
if (parsed.Length > 0)
{
reply.QuickReplies = parsed;
}
}
catch(Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
}