Add Instruct Mode and Open API.

This commit is contained in:
Haiping Chen 2023-09-01 17:26:25 -05:00
parent 55076d5104
commit c2cb80bc67
9 changed files with 220 additions and 3 deletions

View file

@ -1,4 +1,3 @@
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.MLTasks;
namespace BotSharp.Abstraction.Conversations;

View file

@ -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);
}

View file

@ -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; }
}

View file

@ -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;
}

View file

@ -1,4 +1,3 @@
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Functions;
namespace BotSharp.Core.Conversations.Services;

View file

@ -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)

View file

@ -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);
}
}
}
}

View file

@ -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));
}
}

View file

@ -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;
}
}