Add Instruct Mode and Open API.
This commit is contained in:
parent
55076d5104
commit
c2cb80bc67
|
|
@ -1,4 +1,3 @@
|
|||
using BotSharp.Abstraction.Conversations.Models;
|
||||
using BotSharp.Abstraction.MLTasks;
|
||||
|
||||
namespace BotSharp.Abstraction.Conversations;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
namespace BotSharp.Abstraction.Instructs;
|
||||
|
||||
public interface IInstructService
|
||||
{
|
||||
Task<bool> ExecuteInstructionRecursively(Agent agent,
|
||||
List<RoleDialogModel> wholeDialogs,
|
||||
Func<RoleDialogModel, Task> onMessageReceived,
|
||||
Func<RoleDialogModel, Task> onFunctionExecuting,
|
||||
Func<RoleDialogModel, Task> onFunctionExecuted);
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
namespace BotSharp.Abstraction.Instructs.Models;
|
||||
|
||||
public class InstructResult
|
||||
{
|
||||
public string Text { get; set; }
|
||||
public string Function { get; set; }
|
||||
public object Data { get; set; }
|
||||
}
|
||||
|
|
@ -9,6 +9,8 @@ using Microsoft.AspNetCore.Builder;
|
|||
using Microsoft.Extensions.Configuration;
|
||||
using BotSharp.Abstraction.Routing.Settings;
|
||||
using BotSharp.Abstraction.Templating;
|
||||
using BotSharp.Core.Instructs;
|
||||
using BotSharp.Abstraction.Instructs;
|
||||
|
||||
namespace BotSharp.Core;
|
||||
|
||||
|
|
@ -54,7 +56,6 @@ public static class BotSharpServiceCollectionExtensions
|
|||
services.AddScoped<Router>();
|
||||
services.AddScoped<Reasoner>();
|
||||
services.AddScoped<IAgentRouting, Router>();
|
||||
services.AddScoped<Reasoner>();
|
||||
|
||||
// Register function callback
|
||||
services.AddScoped<IFunctionCallback, RouteToAgentFn>();
|
||||
|
|
@ -64,6 +65,8 @@ public static class BotSharpServiceCollectionExtensions
|
|||
|
||||
services.AddScoped<Simulator>();
|
||||
|
||||
services.AddScoped<IInstructService, InstructService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using BotSharp.Abstraction.Conversations.Models;
|
||||
using BotSharp.Abstraction.Functions;
|
||||
|
||||
namespace BotSharp.Core.Conversations.Services;
|
||||
|
|
|
|||
|
|
@ -54,6 +54,11 @@ public class ConversationStateService : IConversationStateService, IDisposable
|
|||
|
||||
_state = new ConversationState();
|
||||
|
||||
if (_conversationId == null)
|
||||
{
|
||||
return _state;
|
||||
}
|
||||
|
||||
_file = GetStorageFile(_conversationId);
|
||||
|
||||
if (File.Exists(_file))
|
||||
|
|
@ -77,6 +82,11 @@ public class ConversationStateService : IConversationStateService, IDisposable
|
|||
|
||||
public void Save()
|
||||
{
|
||||
if (_conversationId == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var states = new List<string>();
|
||||
|
||||
foreach (var dic in _state)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
using BotSharp.Abstraction.Functions;
|
||||
|
||||
namespace BotSharp.Core.Instructs;
|
||||
|
||||
public partial class InstructService
|
||||
{
|
||||
private async Task CallFunctions(RoleDialogModel msg)
|
||||
{
|
||||
var hooks = _services.GetServices<IConversationHook>()
|
||||
.OrderBy(x => x.Priority).ToList();
|
||||
|
||||
// Invoke functions
|
||||
var functions = _services.GetServices<IFunctionCallback>()
|
||||
.Where(x => x.Name == msg.FunctionName)
|
||||
.ToList();
|
||||
|
||||
if (functions.Count == 0)
|
||||
{
|
||||
msg.Content = $"Can't find function implementation of {msg.FunctionName}.";
|
||||
_logger.LogError(msg.Content);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var fn in functions)
|
||||
{
|
||||
// Before executing functions
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
await hook.OnFunctionExecuting(msg);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Execute function
|
||||
await fn.Execute(msg);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
msg.ExecutionResult = ex.Message;
|
||||
_logger.LogError(msg.ExecutionResult);
|
||||
}
|
||||
|
||||
// After functions have been executed
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
await hook.OnFunctionExecuted(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
using BotSharp.Abstraction.Agents.Enums;
|
||||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Instructs;
|
||||
using BotSharp.Abstraction.MLTasks;
|
||||
using BotSharp.Abstraction.Templating;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public async Task<bool> ExecuteInstructionRecursively(Agent agent,
|
||||
List<RoleDialogModel> wholeDialogs,
|
||||
Func<RoleDialogModel, Task> onMessageReceived,
|
||||
Func<RoleDialogModel, Task> onFunctionExecuting,
|
||||
Func<RoleDialogModel, Task> onFunctionExecuted)
|
||||
{
|
||||
var chatCompletion = GetChatCompletion();
|
||||
|
||||
var result = await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs, async msg =>
|
||||
{
|
||||
await onMessageReceived(msg);
|
||||
}, async fn =>
|
||||
{
|
||||
var preAgentId = agent.Id;
|
||||
|
||||
await HandleFunctionMessage(fn, onFunctionExecuting, onFunctionExecuted);
|
||||
|
||||
// Function executed has exception
|
||||
if (fn.ExecutionResult == null || fn.StopCompletion)
|
||||
{
|
||||
await onMessageReceived(new RoleDialogModel(AgentRole.Assistant, fn.Content));
|
||||
return;
|
||||
}
|
||||
|
||||
fn.Content = fn.FunctionArgs.Replace("\r", " ").Replace("\n", " ").Trim() + " => " + fn.ExecutionResult;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
public IChatCompletion GetChatCompletion()
|
||||
{
|
||||
var completions = _services.GetServices<IChatCompletion>();
|
||||
var settings = _services.GetRequiredService<ConversationSetting>();
|
||||
return completions.FirstOrDefault(x => x.GetType().FullName.EndsWith(settings.ChatCompletion));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
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;
|
||||
|
||||
namespace BotSharp.OpenAPI.Controllers;
|
||||
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
public class InstructModeController : ControllerBase, IApiAdapter
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly IUserIdentity _user;
|
||||
|
||||
public InstructModeController(IServiceProvider services,
|
||||
IUserIdentity user)
|
||||
{
|
||||
_services = services;
|
||||
_user = user;
|
||||
}
|
||||
|
||||
[HttpPost("/instruct/{agentId}")]
|
||||
public async Task<InstructResult> NewConversation([FromRoute] string agentId,
|
||||
[FromBody] NewMessageModel 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 =>
|
||||
{
|
||||
|
||||
},
|
||||
async fnExecuted =>
|
||||
{
|
||||
response.Function = fnExecuted.FunctionName;
|
||||
response.Data = fnExecuted.ExecutionData;
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue