Merge pull request #137 from hchen2020/master
Support model switch in request level.
This commit is contained in:
commit
e83aa5c4e3
|
|
@ -1,5 +1,3 @@
|
||||||
using BotSharp.Abstraction.MLTasks;
|
|
||||||
|
|
||||||
namespace BotSharp.Abstraction.Conversations;
|
namespace BotSharp.Abstraction.Conversations;
|
||||||
|
|
||||||
public interface IConversationService
|
public interface IConversationService
|
||||||
|
|
@ -11,8 +9,6 @@ public interface IConversationService
|
||||||
Task<List<Conversation>> GetConversations();
|
Task<List<Conversation>> GetConversations();
|
||||||
Task DeleteConversation(string id);
|
Task DeleteConversation(string id);
|
||||||
|
|
||||||
IChatCompletion GetChatCompletion();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Send message to LLM
|
/// Send message to LLM
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ public class RoleDialogModel
|
||||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
public string Content { get; set; }
|
public string Content { get; set; }
|
||||||
public string CurrentAgentId { get; set; }
|
public string CurrentAgentId { get; set; }
|
||||||
|
public string ModelName { get; set; } = "gpt-3.5-turbo";
|
||||||
|
public float Temperature { get; set; } = 0.5f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Function name if LLM response function call
|
/// Function name if LLM response function call
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
using BotSharp.Abstraction.Conversations.Models;
|
|
||||||
|
|
||||||
namespace BotSharp.Abstraction.MLTasks;
|
namespace BotSharp.Abstraction.MLTasks;
|
||||||
|
|
||||||
public interface IChatCompletion
|
public interface IChatCompletion
|
||||||
{
|
{
|
||||||
|
string ModelName { get; }
|
||||||
Task<bool> GetChatCompletionsAsync(Agent agent,
|
Task<bool> GetChatCompletionsAsync(Agent agent,
|
||||||
List<RoleDialogModel> conversations,
|
List<RoleDialogModel> conversations,
|
||||||
Func<RoleDialogModel, Task> onMessageReceived,
|
Func<RoleDialogModel, Task> onMessageReceived,
|
||||||
|
|
|
||||||
|
|
@ -5,19 +5,19 @@ namespace BotSharp.Abstraction.Routing.Models;
|
||||||
public class RoutingRecord
|
public class RoutingRecord
|
||||||
{
|
{
|
||||||
[JsonPropertyName("agent_id")]
|
[JsonPropertyName("agent_id")]
|
||||||
public string AgentId { get; set; }
|
public string AgentId { get; set; } = string.Empty;
|
||||||
|
|
||||||
[JsonPropertyName("name")]
|
[JsonPropertyName("name")]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
[JsonPropertyName("description")]
|
[JsonPropertyName("description")]
|
||||||
public string Description { get; set; }
|
public string Description { get; set; } = string.Empty;
|
||||||
|
|
||||||
[JsonPropertyName("required")]
|
[JsonPropertyName("required")]
|
||||||
public List<string> RequiredFields { get; set; } = new List<string>();
|
public List<string> RequiredFields { get; set; } = new List<string>();
|
||||||
|
|
||||||
[JsonPropertyName("redirect_to")]
|
[JsonPropertyName("redirect_to")]
|
||||||
public string RedirectTo { get; set; }
|
public string? RedirectTo { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("disabled")]
|
[JsonPropertyName("disabled")]
|
||||||
public bool Disabled { get; set; }
|
public bool Disabled { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -83,4 +83,9 @@
|
||||||
<ProjectReference Include="..\BotSharp.Abstraction\BotSharp.Abstraction.csproj" />
|
<ProjectReference Include="..\BotSharp.Abstraction\BotSharp.Abstraction.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Functions\" />
|
||||||
|
<Folder Include="Hooks\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
using BotSharp.Abstraction.Functions;
|
using BotSharp.Abstraction.Functions;
|
||||||
using BotSharp.Core.Functions;
|
|
||||||
using BotSharp.Core.Hooks;
|
|
||||||
using BotSharp.Core.Routing;
|
using BotSharp.Core.Routing;
|
||||||
using BotSharp.Core.Templating;
|
using BotSharp.Core.Templating;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,14 @@ public partial class ConversationService
|
||||||
{
|
{
|
||||||
int currentRecursiveDepth = 0;
|
int currentRecursiveDepth = 0;
|
||||||
|
|
||||||
private async Task<bool> GetChatCompletionsAsyncRecursively(IChatCompletion chatCompletion,
|
private async Task<bool> GetChatCompletionsAsyncRecursively(Agent agent,
|
||||||
Agent agent,
|
|
||||||
List<RoleDialogModel> wholeDialogs,
|
List<RoleDialogModel> wholeDialogs,
|
||||||
Func<RoleDialogModel, Task> onMessageReceived,
|
Func<RoleDialogModel, Task> onMessageReceived,
|
||||||
Func<RoleDialogModel, Task> onFunctionExecuting,
|
Func<RoleDialogModel, Task> onFunctionExecuting,
|
||||||
Func<RoleDialogModel, Task> onFunctionExecuted)
|
Func<RoleDialogModel, Task> onFunctionExecuted)
|
||||||
{
|
{
|
||||||
|
var chatCompletion = CompletionProvider.GetChatCompletion(_services, wholeDialogs.Last().ModelName);
|
||||||
|
|
||||||
currentRecursiveDepth++;
|
currentRecursiveDepth++;
|
||||||
if (currentRecursiveDepth > _settings.MaxRecursiveDepth)
|
if (currentRecursiveDepth > _settings.MaxRecursiveDepth)
|
||||||
{
|
{
|
||||||
|
|
@ -28,11 +29,16 @@ public partial class ConversationService
|
||||||
text = latestResponse.Content.Split("=>").Last();
|
text = latestResponse.Content.Split("=>").Last();
|
||||||
}
|
}
|
||||||
|
|
||||||
await HandleAssistantMessage(new RoleDialogModel(AgentRole.Assistant, text)
|
var msg = new RoleDialogModel(AgentRole.Assistant, text)
|
||||||
{
|
{
|
||||||
CurrentAgentId = agent.Id,
|
CurrentAgentId = agent.Id,
|
||||||
Channel = wholeDialogs.Last().Channel
|
Channel = wholeDialogs.Last().Channel
|
||||||
}, onMessageReceived);
|
};
|
||||||
|
|
||||||
|
await HandleAssistantMessage(msg, onMessageReceived);
|
||||||
|
|
||||||
|
// Add to dialog history
|
||||||
|
_storage.Append(_conversationId, agent.Id, msg);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -85,8 +91,7 @@ public partial class ConversationService
|
||||||
|
|
||||||
wholeDialogs.Add(fn);
|
wholeDialogs.Add(fn);
|
||||||
|
|
||||||
await GetChatCompletionsAsyncRecursively(chatCompletion,
|
await GetChatCompletionsAsyncRecursively(agent,
|
||||||
agent,
|
|
||||||
wholeDialogs,
|
wholeDialogs,
|
||||||
onMessageReceived,
|
onMessageReceived,
|
||||||
onFunctionExecuting,
|
onFunctionExecuting,
|
||||||
|
|
@ -115,8 +120,7 @@ public partial class ConversationService
|
||||||
// After function is executed, pass the result to LLM to get a natural response
|
// After function is executed, pass the result to LLM to get a natural response
|
||||||
wholeDialogs.Add(fn);
|
wholeDialogs.Add(fn);
|
||||||
|
|
||||||
await GetChatCompletionsAsyncRecursively(chatCompletion,
|
await GetChatCompletionsAsyncRecursively(agent,
|
||||||
agent,
|
|
||||||
wholeDialogs,
|
wholeDialogs,
|
||||||
onMessageReceived,
|
onMessageReceived,
|
||||||
onFunctionExecuting,
|
onFunctionExecuting,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
using BotSharp.Abstraction.Agents.Enums;
|
using BotSharp.Abstraction.Agents.Enums;
|
||||||
using BotSharp.Abstraction.Agents.Models;
|
using BotSharp.Abstraction.Agents.Models;
|
||||||
using BotSharp.Abstraction.MLTasks;
|
|
||||||
using BotSharp.Abstraction.Routing.Settings;
|
using BotSharp.Abstraction.Routing.Settings;
|
||||||
using BotSharp.Core.Routing;
|
using BotSharp.Core.Routing;
|
||||||
|
|
||||||
|
|
@ -92,9 +91,7 @@ public partial class ConversationService
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var chatCompletion = GetChatCompletion();
|
var result = await GetChatCompletionsAsyncRecursively(agent,
|
||||||
var result = await GetChatCompletionsAsyncRecursively(chatCompletion,
|
|
||||||
agent,
|
|
||||||
wholeDialogs,
|
wholeDialogs,
|
||||||
onMessageReceived,
|
onMessageReceived,
|
||||||
onFunctionExecuting,
|
onFunctionExecuting,
|
||||||
|
|
@ -136,16 +133,4 @@ public partial class ConversationService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IChatCompletion GetChatCompletion()
|
|
||||||
{
|
|
||||||
var completions = _services.GetServices<IChatCompletion>();
|
|
||||||
return completions.FirstOrDefault(x => x.GetType().FullName.EndsWith(_settings.ChatCompletion));
|
|
||||||
}
|
|
||||||
|
|
||||||
public IChatCompletion GetGpt4ChatCompletion()
|
|
||||||
{
|
|
||||||
var completions = _services.GetServices<IChatCompletion>();
|
|
||||||
return completions.FirstOrDefault(x => x.GetType().FullName.EndsWith("GPT4CompletionProvider"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
using BotSharp.Abstraction.MLTasks;
|
||||||
|
|
||||||
|
namespace BotSharp.Core.Infrastructures;
|
||||||
|
|
||||||
|
public class CompletionProvider
|
||||||
|
{
|
||||||
|
public static IChatCompletion GetChatCompletion(IServiceProvider services, string modelName = "gpt-3.5-turbo")
|
||||||
|
{
|
||||||
|
var completions = services.GetServices<IChatCompletion>();
|
||||||
|
var settings = services.GetRequiredService<ConversationSetting>();
|
||||||
|
// completions.FirstOrDefault(x => x.GetType().FullName.EndsWith(settings.ChatCompletion));
|
||||||
|
return completions.FirstOrDefault(x => x.ModelName == modelName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -29,7 +29,7 @@ public partial class InstructService : IInstructService
|
||||||
|
|
||||||
var wholeDialogs = new List<RoleDialogModel>
|
var wholeDialogs = new List<RoleDialogModel>
|
||||||
{
|
{
|
||||||
new RoleDialogModel("user", message.Content)
|
message
|
||||||
};
|
};
|
||||||
|
|
||||||
// Trigger before completion hooks
|
// Trigger before completion hooks
|
||||||
|
|
@ -71,7 +71,7 @@ public partial class InstructService : IInstructService
|
||||||
Func<RoleDialogModel, Task> onFunctionExecuting,
|
Func<RoleDialogModel, Task> onFunctionExecuting,
|
||||||
Func<RoleDialogModel, Task> onFunctionExecuted)
|
Func<RoleDialogModel, Task> onFunctionExecuted)
|
||||||
{
|
{
|
||||||
var chatCompletion = GetChatCompletion();
|
var chatCompletion = CompletionProvider.GetChatCompletion(_services, wholeDialogs.Last().ModelName);
|
||||||
|
|
||||||
var result = await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs, async msg =>
|
var result = await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs, async msg =>
|
||||||
{
|
{
|
||||||
|
|
@ -124,11 +124,4 @@ public partial class InstructService : IInstructService
|
||||||
await CallFunctions(msg);
|
await CallFunctions(msg);
|
||||||
await onFunctionExecuted(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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
namespace BotSharp.Core.Hooks;
|
namespace BotSharp.Core.Routing;
|
||||||
|
|
||||||
public class ReasoningHook : AgentHookBase
|
public class ReasoningHook : AgentHookBase
|
||||||
{
|
{
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
using BotSharp.Abstraction.Conversations.Models;
|
|
||||||
using BotSharp.Abstraction.Functions;
|
using BotSharp.Abstraction.Functions;
|
||||||
|
using BotSharp.Abstraction.MLTasks;
|
||||||
using BotSharp.Abstraction.Routing.Models;
|
using BotSharp.Abstraction.Routing.Models;
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace BotSharp.Core.Functions;
|
namespace BotSharp.Core.Routing;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Router calls this function to set the Active Agent according to the context
|
/// Router calls this function to set the Active Agent according to the context
|
||||||
|
|
@ -63,26 +62,36 @@ public class RouteToAgentFn : IFunctionCallback
|
||||||
agentId = routingRule.AgentId;
|
agentId = routingRule.AgentId;
|
||||||
|
|
||||||
// Check required fields
|
// Check required fields
|
||||||
var jo = JsonSerializer.Deserialize<object>(message.FunctionArgs);
|
var root = JsonSerializer.Deserialize<JsonElement>(message.FunctionArgs);
|
||||||
bool hasMissingField = false;
|
bool hasMissingField = false;
|
||||||
|
string missingFieldName = "";
|
||||||
foreach (var field in routingRule.RequiredFields)
|
foreach (var field in routingRule.RequiredFields)
|
||||||
{
|
{
|
||||||
if (jo is JsonElement root)
|
if (!root.EnumerateObject().Any(x => x.Name == field))
|
||||||
{
|
{
|
||||||
if (!root.EnumerateObject().Any(x => x.Name == field))
|
message.ExecutionResult = $"missing {field}.";
|
||||||
{
|
hasMissingField = true;
|
||||||
message.ExecutionResult = $"missing {field}.";
|
missingFieldName = field;
|
||||||
hasMissingField = true;
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (root.EnumerateObject().Any(x => x.Name == field) &&
|
|
||||||
string.IsNullOrEmpty(root.EnumerateObject().FirstOrDefault(x => x.Name == field).Value.ToString()))
|
|
||||||
{
|
|
||||||
message.ExecutionResult = $"missing {field}.";
|
|
||||||
hasMissingField = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else if (root.EnumerateObject().Any(x => x.Name == field) &&
|
||||||
|
string.IsNullOrEmpty(root.EnumerateObject().FirstOrDefault(x => x.Name == field).Value.ToString()))
|
||||||
|
{
|
||||||
|
message.ExecutionResult = $"missing {field}.";
|
||||||
|
hasMissingField = true;
|
||||||
|
missingFieldName = field;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if states contains the field according conversation context.
|
||||||
|
var states = _services.GetRequiredService<IConversationStateService>();
|
||||||
|
if (!string.IsNullOrEmpty(states.GetState(missingFieldName)))
|
||||||
|
{
|
||||||
|
var value = states.GetState(missingFieldName);
|
||||||
|
message.FunctionArgs = message.FunctionArgs.Substring(0, message.FunctionArgs.Length - 1) + $", \"{missingFieldName}\": \"{value}\"" + "}";
|
||||||
|
hasMissingField = false;
|
||||||
|
missingFieldName = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasMissingField && !string.IsNullOrEmpty(routingRule.RedirectTo))
|
if (hasMissingField && !string.IsNullOrEmpty(routingRule.RedirectTo))
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
namespace BotSharp.Core.Hooks;
|
namespace BotSharp.Core.Routing;
|
||||||
|
|
||||||
public class RoutingHook : AgentHookBase
|
public class RoutingHook : AgentHookBase
|
||||||
{
|
{
|
||||||
|
|
@ -64,7 +64,7 @@ public class Simulator
|
||||||
new RoleDialogModel(AgentRole.User, @"What's the next step, your response must be in JSON format with ""function"" and ""parameters"". ")
|
new RoleDialogModel(AgentRole.User, @"What's the next step, your response must be in JSON format with ""function"" and ""parameters"". ")
|
||||||
};
|
};
|
||||||
|
|
||||||
var chatCompletion = GetGpt4ChatCompletion();
|
var chatCompletion = CompletionProvider.GetChatCompletion(_services, "gpt-4");
|
||||||
|
|
||||||
RoleDialogModel response = null;
|
RoleDialogModel response = null;
|
||||||
await chatCompletion.GetChatCompletionsAsync(reasoner, wholeDialogs, async msg
|
await chatCompletion.GetChatCompletionsAsync(reasoner, wholeDialogs, async msg
|
||||||
|
|
@ -111,7 +111,7 @@ public class Simulator
|
||||||
var agentService = _services.GetRequiredService<IAgentService>();
|
var agentService = _services.GetRequiredService<IAgentService>();
|
||||||
var agent = await agentService.LoadAgent(agentId);
|
var agent = await agentService.LoadAgent(agentId);
|
||||||
|
|
||||||
var chatCompletion = GetChatCompletion();
|
var chatCompletion = CompletionProvider.GetChatCompletion(_services, wholeDialogs.Last().ModelName);
|
||||||
|
|
||||||
RoleDialogModel response = null;
|
RoleDialogModel response = null;
|
||||||
await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs, async msg
|
await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs, async msg
|
||||||
|
|
@ -132,19 +132,6 @@ public class Simulator
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IChatCompletion GetChatCompletion()
|
|
||||||
{
|
|
||||||
var completions = _services.GetServices<IChatCompletion>();
|
|
||||||
var settings = _services.GetRequiredService<ConversationSetting>();
|
|
||||||
return completions.FirstOrDefault(x => x.GetType().FullName.EndsWith(settings.ChatCompletion));
|
|
||||||
}
|
|
||||||
|
|
||||||
public IChatCompletion GetGpt4ChatCompletion()
|
|
||||||
{
|
|
||||||
var completions = _services.GetServices<IChatCompletion>();
|
|
||||||
return completions.FirstOrDefault(x => x.GetType().FullName.EndsWith("GPT4CompletionProvider"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SaveStateByArgs(JsonDocument args)
|
private void SaveStateByArgs(JsonDocument args)
|
||||||
{
|
{
|
||||||
var stateService = _services.GetRequiredService<IConversationStateService>();
|
var stateService = _services.GetRequiredService<IConversationStateService>();
|
||||||
|
|
|
||||||
|
|
@ -40,11 +40,10 @@ public class ConversationController : ControllerBase, IApiAdapter
|
||||||
[HttpPost("/conversation/{agentId}/{conversationId}")]
|
[HttpPost("/conversation/{agentId}/{conversationId}")]
|
||||||
public async Task<MessageResponseModel> SendMessage([FromRoute] string agentId,
|
public async Task<MessageResponseModel> SendMessage([FromRoute] string agentId,
|
||||||
[FromRoute] string conversationId,
|
[FromRoute] string conversationId,
|
||||||
[FromBody] NewMessageModel input,
|
[FromBody] NewMessageModel input)
|
||||||
[FromQuery] string? channel = "openapi")
|
|
||||||
{
|
{
|
||||||
var conv = _services.GetRequiredService<IConversationService>();
|
var conv = _services.GetRequiredService<IConversationService>();
|
||||||
conv.SetConversationId(conversationId, channel);
|
conv.SetConversationId(conversationId, input.Channel);
|
||||||
input.States.ForEach(x => conv.States.SetState(x.Split('=')[0], x.Split('=')[1]));
|
input.States.ForEach(x => conv.States.SetState(x.Split('=')[0], x.Split('=')[1]));
|
||||||
|
|
||||||
var response = new MessageResponseModel();
|
var response = new MessageResponseModel();
|
||||||
|
|
@ -53,7 +52,8 @@ public class ConversationController : ControllerBase, IApiAdapter
|
||||||
await conv.SendMessage(agentId,
|
await conv.SendMessage(agentId,
|
||||||
new RoleDialogModel("user", input.Text)
|
new RoleDialogModel("user", input.Text)
|
||||||
{
|
{
|
||||||
Channel = channel
|
Channel = input.Channel,
|
||||||
|
ModelName = input.ModelName
|
||||||
},
|
},
|
||||||
async msg =>
|
async msg =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,10 @@ public class InstructModeController : ControllerBase, IApiAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
return await instructor.ExecuteInstruction(agent,
|
return await instructor.ExecuteInstruction(agent,
|
||||||
new RoleDialogModel(AgentRole.User, input.Text),
|
new RoleDialogModel(AgentRole.User, input.Text)
|
||||||
|
{
|
||||||
|
ModelName = input.ModelName
|
||||||
|
},
|
||||||
fn => Task.CompletedTask,
|
fn => Task.CompletedTask,
|
||||||
fn => Task.CompletedTask,
|
fn => Task.CompletedTask,
|
||||||
fn => Task.CompletedTask);
|
fn => Task.CompletedTask);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ namespace BotSharp.OpenAPI.ViewModels.Conversations;
|
||||||
public class NewMessageModel
|
public class NewMessageModel
|
||||||
{
|
{
|
||||||
public string Text { get; set; }
|
public string Text { get; set; }
|
||||||
|
public string ModelName { get; set; } = "gpt-3.5-turbo";
|
||||||
|
public string Channel { get; set; } = "openapi";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Conversation states from input
|
/// Conversation states from input
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ public class ChatCompletionProvider : IChatCompletion
|
||||||
private readonly IServiceProvider _services;
|
private readonly IServiceProvider _services;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
public virtual string ModelName => "gpt-3.5-turbo";
|
||||||
|
|
||||||
public ChatCompletionProvider(AzureOpenAiSettings settings,
|
public ChatCompletionProvider(AzureOpenAiSettings settings,
|
||||||
ILogger<ChatCompletionProvider> logger,
|
ILogger<ChatCompletionProvider> logger,
|
||||||
IServiceProvider services)
|
IServiceProvider services)
|
||||||
|
|
@ -32,10 +34,10 @@ public class ChatCompletionProvider : IChatCompletion
|
||||||
_services = services;
|
_services = services;
|
||||||
}
|
}
|
||||||
|
|
||||||
private OpenAIClient GetClient()
|
protected virtual (OpenAIClient, string) GetClient()
|
||||||
{
|
{
|
||||||
var client = new OpenAIClient(new Uri(_settings.Endpoint), new AzureKeyCredential(_settings.ApiKey));
|
var client = new OpenAIClient(new Uri(_settings.Endpoint), new AzureKeyCredential(_settings.ApiKey));
|
||||||
return client;
|
return (client, _settings.DeploymentModel.ChatCompletionModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<RoleDialogModel> GetChatSamples(string sampleText)
|
public List<RoleDialogModel> GetChatSamples(string sampleText)
|
||||||
|
|
@ -89,10 +91,10 @@ public class ChatCompletionProvider : IChatCompletion
|
||||||
Func<RoleDialogModel, Task> onMessageReceived,
|
Func<RoleDialogModel, Task> onMessageReceived,
|
||||||
Func<RoleDialogModel, Task> onFunctionExecuting)
|
Func<RoleDialogModel, Task> onFunctionExecuting)
|
||||||
{
|
{
|
||||||
var client = GetClient();
|
var (client, deploymentModel) = GetClient();
|
||||||
var chatCompletionsOptions = PrepareOptions(agent, conversations);
|
var chatCompletionsOptions = PrepareOptions(agent, conversations);
|
||||||
|
|
||||||
var response = await client.GetChatCompletionsAsync(_settings.DeploymentModel.ChatCompletionModel, chatCompletionsOptions);
|
var response = await client.GetChatCompletionsAsync(deploymentModel, chatCompletionsOptions);
|
||||||
var choice = response.Value.Choices[0];
|
var choice = response.Value.Choices[0];
|
||||||
var message = choice.Message;
|
var message = choice.Message;
|
||||||
|
|
||||||
|
|
@ -110,6 +112,12 @@ public class ChatCompletionProvider : IChatCompletion
|
||||||
Channel = conversations.Last().Channel
|
Channel = conversations.Last().Channel
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Somethings LLM will generate a function name with agent name.
|
||||||
|
if (!string.IsNullOrEmpty(funcContextIn.FunctionName))
|
||||||
|
{
|
||||||
|
funcContextIn.FunctionName = funcContextIn.FunctionName.Split('.').Last();
|
||||||
|
}
|
||||||
|
|
||||||
// Execute functions
|
// Execute functions
|
||||||
await onFunctionExecuting(funcContextIn);
|
await onFunctionExecuting(funcContextIn);
|
||||||
}
|
}
|
||||||
|
|
@ -175,7 +183,7 @@ public class ChatCompletionProvider : IChatCompletion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private ChatCompletionsOptions PrepareOptions(Agent agent, List<RoleDialogModel> conversations)
|
protected ChatCompletionsOptions PrepareOptions(Agent agent, List<RoleDialogModel> conversations)
|
||||||
{
|
{
|
||||||
var chatCompletionsOptions = new ChatCompletionsOptions();
|
var chatCompletionsOptions = new ChatCompletionsOptions();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,240 +1,31 @@
|
||||||
using Azure;
|
using Azure;
|
||||||
using Azure.AI.OpenAI;
|
using Azure.AI.OpenAI;
|
||||||
using BotSharp.Abstraction.Agents.Enums;
|
|
||||||
using BotSharp.Abstraction.Agents.Models;
|
|
||||||
using BotSharp.Abstraction.Conversations.Models;
|
|
||||||
using BotSharp.Abstraction.Conversations.Settings;
|
|
||||||
using BotSharp.Abstraction.Functions.Models;
|
|
||||||
using BotSharp.Abstraction.MLTasks;
|
|
||||||
using BotSharp.Plugin.AzureOpenAI.Settings;
|
using BotSharp.Plugin.AzureOpenAI.Settings;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace BotSharp.Plugin.AzureOpenAI.Providers;
|
namespace BotSharp.Plugin.AzureOpenAI.Providers;
|
||||||
|
|
||||||
public class GPT4CompletionProvider : IChatCompletion
|
public class GPT4CompletionProvider : ChatCompletionProvider
|
||||||
{
|
{
|
||||||
private readonly AzureOpenAiSettings _settings;
|
private readonly AzureOpenAiSettings _settings;
|
||||||
private readonly IServiceProvider _services;
|
private readonly IServiceProvider _services;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
public override string ModelName => "gpt-4";
|
||||||
|
|
||||||
public GPT4CompletionProvider(AzureOpenAiSettings settings,
|
public GPT4CompletionProvider(AzureOpenAiSettings settings,
|
||||||
ILogger<GPT4CompletionProvider> logger,
|
ILogger<GPT4CompletionProvider> logger,
|
||||||
IServiceProvider services)
|
IServiceProvider services) : base(settings, logger, services)
|
||||||
{
|
{
|
||||||
_settings = settings;
|
_settings = settings;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_services = services;
|
_services = services;
|
||||||
}
|
}
|
||||||
|
|
||||||
private OpenAIClient GetClient()
|
protected override (OpenAIClient, string) GetClient()
|
||||||
{
|
{
|
||||||
var client = new OpenAIClient(new Uri(_settings.GPT4.Endpoint), new AzureKeyCredential(_settings.GPT4.ApiKey));
|
var client = new OpenAIClient(new Uri(_settings.GPT4.Endpoint), new AzureKeyCredential(_settings.GPT4.ApiKey));
|
||||||
return client;
|
return (client, _settings.GPT4.DeploymentModel);
|
||||||
}
|
|
||||||
|
|
||||||
public List<RoleDialogModel> GetChatSamples(string sampleText)
|
|
||||||
{
|
|
||||||
var samples = new List<RoleDialogModel>();
|
|
||||||
if (string.IsNullOrEmpty(sampleText))
|
|
||||||
{
|
|
||||||
return samples;
|
|
||||||
}
|
|
||||||
|
|
||||||
var lines = sampleText.Split('\n');
|
|
||||||
for (int i = 0; i < lines.Length; i++)
|
|
||||||
{
|
|
||||||
var line = lines[i];
|
|
||||||
if (string.IsNullOrEmpty(line.Trim()))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var role = line.Substring(0, line.IndexOf(' ') - 1).Trim();
|
|
||||||
var content = line.Substring(line.IndexOf(' ') + 1).Trim();
|
|
||||||
|
|
||||||
// comments
|
|
||||||
if (role == "##")
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
samples.Add(new RoleDialogModel(role, content));
|
|
||||||
}
|
|
||||||
|
|
||||||
return samples;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<FunctionDef> GetFunctions(string functionsJson)
|
|
||||||
{
|
|
||||||
var functions = new List<FunctionDef>();
|
|
||||||
if (!string.IsNullOrEmpty(functionsJson))
|
|
||||||
{
|
|
||||||
functions = JsonSerializer.Deserialize<List<FunctionDef>>(functionsJson, new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
PropertyNameCaseInsensitive = true,
|
|
||||||
AllowTrailingCommas = true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return functions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> GetChatCompletionsAsync(Agent agent,
|
|
||||||
List<RoleDialogModel> conversations,
|
|
||||||
Func<RoleDialogModel, Task> onMessageReceived,
|
|
||||||
Func<RoleDialogModel, Task> onFunctionExecuting)
|
|
||||||
{
|
|
||||||
var client = GetClient();
|
|
||||||
var chatCompletionsOptions = PrepareOptions(agent, conversations);
|
|
||||||
|
|
||||||
var response = await client.GetChatCompletionsAsync(_settings.GPT4.DeploymentModel, chatCompletionsOptions);
|
|
||||||
var choice = response.Value.Choices[0];
|
|
||||||
var message = choice.Message;
|
|
||||||
|
|
||||||
if (choice.FinishReason == CompletionsFinishReason.FunctionCall)
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"[{agent.Name}]: {message.FunctionCall.Name} => {message.FunctionCall.Arguments}");
|
|
||||||
|
|
||||||
var funcContextIn = new RoleDialogModel(AgentRole.Function, message.Content)
|
|
||||||
{
|
|
||||||
CurrentAgentId = agent.Id,
|
|
||||||
FunctionName = message.FunctionCall.Name,
|
|
||||||
FunctionArgs = message.FunctionCall.Arguments,
|
|
||||||
Channel = conversations.Last().Channel
|
|
||||||
};
|
|
||||||
|
|
||||||
// Execute functions
|
|
||||||
await onFunctionExecuting(funcContextIn);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"[{agent.Name}] {message.Role}: {message.Content}");
|
|
||||||
|
|
||||||
var msg = new RoleDialogModel(AgentRole.Assistant, message.Content)
|
|
||||||
{
|
|
||||||
CurrentAgentId= agent.Id,
|
|
||||||
Channel = conversations.Last().Channel
|
|
||||||
};
|
|
||||||
|
|
||||||
// Text response received
|
|
||||||
await onMessageReceived(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 chatCompletionsOptions = PrepareOptions(agent, conversations);
|
|
||||||
|
|
||||||
var response = await client.GetChatCompletionsStreamingAsync(_settings.DeploymentModel.ChatCompletionModel, chatCompletionsOptions);
|
|
||||||
using StreamingChatCompletions streaming = response.Value;
|
|
||||||
|
|
||||||
string output = "";
|
|
||||||
await foreach (var choice in streaming.GetChoicesStreaming())
|
|
||||||
{
|
|
||||||
if (choice.FinishReason == CompletionsFinishReason.FunctionCall)
|
|
||||||
{
|
|
||||||
var args = "";
|
|
||||||
await foreach (var message in choice.GetMessageStreaming())
|
|
||||||
{
|
|
||||||
if (message.FunctionCall == null || message.FunctionCall.Arguments == null)
|
|
||||||
continue;
|
|
||||||
Console.Write(message.FunctionCall.Arguments);
|
|
||||||
args += message.FunctionCall.Arguments;
|
|
||||||
|
|
||||||
}
|
|
||||||
await onMessageReceived(new RoleDialogModel(ChatRole.Assistant.ToString(), args));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
await foreach (var message in choice.GetMessageStreaming())
|
|
||||||
{
|
|
||||||
if (message.Content == null)
|
|
||||||
continue;
|
|
||||||
Console.Write(message.Content);
|
|
||||||
output += message.Content;
|
|
||||||
|
|
||||||
_logger.LogInformation(message.Content);
|
|
||||||
|
|
||||||
await onMessageReceived(new RoleDialogModel(message.Role.ToString(), message.Content));
|
|
||||||
}
|
|
||||||
|
|
||||||
output = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private ChatCompletionsOptions PrepareOptions(Agent agent, List<RoleDialogModel> conversations)
|
|
||||||
{
|
|
||||||
var chatCompletionsOptions = new ChatCompletionsOptions();
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(agent.Instruction))
|
|
||||||
{
|
|
||||||
chatCompletionsOptions.Messages.Add(new ChatMessage(ChatRole.System, agent.Instruction));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(agent.Knowledges))
|
|
||||||
{
|
|
||||||
chatCompletionsOptions.Messages.Add(new ChatMessage(ChatRole.System, agent.Knowledges));
|
|
||||||
}
|
|
||||||
|
|
||||||
var samples = GetChatSamples(agent.Samples);
|
|
||||||
foreach (var message in samples)
|
|
||||||
{
|
|
||||||
chatCompletionsOptions.Messages.Add(new ChatMessage(message.Role, message.Content));
|
|
||||||
}
|
|
||||||
|
|
||||||
var functions = GetFunctions(agent.Functions);
|
|
||||||
foreach (var function in functions)
|
|
||||||
{
|
|
||||||
chatCompletionsOptions.Functions.Add(new FunctionDefinition
|
|
||||||
{
|
|
||||||
Name = function.Name,
|
|
||||||
Description = function.Description,
|
|
||||||
Parameters = BinaryData.FromObjectAsJson(function.Parameters)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var message in conversations)
|
|
||||||
{
|
|
||||||
if (message.Role == ChatRole.Function)
|
|
||||||
{
|
|
||||||
chatCompletionsOptions.Messages.Add(new ChatMessage(message.Role, message.Content)
|
|
||||||
{
|
|
||||||
Name = message.FunctionName
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
chatCompletionsOptions.Messages.Add(new ChatMessage(message.Role, message.Content));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://community.openai.com/t/cheat-sheet-mastering-temperature-and-top-p-in-chatgpt-api-a-few-tips-and-tricks-on-controlling-the-creativity-deterministic-output-of-prompt-responses/172683
|
|
||||||
chatCompletionsOptions.Temperature = 0.5f;
|
|
||||||
chatCompletionsOptions.NucleusSamplingFactor = 0.5f;
|
|
||||||
|
|
||||||
var convSetting = _services.GetRequiredService<ConversationSetting>();
|
|
||||||
if (convSetting.ShowVerboseLog)
|
|
||||||
{
|
|
||||||
var verbose = string.Join("\n", chatCompletionsOptions.Messages.Select(x =>
|
|
||||||
{
|
|
||||||
return x.Role == ChatRole.Function ?
|
|
||||||
$"{x.Role}: {x.Name} {x.Content}" :
|
|
||||||
$"{x.Role}: {x.Content}";
|
|
||||||
}));
|
|
||||||
_logger.LogInformation(verbose);
|
|
||||||
}
|
|
||||||
|
|
||||||
return chatCompletionsOptions;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ namespace BotSharp.Plugin.AzureOpenAI.Settings;
|
||||||
|
|
||||||
public class DeploymentModelSetting
|
public class DeploymentModelSetting
|
||||||
{
|
{
|
||||||
public string? ChatCompletionModel { get; set; }
|
public string ChatCompletionModel { get; set; } = string.Empty;
|
||||||
public string? TextCompletionModel { get; set; }
|
public string? TextCompletionModel { get; set; }
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
|
|
|
||||||
|
|
@ -44,9 +44,17 @@ public class ChatbotUiController : ControllerBase, IApiAdapter
|
||||||
{
|
{
|
||||||
Id = "gpt-3.5-turbo",
|
Id = "gpt-3.5-turbo",
|
||||||
Model = "gpt-3.5-turbo",
|
Model = "gpt-3.5-turbo",
|
||||||
Name = "Default (GPT-3.5)",
|
Name = "GPT-3.5 Turbo",
|
||||||
MaxLength = 4000,
|
MaxLength = 4 * 1024,
|
||||||
TokenLimit = 4000
|
TokenLimit = 4 * 1024
|
||||||
|
},
|
||||||
|
new AiModel
|
||||||
|
{
|
||||||
|
Id = "gpt-4",
|
||||||
|
Model = "gpt-4",
|
||||||
|
Name = "GPT-4",
|
||||||
|
MaxLength = 8 * 1024,
|
||||||
|
TokenLimit = 8 * 1024
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -62,7 +70,7 @@ public class ChatbotUiController : ControllerBase, IApiAdapter
|
||||||
var outputStream = Response.Body;
|
var outputStream = Response.Body;
|
||||||
|
|
||||||
var channel = "webchat";
|
var channel = "webchat";
|
||||||
var conversation = input.Messages
|
var message = input.Messages
|
||||||
.Where(x => x.Role == AgentRole.User)
|
.Where(x => x.Role == AgentRole.User)
|
||||||
.Select(x => new RoleDialogModel(x.Role, x.Content)
|
.Select(x => new RoleDialogModel(x.Role, x.Content)
|
||||||
{
|
{
|
||||||
|
|
@ -74,7 +82,7 @@ public class ChatbotUiController : ControllerBase, IApiAdapter
|
||||||
input.States.ForEach(x => conv.States.SetState(x.Split('=')[0], x.Split('=')[1]));
|
input.States.ForEach(x => conv.States.SetState(x.Split('=')[0], x.Split('=')[1]));
|
||||||
|
|
||||||
var result = await conv.SendMessage(input.AgentId,
|
var result = await conv.SendMessage(input.AgentId,
|
||||||
conversation,
|
message,
|
||||||
async msg =>
|
async msg =>
|
||||||
await OnChunkReceived(outputStream, msg),
|
await OnChunkReceived(outputStream, msg),
|
||||||
async fn
|
async fn
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,8 @@ public class ChatCompletionProvider : IChatCompletion
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetChatCompletions(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived)
|
public string ModelName => "llama-2";
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> GetChatCompletionsAsync(Agent agent,
|
public async Task<bool> GetChatCompletionsAsync(Agent agent,
|
||||||
List<RoleDialogModel> conversations,
|
List<RoleDialogModel> conversations,
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,7 @@
|
||||||
|
|
||||||
"Conversation": {
|
"Conversation": {
|
||||||
"DataDir": "conversations",
|
"DataDir": "conversations",
|
||||||
"ShowVerboseLog": false,
|
"ShowVerboseLog": false
|
||||||
"ChatCompletion": "AzureOpenAI.Providers.ChatCompletionProvider"
|
|
||||||
// "ChatCompletion": "LLamaSharp.ChatCompletionProvider"
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"LlamaSharp": {
|
"LlamaSharp": {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue