Merge pull request #187 from hchen2020/master
Use text completion for instruct mode.
This commit is contained in:
commit
ea3bf3e711
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
namespace BotSharp.Abstraction.MLTasks.Settings;
|
||||
|
||||
public class ChatCompletionSetting
|
||||
{
|
||||
public string Provider { get; set; }
|
||||
public string Model { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
namespace BotSharp.Abstraction.MLTasks.Settings;
|
||||
|
||||
public class TextCompletionSetting
|
||||
{
|
||||
public string Provider { get; set; }
|
||||
public string Model { get; set; }
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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 = "";
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}";
|
||||
}
|
||||
}
|
||||
|
|
@ -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": {
|
||||
|
|
|
|||
Loading…
Reference in a new issue