Add IInstructHook.

This commit is contained in:
hchen 2023-09-08 12:03:49 -05:00
parent 760a047d71
commit d3e33a9aa4
9 changed files with 102 additions and 29 deletions

View file

@ -0,0 +1,9 @@
using BotSharp.Abstraction.Instructs.Models;
namespace BotSharp.Abstraction.Instructs;
public interface IInstructHook
{
Task BeforeCompletion(RoleDialogModel message);
Task AfterCompletion(InstructResult result);
}

View file

@ -1,9 +1,11 @@
using BotSharp.Abstraction.Instructs.Models;
namespace BotSharp.Abstraction.Instructs;
public interface IInstructService
{
Task<bool> ExecuteInstructionRecursively(Agent agent,
List<RoleDialogModel> wholeDialogs,
Task<InstructResult> ExecuteInstruction(Agent agent,
RoleDialogModel message,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting,
Func<RoleDialogModel, Task> onFunctionExecuted);

View file

@ -0,0 +1,16 @@
using BotSharp.Abstraction.Instructs.Models;
namespace BotSharp.Abstraction.Instructs;
public class InstructHookBase : IInstructHook
{
public virtual async Task AfterCompletion(InstructResult result)
{
return;
}
public virtual async Task BeforeCompletion(RoleDialogModel message)
{
return;
}
}

View file

@ -5,6 +5,9 @@ namespace BotSharp.Core.Agents.Services;
public partial class AgentService
{
#if !DEBUG
[MemoryCache(10 * 60)]
#endif
public async Task<Agent> LoadAgent(string id)
{
var hooks = _services.GetServices<IAgentHook>();

View file

@ -73,6 +73,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Aspects.Cache" Version="2.0.3" />
<PackageReference Include="EntityFrameworkCore.BootKit" Version="6.2.1" />
<PackageReference Include="Fluid.Core" Version="2.4.0" />
<PackageReference Include="TensorFlow.Keras" Version="0.11.2" />

View file

@ -1,8 +1,10 @@
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Instructs;
using BotSharp.Abstraction.Instructs.Models;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Templating;
using System.IO;
namespace BotSharp.Core.Instructs;
@ -17,7 +19,53 @@ public partial class InstructService : IInstructService
_logger = logger;
}
public async Task<bool> ExecuteInstructionRecursively(Agent agent,
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>
{
new RoleDialogModel("user", message.Content)
};
// 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 =>
{
response.Data = fn.ExecutionData;
await onFunctionExecuted(fn);
});
foreach (var hook in hooks)
{
await hook.AfterCompletion(response);
}
return response;
}
private async Task<bool> ExecuteInstructionRecursively(Agent agent,
List<RoleDialogModel> wholeDialogs,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting,
@ -28,6 +76,8 @@ public partial class InstructService : IInstructService
var result = await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs, async msg =>
{
await onMessageReceived(msg);
wholeDialogs.Add(msg);
}, async fn =>
{
var preAgentId = agent.Id;

View file

@ -1,3 +1,4 @@
using Aspects.Cache;
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Routing.Settings;
@ -28,6 +29,7 @@ public class Router : IAgentRouting
return await agentService.LoadAgent(AgentId);
}
[MemoryCache(10 * 60)]
public RoutingRecord[] GetRoutingRecords()
{
var agentSettings = _services.GetRequiredService<AgentSettings>();

View file

@ -22,4 +22,5 @@ global using BotSharp.Core.Repository.DbTables;
global using BotSharp.Core.Agents.Services;
global using BotSharp.Core.Conversations.Services;
global using BotSharp.Core.Infrastructures;
global using BotSharp.Core.Users.Services;
global using BotSharp.Core.Users.Services;
global using Aspects.Cache;

View file

@ -1,9 +1,9 @@
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.ApiAdapters;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Instructs;
using BotSharp.Abstraction.Instructs.Models;
using BotSharp.OpenAPI.ViewModels.Conversations;
using BotSharp.OpenAPI.ViewModels.Instructs;
namespace BotSharp.OpenAPI.Controllers;
@ -13,43 +13,32 @@ namespace BotSharp.OpenAPI.Controllers;
public class InstructModeController : ControllerBase, IApiAdapter
{
private readonly IServiceProvider _services;
private readonly IUserIdentity _user;
public InstructModeController(IServiceProvider services,
IUserIdentity user)
public InstructModeController(IServiceProvider services)
{
_services = services;
_user = user;
}
[HttpPost("/instruct/{agentId}")]
public async Task<InstructResult> NewConversation([FromRoute] string agentId,
[FromBody] InstructMessageModel input)
{
var response = new InstructResult();
var instructor = _services.GetRequiredService<IInstructService>();
var agentService = _services.GetRequiredService<IAgentService>();
Agent agent = await agentService.LoadAgent(agentId);
await instructor.ExecuteInstructionRecursively(agent,
new List<RoleDialogModel>
{
new RoleDialogModel("user", input.Text)
},
async msg =>
{
response.Text = msg.Content;
},
async fnExecuting =>
{
// switch to different instruction template
if (!string.IsNullOrEmpty(input.TemplateName))
{
var agentSettings = _services.GetRequiredService<AgentSettings>();
var filePath = Path.Combine(agentService.GetAgentDataDir(agentId), $"{input.TemplateName}.{agentSettings.TemplateFormat}");
agent.Instruction = System.IO.File.ReadAllText(filePath);
}
},
async fnExecuted =>
{
response.Function = fnExecuted.FunctionName;
response.Data = fnExecuted.ExecutionData;
});
return response;
return await instructor.ExecuteInstruction(agent,
new RoleDialogModel(AgentRole.User, input.Text),
fn => Task.CompletedTask,
fn => Task.CompletedTask,
fn => Task.CompletedTask);
}
}