2023-09-19 12:17:50 +00:00
|
|
|
using BotSharp.Abstraction.Agents.Models;
|
|
|
|
|
using BotSharp.Abstraction.Functions;
|
|
|
|
|
using BotSharp.Abstraction.Functions.Models;
|
|
|
|
|
using BotSharp.Abstraction.Repositories;
|
|
|
|
|
using BotSharp.Abstraction.Routing;
|
|
|
|
|
using BotSharp.Abstraction.Routing.Models;
|
2023-09-20 10:31:50 +00:00
|
|
|
using BotSharp.Abstraction.Routing.Settings;
|
|
|
|
|
using BotSharp.Abstraction.Templating;
|
|
|
|
|
using System.IO;
|
2023-09-19 12:17:50 +00:00
|
|
|
|
|
|
|
|
namespace BotSharp.Core.Routing;
|
|
|
|
|
|
|
|
|
|
public class RoutingService : IRoutingService
|
|
|
|
|
{
|
|
|
|
|
private readonly IServiceProvider _services;
|
2023-09-20 10:31:50 +00:00
|
|
|
private readonly RoutingSettings _settings;
|
2023-09-19 12:17:50 +00:00
|
|
|
private readonly ILogger _logger;
|
|
|
|
|
private List<RoleDialogModel> _dialogs;
|
|
|
|
|
public List<RoleDialogModel> Dialogs => _dialogs;
|
|
|
|
|
|
2023-09-20 10:31:50 +00:00
|
|
|
public RoutingService(IServiceProvider services,
|
|
|
|
|
RoutingSettings settings,
|
|
|
|
|
ILogger<RoutingService> logger)
|
2023-09-19 12:17:50 +00:00
|
|
|
{
|
|
|
|
|
_services = services;
|
2023-09-20 10:31:50 +00:00
|
|
|
_settings = settings;
|
2023-09-19 12:17:50 +00:00
|
|
|
_logger = logger;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-21 13:08:27 +00:00
|
|
|
public async Task<RoleDialogModel> Enter(Agent router, List<RoleDialogModel> whileDialogs)
|
2023-09-19 12:17:50 +00:00
|
|
|
{
|
|
|
|
|
_dialogs = new List<RoleDialogModel>();
|
|
|
|
|
RoleDialogModel result = new RoleDialogModel(AgentRole.Assistant, "not handled");
|
|
|
|
|
|
2023-09-20 22:08:14 +00:00
|
|
|
foreach (var dialog in whileDialogs.TakeLast(20))
|
2023-09-19 12:17:50 +00:00
|
|
|
{
|
2023-09-21 13:08:27 +00:00
|
|
|
router.Instruction += $"\r\n{dialog.Role}: {dialog.Content}";
|
2023-09-19 12:17:50 +00:00
|
|
|
}
|
|
|
|
|
|
2023-09-21 13:08:27 +00:00
|
|
|
var inst = await GetNextInstructionFromReasoner($"What's the next step to make user's original goal?", router);
|
2023-09-19 12:17:50 +00:00
|
|
|
int loopCount = 0;
|
|
|
|
|
while (loopCount < 3)
|
|
|
|
|
{
|
|
|
|
|
loopCount++;
|
|
|
|
|
if (inst.Function == "continue_execute_task")
|
|
|
|
|
{
|
2023-09-21 13:08:27 +00:00
|
|
|
var routing = _services.GetRequiredService<IAgentRouting>();
|
2023-09-19 12:17:50 +00:00
|
|
|
var db = _services.GetRequiredService<IBotSharpRepository>();
|
|
|
|
|
var record = db.Agents.First(x => x.Name.ToLower() == inst.Parameters.AgentName.ToLower());
|
|
|
|
|
|
|
|
|
|
result = new RoleDialogModel(AgentRole.Function, inst.Parameters.Question)
|
|
|
|
|
{
|
|
|
|
|
FunctionName = inst.Function,
|
|
|
|
|
FunctionArgs = JsonSerializer.Serialize(inst.Parameters.Arguments),
|
|
|
|
|
CurrentAgentId = record.Id,
|
|
|
|
|
};
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// Compatible with previous Router, can be removed in the future.
|
|
|
|
|
else if (inst.Function == "route_to_agent")
|
|
|
|
|
{
|
|
|
|
|
var function = _services.GetServices<IFunctionCallback>().FirstOrDefault(x => x.Name == inst.Function);
|
|
|
|
|
result = new RoleDialogModel(AgentRole.Function, inst.Parameters.Question)
|
|
|
|
|
{
|
|
|
|
|
FunctionName = inst.Function,
|
|
|
|
|
FunctionArgs = JsonSerializer.Serialize(new RoutingArgs
|
|
|
|
|
{
|
|
|
|
|
AgentName = inst.Parameters.AgentName
|
|
|
|
|
}),
|
|
|
|
|
};
|
|
|
|
|
var ret = await function.Execute(result);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (inst.Function == "interrupt_task_execution")
|
|
|
|
|
{
|
2023-09-21 13:08:27 +00:00
|
|
|
result = new RoleDialogModel(AgentRole.User, inst.Reason)
|
2023-09-19 12:17:50 +00:00
|
|
|
{
|
|
|
|
|
FunctionName = inst.Function
|
|
|
|
|
};
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (inst.Function == "response_to_user")
|
|
|
|
|
{
|
|
|
|
|
result = new RoleDialogModel(AgentRole.User, inst.Parameters.Answer)
|
|
|
|
|
{
|
|
|
|
|
FunctionName = inst.Function
|
|
|
|
|
};
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (inst.Function == "retrieve_data_from_agent")
|
|
|
|
|
{
|
|
|
|
|
// Retrieve information from specific agent
|
|
|
|
|
var db = _services.GetRequiredService<IBotSharpRepository>();
|
|
|
|
|
var record = db.Agents.First(x => x.Name.ToLower() == inst.Parameters.AgentName.ToLower());
|
|
|
|
|
var response = await RetrieveDataFromAgent(record.Id, new List<RoleDialogModel>
|
|
|
|
|
{
|
|
|
|
|
new RoleDialogModel(AgentRole.User, inst.Parameters.Question)
|
|
|
|
|
});
|
|
|
|
|
|
2023-09-20 10:31:50 +00:00
|
|
|
inst.Parameters.Answer = response.Content;
|
2023-09-21 13:08:27 +00:00
|
|
|
|
|
|
|
|
_dialogs.Add(new RoleDialogModel(AgentRole.Assistant, inst.Parameters.Question)
|
|
|
|
|
{
|
|
|
|
|
CurrentAgentId = record.Id
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
router.Instruction += $"\r\n{AgentRole.Assistant}: {inst.Parameters.Question}";
|
|
|
|
|
|
|
|
|
|
_dialogs.Add(new RoleDialogModel(AgentRole.Function, inst.Parameters.Answer)
|
2023-09-19 12:17:50 +00:00
|
|
|
{
|
|
|
|
|
FunctionName = inst.Function,
|
|
|
|
|
FunctionArgs = JsonSerializer.Serialize(inst.Parameters.Arguments),
|
2023-09-21 13:08:27 +00:00
|
|
|
ExecutionResult = inst.Parameters.Answer,
|
|
|
|
|
ExecutionData = response.ExecutionData,
|
2023-09-19 12:17:50 +00:00
|
|
|
CurrentAgentId = record.Id
|
|
|
|
|
});
|
|
|
|
|
|
2023-09-21 13:08:27 +00:00
|
|
|
router.Instruction += $"\r\n{AgentRole.Function}: {response.Content}";
|
2023-09-19 12:17:50 +00:00
|
|
|
|
|
|
|
|
// Got the response from agent, then send to reasoner again to make the decision
|
2023-09-21 13:08:27 +00:00
|
|
|
inst = await GetNextInstructionFromReasoner($"What's the next step based on user's original goal and function result?", router);
|
2023-09-19 12:17:50 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-21 13:08:27 +00:00
|
|
|
private async Task<FunctionCallFromLlm> GetNextInstructionFromReasoner(string prompt, Agent reasoner)
|
2023-09-19 12:17:50 +00:00
|
|
|
{
|
2023-09-21 13:08:27 +00:00
|
|
|
var responseFormat = JsonSerializer.Serialize(new FunctionCallFromLlm());
|
2023-09-19 12:17:50 +00:00
|
|
|
var wholeDialogs = new List<RoleDialogModel>
|
|
|
|
|
{
|
2023-09-21 13:08:27 +00:00
|
|
|
new RoleDialogModel(AgentRole.User, $"{prompt} Response in JSON format {responseFormat}")
|
2023-09-19 12:17:50 +00:00
|
|
|
};
|
|
|
|
|
|
2023-09-20 22:08:14 +00:00
|
|
|
var chatCompletion = CompletionProvider.GetChatCompletion(_services,
|
|
|
|
|
provider: _settings.Provider,
|
|
|
|
|
model: _settings.Model);
|
2023-09-19 12:17:50 +00:00
|
|
|
|
|
|
|
|
RoleDialogModel response = null;
|
|
|
|
|
await chatCompletion.GetChatCompletionsAsync(reasoner, wholeDialogs, async msg
|
|
|
|
|
=> response = msg, fn
|
|
|
|
|
=> Task.CompletedTask);
|
|
|
|
|
|
|
|
|
|
var args = JsonSerializer.Deserialize<FunctionCallFromLlm>(response.Content);
|
|
|
|
|
|
|
|
|
|
if (args.Parameters.Arguments != null)
|
|
|
|
|
{
|
|
|
|
|
SaveStateByArgs(args.Parameters.Arguments);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
args.Function = args.Function.Split('.').Last();
|
2023-09-21 13:08:27 +00:00
|
|
|
args.Parameters.AgentName = args.Parameters.AgentName.Split(':').Last().Trim();
|
2023-09-19 12:17:50 +00:00
|
|
|
|
2023-09-20 22:08:14 +00:00
|
|
|
_logger.LogInformation($"*** Next Instruction *** {args}");
|
2023-09-20 10:31:50 +00:00
|
|
|
|
2023-09-19 12:17:50 +00:00
|
|
|
return args;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task<RoleDialogModel> RetrieveDataFromAgent(string agentId, List<RoleDialogModel> wholeDialogs)
|
|
|
|
|
{
|
|
|
|
|
var agentService = _services.GetRequiredService<IAgentService>();
|
|
|
|
|
var agent = await agentService.LoadAgent(agentId);
|
|
|
|
|
|
|
|
|
|
var chatCompletion = CompletionProvider.GetChatCompletion(_services);
|
|
|
|
|
|
|
|
|
|
RoleDialogModel response = null;
|
|
|
|
|
await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs, async msg
|
|
|
|
|
=> response = msg, async fn
|
|
|
|
|
=>
|
|
|
|
|
{
|
|
|
|
|
// execute function
|
|
|
|
|
// Save states
|
|
|
|
|
SaveStateByArgs(JsonSerializer.Deserialize<JsonDocument>(fn.FunctionArgs));
|
|
|
|
|
|
|
|
|
|
var conversationService = _services.GetRequiredService<IConversationService>();
|
|
|
|
|
// Call functions
|
|
|
|
|
await conversationService.CallFunctions(fn);
|
|
|
|
|
|
|
|
|
|
response = fn;
|
|
|
|
|
response.Content = fn.ExecutionResult;
|
|
|
|
|
});
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SaveStateByArgs(JsonDocument args)
|
|
|
|
|
{
|
|
|
|
|
if (args == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var stateService = _services.GetRequiredService<IConversationStateService>();
|
|
|
|
|
if (args.RootElement is JsonElement root)
|
|
|
|
|
{
|
|
|
|
|
foreach (JsonProperty property in root.EnumerateObject())
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrEmpty(property.Value.ToString()))
|
|
|
|
|
{
|
|
|
|
|
stateService.SetState(property.Name, property.Value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-09-20 10:31:50 +00:00
|
|
|
|
|
|
|
|
public Agent LoadRouter()
|
|
|
|
|
{
|
|
|
|
|
var db = _services.GetRequiredService<IBotSharpRepository>();
|
|
|
|
|
|
|
|
|
|
var router = new Agent()
|
|
|
|
|
{
|
|
|
|
|
Id = _settings.RouterId,
|
2023-09-21 16:23:43 +00:00
|
|
|
Name = _settings.RouteName
|
2023-09-20 10:31:50 +00:00
|
|
|
};
|
|
|
|
|
var agents = db.Agents.Where(x => !x.Disabled && x.AllowRouting).ToArray();
|
|
|
|
|
|
|
|
|
|
var dict = new Dictionary<string, object>();
|
|
|
|
|
dict["routing_records"] = agents.Select(x => new RoutingItem
|
|
|
|
|
{
|
|
|
|
|
AgentId = x.Id,
|
|
|
|
|
Description = x.Description,
|
|
|
|
|
Name = x.Name,
|
|
|
|
|
RequiredFields = x.RoutingRules.Where(x => x.Required)
|
|
|
|
|
.Select(x => x.Field)
|
|
|
|
|
.ToArray()
|
|
|
|
|
}).ToArray();
|
|
|
|
|
|
2023-09-21 13:08:27 +00:00
|
|
|
dict["enable_reasoning"] = _settings.EnableReasoning;
|
2023-09-20 10:31:50 +00:00
|
|
|
if (_settings.EnableReasoning)
|
|
|
|
|
{
|
2023-09-21 15:10:24 +00:00
|
|
|
dict["reasoning_functions"] = PromptConst.REASONING_FUNCTIONS;
|
2023-09-20 10:31:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var render = _services.GetRequiredService<ITemplateRender>();
|
2023-09-21 15:10:24 +00:00
|
|
|
router.Instruction = render.Render(PromptConst.ROUTER_PROMPT, dict);
|
2023-09-20 10:31:50 +00:00
|
|
|
|
|
|
|
|
return router;
|
|
|
|
|
}
|
2023-09-19 12:17:50 +00:00
|
|
|
}
|