2023-09-01 22:26:25 +00:00
|
|
|
using BotSharp.Abstraction.Agents.Enums;
|
|
|
|
|
using BotSharp.Abstraction.Agents.Models;
|
|
|
|
|
using BotSharp.Abstraction.Instructs;
|
2023-09-08 17:03:49 +00:00
|
|
|
using BotSharp.Abstraction.Instructs.Models;
|
2023-09-01 22:26:25 +00:00
|
|
|
using BotSharp.Abstraction.MLTasks;
|
|
|
|
|
using BotSharp.Abstraction.Templating;
|
2023-09-08 17:03:49 +00:00
|
|
|
using System.IO;
|
2023-09-01 22:26:25 +00:00
|
|
|
|
|
|
|
|
namespace BotSharp.Core.Instructs;
|
|
|
|
|
|
|
|
|
|
public partial class InstructService : IInstructService
|
|
|
|
|
{
|
|
|
|
|
private readonly IServiceProvider _services;
|
|
|
|
|
private readonly ILogger _logger;
|
|
|
|
|
|
|
|
|
|
public InstructService(IServiceProvider services, ILogger<InstructService> logger)
|
|
|
|
|
{
|
|
|
|
|
_services = services;
|
|
|
|
|
_logger = logger;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-08 17:03:49 +00:00
|
|
|
public async Task<InstructResult> ExecuteInstruction(Agent agent,
|
|
|
|
|
RoleDialogModel message,
|
|
|
|
|
Func<RoleDialogModel, Task> onMessageReceived,
|
|
|
|
|
Func<RoleDialogModel, Task> onFunctionExecuting,
|
|
|
|
|
Func<RoleDialogModel, Task> onFunctionExecuted)
|
|
|
|
|
{
|
|
|
|
|
var response = new InstructResult();
|
|
|
|
|
|
|
|
|
|
var wholeDialogs = new List<RoleDialogModel>
|
|
|
|
|
{
|
2023-09-09 15:37:38 +00:00
|
|
|
message
|
2023-09-08 17:03:49 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Trigger before completion hooks
|
|
|
|
|
var hooks = _services.GetServices<IInstructHook>();
|
|
|
|
|
foreach (var hook in hooks)
|
|
|
|
|
{
|
|
|
|
|
await hook.BeforeCompletion(message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await ExecuteInstructionRecursively(agent,
|
|
|
|
|
wholeDialogs,
|
|
|
|
|
async msg =>
|
|
|
|
|
{
|
|
|
|
|
response.Text = msg.Content;
|
|
|
|
|
await onMessageReceived(msg);
|
|
|
|
|
},
|
|
|
|
|
async fn =>
|
|
|
|
|
{
|
|
|
|
|
response.Function = fn.FunctionName;
|
|
|
|
|
await onFunctionExecuting(fn);
|
|
|
|
|
},
|
|
|
|
|
async fn =>
|
|
|
|
|
{
|
2023-10-23 00:31:49 +00:00
|
|
|
response.Data = fn.Data;
|
2023-09-08 17:03:49 +00:00
|
|
|
await onFunctionExecuted(fn);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
foreach (var hook in hooks)
|
|
|
|
|
{
|
|
|
|
|
await hook.AfterCompletion(response);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task<bool> ExecuteInstructionRecursively(Agent agent,
|
2023-09-01 22:26:25 +00:00
|
|
|
List<RoleDialogModel> wholeDialogs,
|
|
|
|
|
Func<RoleDialogModel, Task> onMessageReceived,
|
|
|
|
|
Func<RoleDialogModel, Task> onFunctionExecuting,
|
|
|
|
|
Func<RoleDialogModel, Task> onFunctionExecuted)
|
|
|
|
|
{
|
2023-09-14 01:41:51 +00:00
|
|
|
var chatCompletion = CompletionProvider.GetChatCompletion(_services);
|
2023-09-01 22:26:25 +00:00
|
|
|
|
|
|
|
|
var result = await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs, async msg =>
|
|
|
|
|
{
|
|
|
|
|
await onMessageReceived(msg);
|
2023-09-08 17:03:49 +00:00
|
|
|
|
|
|
|
|
wholeDialogs.Add(msg);
|
2023-09-01 22:26:25 +00:00
|
|
|
}, async fn =>
|
|
|
|
|
{
|
|
|
|
|
var preAgentId = agent.Id;
|
|
|
|
|
|
|
|
|
|
await HandleFunctionMessage(fn, onFunctionExecuting, onFunctionExecuted);
|
|
|
|
|
|
|
|
|
|
// Function executed has exception
|
2023-10-23 00:31:49 +00:00
|
|
|
if (fn.Content == null || fn.StopCompletion)
|
2023-09-01 22:26:25 +00:00
|
|
|
{
|
|
|
|
|
await onMessageReceived(new RoleDialogModel(AgentRole.Assistant, fn.Content));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-23 00:31:49 +00:00
|
|
|
fn.Content = fn.FunctionArgs.Replace("\r", " ").Replace("\n", " ").Trim() + " => " + fn.Content;
|
2023-09-01 22:26:25 +00:00
|
|
|
|
|
|
|
|
// Find response template
|
|
|
|
|
var templateService = _services.GetRequiredService<IResponseTemplateService>();
|
|
|
|
|
var response = await templateService.RenderFunctionResponse(agent.Id, fn);
|
|
|
|
|
if (!string.IsNullOrEmpty(response))
|
|
|
|
|
{
|
|
|
|
|
await onMessageReceived(new RoleDialogModel(AgentRole.Assistant, response));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// After function is executed, pass the result to LLM to get a natural response
|
|
|
|
|
wholeDialogs.Add(fn);
|
|
|
|
|
|
|
|
|
|
await ExecuteInstructionRecursively(agent,
|
|
|
|
|
wholeDialogs,
|
|
|
|
|
onMessageReceived,
|
|
|
|
|
onFunctionExecuting,
|
|
|
|
|
onFunctionExecuted);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task HandleFunctionMessage(RoleDialogModel msg,
|
|
|
|
|
Func<RoleDialogModel, Task> onFunctionExecuting,
|
|
|
|
|
Func<RoleDialogModel, Task> onFunctionExecuted)
|
|
|
|
|
{
|
|
|
|
|
// Call functions
|
|
|
|
|
await onFunctionExecuting(msg);
|
|
|
|
|
await CallFunctions(msg);
|
|
|
|
|
await onFunctionExecuted(msg);
|
|
|
|
|
}
|
|
|
|
|
}
|