Merge pull request #187 from hchen2020/master

Use text completion for instruct mode.
This commit is contained in:
Haiping 2023-10-23 18:22:40 -05:00 committed by GitHub
commit ea3bf3e711
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 86 additions and 153 deletions

View file

@ -4,9 +4,5 @@ namespace BotSharp.Abstraction.Instructs;
public interface IInstructService
{
Task<InstructResult> ExecuteInstruction(Agent agent,
RoleDialogModel message,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting,
Func<RoleDialogModel, Task> onFunctionExecuted);
Task<InstructResult> Execute(Agent agent, RoleDialogModel message);
}

View file

@ -3,6 +3,5 @@ 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

@ -0,0 +1,7 @@
namespace BotSharp.Abstraction.MLTasks.Settings;
public class ChatCompletionSetting
{
public string Provider { get; set; }
public string Model { get; set; }
}

View file

@ -0,0 +1,7 @@
namespace BotSharp.Abstraction.MLTasks.Settings;
public class TextCompletionSetting
{
public string Provider { get; set; }
public string Model { get; set; }
}

View file

@ -16,6 +16,7 @@ using BotSharp.Abstraction.Evaluations.Settings;
using BotSharp.Abstraction.Evaluations;
using BotSharp.Core.Evaluatings;
using BotSharp.Core.Evaluations;
using BotSharp.Abstraction.MLTasks.Settings;
namespace BotSharp.Core;
@ -48,6 +49,14 @@ public static class BotSharpServiceCollectionExtensions
config.Bind("Database", myDatabaseSettings);
services.AddSingleton((IServiceProvider x) => myDatabaseSettings);
var textCompletionSettings = new TextCompletionSetting();
config.Bind("TextCompletion", textCompletionSettings);
services.AddSingleton((IServiceProvider x) => textCompletionSettings);
var chatCompletionSettings = new ChatCompletionSetting();
config.Bind("ChatCompletion", chatCompletionSettings);
services.AddSingleton((IServiceProvider x) => chatCompletionSettings);
RegisterPlugins(services, config);
// Register template render

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.MLTasks.Settings;
namespace BotSharp.Core.Infrastructures;
@ -6,18 +7,19 @@ public class CompletionProvider
{
public static IChatCompletion GetChatCompletion(IServiceProvider services, string? provider = null, string? model = null)
{
var settings = services.GetRequiredService<ChatCompletionSetting>();
var completions = services.GetServices<IChatCompletion>();
var state = services.GetRequiredService<IConversationStateService>();
if (string.IsNullOrEmpty(provider))
{
provider = state.GetState("provider", "azure-openai");
provider = state.GetState("provider", settings.Provider ?? "azure-openai");
}
if (string.IsNullOrEmpty(model))
{
model = state.GetState("model", "gpt-3.5-turbo");
model = state.GetState("model", settings.Model ?? "gpt-3.5-turbo");
}
var completer = completions.FirstOrDefault(x => x.Provider == provider);
@ -34,18 +36,19 @@ public class CompletionProvider
public static ITextCompletion GetTextCompletion(IServiceProvider services, string? provider = null, string? model = null)
{
var settings = services.GetRequiredService<TextCompletionSetting>();
var completions = services.GetServices<ITextCompletion>();
var state = services.GetRequiredService<IConversationStateService>();
if (string.IsNullOrEmpty(provider))
{
provider = state.GetState("provider", "azure-openai");
provider = state.GetState("provider", settings.Provider ?? "azure-openai");
}
if (string.IsNullOrEmpty(model))
{
model = state.GetState("model", "gpt-3.5-turbo");
model = state.GetState("model", settings.Model ?? "gpt-3.5-turbo");
}
var completer = completions.FirstOrDefault(x => x.Provider == provider);

View file

@ -1,10 +1,6 @@
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;
@ -19,19 +15,8 @@ public partial class InstructService : IInstructService
_logger = logger;
}
public async Task<InstructResult> ExecuteInstruction(Agent agent,
RoleDialogModel message,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting,
Func<RoleDialogModel, Task> onFunctionExecuted)
public async Task<InstructResult> Execute(Agent agent, RoleDialogModel message)
{
var response = new InstructResult();
var wholeDialogs = new List<RoleDialogModel>
{
message
};
// Trigger before completion hooks
var hooks = _services.GetServices<IInstructHook>();
foreach (var hook in hooks)
@ -39,23 +24,13 @@ public partial class InstructService : IInstructService
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.Data;
await onFunctionExecuted(fn);
});
var completer = CompletionProvider.GetTextCompletion(_services);
var result = await completer.GetCompletion(agent.Instruction);
var response = new InstructResult
{
Text = result
};
foreach (var hook in hooks)
{
@ -64,64 +39,4 @@ public partial class InstructService : IInstructService
return response;
}
private async Task<bool> ExecuteInstructionRecursively(Agent agent,
List<RoleDialogModel> wholeDialogs,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting,
Func<RoleDialogModel, Task> onFunctionExecuted)
{
var chatCompletion = CompletionProvider.GetChatCompletion(_services);
var result = await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs, async msg =>
{
await onMessageReceived(msg);
wholeDialogs.Add(msg);
}, async fn =>
{
var preAgentId = agent.Id;
await HandleFunctionMessage(fn, onFunctionExecuting, onFunctionExecuted);
// Function executed has exception
if (fn.Content == null || fn.StopCompletion)
{
await onMessageReceived(new RoleDialogModel(AgentRole.Assistant, fn.Content));
return;
}
fn.Content = fn.FunctionArgs.Replace("\r", " ").Replace("\n", " ").Trim() + " => " + fn.Content;
// 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);
}
}

View file

@ -4,6 +4,7 @@ using BotSharp.Abstraction.ApiAdapters;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Instructs;
using BotSharp.Abstraction.Instructs.Models;
using BotSharp.Abstraction.Templating;
using BotSharp.Core.Infrastructures;
using BotSharp.OpenAPI.ViewModels.Instructs;
@ -24,34 +25,37 @@ public class InstructModeController : ControllerBase, IApiAdapter
public async Task<InstructResult> InstructCompletion([FromRoute] string agentId,
[FromBody] InstructMessageModel input)
{
var instructor = _services.GetRequiredService<IInstructService>();
var state = _services.GetRequiredService<IConversationStateService>();
input.States.ForEach(x => state.SetState(x.Split('=')[0], x.Split('=')[1]));
state.SetState("provider", input.Provider)
.SetState("model", input.Model)
.SetState("input_text", input.Text);
var agentService = _services.GetRequiredService<IAgentService>();
Agent agent = await agentService.LoadAgent(agentId);
// switch to different instruction template
if (!string.IsNullOrEmpty(input.Template))
{
agent.Instruction = agent.Templates.First(x => x.Name == input.Template).Content;
var template = agent.Templates.First(x => x.Name == input.Template).Content;
var render = _services.GetRequiredService<ITemplateRender>();
var dict = new Dictionary<string, object>();
state.GetStates().Select(x => dict[x.Key] = x.Value).ToArray();
var prompt = render.Render(template, dict);
agent.Instruction = prompt;
}
var conv = _services.GetRequiredService<IConversationService>();
input.States.ForEach(x => conv.States.SetState(x.Split('=')[0], x.Split('=')[1]));
conv.States.SetState("provider", input.Provider)
.SetState("model", input.Model);
return await instructor.ExecuteInstruction(agent,
new RoleDialogModel(AgentRole.User, input.Text),
fn => Task.CompletedTask,
fn => Task.CompletedTask,
fn => Task.CompletedTask);
var instructor = _services.GetRequiredService<IInstructService>();
return await instructor.Execute(agent,
new RoleDialogModel(AgentRole.User, input.Text));
}
[HttpPost("/instruct/text-completion")]
public async Task<string> TextCompletion([FromBody] IncomingMessageModel input)
{
var conv = _services.GetRequiredService<IConversationService>();
input.States.ForEach(x => conv.States.SetState(x.Split('=')[0], x.Split('=')[1]));
conv.States.SetState("provider", input.Provider)
var state = _services.GetRequiredService<IConversationStateService>();
input.States.ForEach(x => state.SetState(x.Split('=')[0], x.Split('=')[1]));
state.SetState("provider", input.Provider)
.SetState("model", input.Model);
var textCompletion = CompletionProvider.GetTextCompletion(_services);

View file

@ -24,7 +24,7 @@ public class AzureOpenAiPlugin : IBotSharpPlugin
config.Bind("AzureOpenAi", settings);
services.AddSingleton(x =>
{
Console.WriteLine($"Loaded AzureOpenAi settings: {settings.DeploymentModel} ({settings.Endpoint}) {settings.ApiKey.SubstringMax(4)}");
Console.WriteLine($"Loaded AzureOpenAi settings: ({settings.Endpoint}) {settings.ApiKey.SubstringMax(4)}");
return settings;
});

View file

@ -43,10 +43,10 @@ public class ChatCompletionProvider : IChatCompletion
Task.WaitAll(hooks.Select(hook =>
hook.BeforeGenerating(agent, conversations)).ToArray());
var (client, deploymentModel) = ProviderHelper.GetClient(_model, _settings);
var client = ProviderHelper.GetClient(_model, _settings);
var chatCompletionsOptions = PrepareOptions(agent, conversations);
var response = client.GetChatCompletions(deploymentModel, chatCompletionsOptions);
var response = client.GetChatCompletions(_model, chatCompletionsOptions);
var choice = response.Value.Choices[0];
var message = choice.Message;
@ -96,10 +96,10 @@ public class ChatCompletionProvider : IChatCompletion
Task.WaitAll(hooks.Select(hook =>
hook.BeforeGenerating(agent, conversations)).ToArray());
var (client, deploymentModel) = ProviderHelper.GetClient(_model, _settings);
var client = ProviderHelper.GetClient(_model, _settings);
var chatCompletionsOptions = PrepareOptions(agent, conversations);
var response = await client.GetChatCompletionsAsync(deploymentModel, chatCompletionsOptions);
var response = await client.GetChatCompletionsAsync(_model, chatCompletionsOptions);
var choice = response.Value.Choices[0];
var message = choice.Message;
@ -148,10 +148,10 @@ public class ChatCompletionProvider : IChatCompletion
public async Task<bool> GetChatCompletionsStreamingAsync(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived)
{
var client = new OpenAIClient(new Uri(_settings.Endpoint), new AzureKeyCredential(_settings.ApiKey));
var client = ProviderHelper.GetClient(_model, _settings);
var chatCompletionsOptions = PrepareOptions(agent, conversations);
var response = await client.GetChatCompletionsStreamingAsync(_settings.DeploymentModel.ChatCompletionModel, chatCompletionsOptions);
var response = await client.GetChatCompletionsStreamingAsync(_model, chatCompletionsOptions);
using StreamingChatCompletions streaming = response.Value;
string output = "";

View file

@ -9,17 +9,17 @@ namespace BotSharp.Plugin.AzureOpenAI.Providers;
public class ProviderHelper
{
public static (OpenAIClient, string) GetClient(string model, AzureOpenAiSettings settings)
public static OpenAIClient GetClient(string model, AzureOpenAiSettings settings)
{
if (model == "gpt-4")
{
var client = new OpenAIClient(new Uri(settings.GPT4.Endpoint), new AzureKeyCredential(settings.GPT4.ApiKey));
return (client, settings.GPT4.DeploymentModel);
return client;
}
else
{
var client = new OpenAIClient(new Uri(settings.Endpoint), new AzureKeyCredential(settings.ApiKey));
return (client, settings.DeploymentModel.ChatCompletionModel);
return client;
}
}

View file

@ -37,9 +37,13 @@ public class TextCompletionProvider : ITextCompletion
// Before chat completion hook
Task.WaitAll(hooks.Select(hook =>
hook.BeforeGenerating(new Agent(), new List<RoleDialogModel> { new RoleDialogModel(AgentRole.User, text) })).ToArray());
hook.BeforeGenerating(new Agent(),
new List<RoleDialogModel>
{
new RoleDialogModel(AgentRole.User, text)
})).ToArray());
var (client, _) = ProviderHelper.GetClient(_model, _settings);
var client = ProviderHelper.GetClient(_model, _settings);
var completionsOptions = new CompletionsOptions()
{
@ -58,7 +62,7 @@ public class TextCompletionProvider : ITextCompletion
completionsOptions.NucleusSamplingFactor = samplingFactor;
var response = await client.GetCompletionsAsync(
deploymentOrModelName: _settings.DeploymentModel.TextCompletionModel,
deploymentOrModelName: _model,
completionsOptions);
// OpenAI

View file

@ -1,13 +1,8 @@
using BotSharp.Abstraction.Routing.Settings;
namespace BotSharp.Plugin.AzureOpenAI.Settings;
public class AzureOpenAiSettings
{
public string ApiKey { get; set; } = string.Empty;
public string Endpoint { get; set; } = string.Empty;
public DeploymentModelSetting DeploymentModel { get; set; }
= new DeploymentModelSetting();
public GPT4Settings GPT4 { get; set; }
}

View file

@ -1,12 +0,0 @@
namespace BotSharp.Plugin.AzureOpenAI.Settings;
public class DeploymentModelSetting
{
public string ChatCompletionModel { get; set; } = string.Empty;
public string? TextCompletionModel { get; set; }
public override string ToString()
{
return $"ChatCompletion - {ChatCompletionModel}, TextCompletion - {TextCompletionModel}";
}
}

View file

@ -46,13 +46,19 @@
"NumberOfGpuLayer": 10
},
"ChatCompletion": {
"Provider": "azure-openai",
"Model": "gpt-3.5-turbo"
},
"TextCompletion": {
"Provider": "azure-openai",
"Model": "gpt-3.5-turbo"
},
"AzureOpenAi": {
"ApiKey": "",
"Endpoint": "",
"DeploymentModel": {
"ChatCompletionModel": "",
"TextCompletionModel": ""
}
"Endpoint": ""
},
"GoogleAi": {