diff --git a/BotSharp.sln b/BotSharp.sln
index 9725dec4..18e39027 100644
--- a/BotSharp.sln
+++ b/BotSharp.sln
@@ -89,9 +89,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.MetaGLM", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.AnthropicAI", "src\Plugins\BotSharp.Plugin.AnthropicAI\BotSharp.Plugin.AnthropicAI.csproj", "{806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.ServiceDefaults", "src\BotSharp.ServiceDefaults\BotSharp.ServiceDefaults.csproj", "{6406DC61-0F30-42E8-A1DB-B38CDF454273}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.ServiceDefaults", "src\BotSharp.ServiceDefaults\BotSharp.ServiceDefaults.csproj", "{6406DC61-0F30-42E8-A1DB-B38CDF454273}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.AppHost", "src\BotSharp.AppHost\BotSharp.AppHost.csproj", "{E04FBBEF-744E-4EF3-B634-42AD9F8B68B1}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.AppHost", "src\BotSharp.AppHost\BotSharp.AppHost.csproj", "{E04FBBEF-744E-4EF3-B634-42AD9F8B68B1}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.OpenAI", "src\Plugins\BotSharp.Plugin.OpenAI\BotSharp.Plugin.OpenAI.csproj", "{6507D336-3A4D-41D4-81C0-2B900173A5FE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -373,6 +375,14 @@ Global
{E04FBBEF-744E-4EF3-B634-42AD9F8B68B1}.Release|Any CPU.Build.0 = Release|Any CPU
{E04FBBEF-744E-4EF3-B634-42AD9F8B68B1}.Release|x64.ActiveCfg = Release|Any CPU
{E04FBBEF-744E-4EF3-B634-42AD9F8B68B1}.Release|x64.Build.0 = Release|Any CPU
+ {6507D336-3A4D-41D4-81C0-2B900173A5FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6507D336-3A4D-41D4-81C0-2B900173A5FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6507D336-3A4D-41D4-81C0-2B900173A5FE}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {6507D336-3A4D-41D4-81C0-2B900173A5FE}.Debug|x64.Build.0 = Debug|Any CPU
+ {6507D336-3A4D-41D4-81C0-2B900173A5FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6507D336-3A4D-41D4-81C0-2B900173A5FE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6507D336-3A4D-41D4-81C0-2B900173A5FE}.Release|x64.ActiveCfg = Release|Any CPU
+ {6507D336-3A4D-41D4-81C0-2B900173A5FE}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -417,6 +427,7 @@ Global
{289E25C8-63F1-4D52-9909-207724DB40CB} = {D5293208-2BEF-42FC-A64C-5954F61720BA}
{CCF745F2-0C95-4ED0-983B-507C528B39EA} = {D5293208-2BEF-42FC-A64C-5954F61720BA}
{806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C} = {D5293208-2BEF-42FC-A64C-5954F61720BA}
+ {6507D336-3A4D-41D4-81C0-2B900173A5FE} = {D5293208-2BEF-42FC-A64C-5954F61720BA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/BuiltInAgentId.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/BuiltInAgentId.cs
index 98aaf454..44493f9a 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/BuiltInAgentId.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/BuiltInAgentId.cs
@@ -5,4 +5,5 @@ public class BuiltInAgentId
public const string AIAssistant = "01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a";
public const string Chatbot = "01e2fc5c-2c89-4ec7-8470-7688608b496c";
public const string HumanSupport = "01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b";
+ public const string UtilityAssistant = "6745151e-6d46-4a02-8de4-1c4f21c7da95";
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Plugins/IBotSharpPlugin.cs b/src/Infrastructure/BotSharp.Abstraction/Plugins/IBotSharpPlugin.cs
index 06dc4992..2ff3d32c 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Plugins/IBotSharpPlugin.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Plugins/IBotSharpPlugin.cs
@@ -21,7 +21,7 @@ public interface IBotSharpPlugin
///
/// Has build-in agent profile with this plugin
///
- string[] AgentIds => new string[0];
+ string[] AgentIds => [];
void RegisterDI(IServiceCollection services, IConfiguration config);
diff --git a/src/Infrastructure/BotSharp.Abstraction/Utilities/EnumHelper.cs b/src/Infrastructure/BotSharp.Abstraction/Utilities/EnumHelper.cs
new file mode 100644
index 00000000..1ac23ed7
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Utilities/EnumHelper.cs
@@ -0,0 +1,18 @@
+using System.ComponentModel;
+using System.Reflection;
+
+namespace BotSharp.Abstraction.Utilities;
+
+public static class EnumHelper
+{
+ public static string GetDescription(Enum value)
+ {
+ FieldInfo fi = value.GetType().GetField(value.ToString());
+ DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
+
+ if (attributes != null && attributes.Length > 0)
+ return attributes[0].Description;
+ else
+ return value.ToString();
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs
index defe7647..c385153a 100644
--- a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs
@@ -4,6 +4,6 @@ public interface IVectorDb
{
Task> GetCollections();
Task CreateCollection(string collectionName, int dim);
- Task Upsert(string collectionName, int id, float[] vector, string text);
+ Task Upsert(string collectionName, string id, float[] vector, string text, Dictionary? payload = null);
Task> Search(string collectionName, float[] vector, int limit = 5);
}
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs
index e3f1774e..89c45510 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs
@@ -1,5 +1,3 @@
-using BotSharp.Abstraction.Templating;
-
namespace BotSharp.Core.Agents.Services;
public partial class AgentService
@@ -7,7 +5,7 @@ public partial class AgentService
[MemoryCache(10 * 60, perInstanceCache: true)]
public async Task LoadAgent(string id)
{
- if (string.IsNullOrEmpty(id))
+ if (string.IsNullOrEmpty(id) || id == Guid.Empty.ToString())
{
return null;
}
diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj
index 8f44be5c..da864c60 100644
--- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj
+++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj
@@ -46,12 +46,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
@@ -153,22 +153,22 @@
PreserveNewest
-
+
PreserveNewest
-
+
PreserveNewest
-
+
PreserveNewest
-
+
PreserveNewest
-
+
PreserveNewest
-
+
PreserveNewest
diff --git a/src/Infrastructure/BotSharp.Core/Files/Functions/GenerateImageFn.cs b/src/Infrastructure/BotSharp.Core/Files/Functions/GenerateImageFn.cs
index 18c34c12..c1e816e3 100644
--- a/src/Infrastructure/BotSharp.Core/Files/Functions/GenerateImageFn.cs
+++ b/src/Infrastructure/BotSharp.Core/Files/Functions/GenerateImageFn.cs
@@ -9,7 +9,6 @@ public class GenerateImageFn : IFunctionCallback
private readonly IServiceProvider _services;
private readonly ILogger _logger;
- private static string UTILITY_ASSISTANT = Guid.Empty.ToString();
private string _conversationId;
private string _messageId;
@@ -29,7 +28,7 @@ public class GenerateImageFn : IFunctionCallback
SetImageOptions();
var agentService = _services.GetRequiredService();
- var agent = await agentService.LoadAgent(UTILITY_ASSISTANT);
+ var agent = await agentService.LoadAgent(BuiltInAgentId.UtilityAssistant);
var imageAgent = new Agent
{
Id = agent?.Id ?? Guid.Empty.ToString(),
diff --git a/src/Infrastructure/BotSharp.Core/Files/Functions/ReadFileFn.cs b/src/Infrastructure/BotSharp.Core/Files/Functions/ReadFileFn.cs
index a1bb50df..5aa75cd5 100644
--- a/src/Infrastructure/BotSharp.Core/Files/Functions/ReadFileFn.cs
+++ b/src/Infrastructure/BotSharp.Core/Files/Functions/ReadFileFn.cs
@@ -12,7 +12,6 @@ public class ReadFileFn : IFunctionCallback
private readonly ILogger _logger;
private readonly IEnumerable _imageTypes = new List { "image", "images", "png", "jpg", "jpeg" };
private readonly IEnumerable _pdfTypes = new List { "pdf" };
- private static string UTILITY_ASSISTANT = Guid.Empty.ToString();
public ReadFileFn(
IServiceProvider services,
@@ -31,7 +30,7 @@ public class ReadFileFn : IFunctionCallback
var wholeDialogs = conv.GetDialogHistory();
var fileTypes = args?.FileTypes?.Split(",", StringSplitOptions.RemoveEmptyEntries)?.ToList() ?? new List();
var dialogs = await AssembleFiles(conv.ConversationId, wholeDialogs, fileTypes);
- var agent = await agentService.LoadAgent(UTILITY_ASSISTANT);
+ var agent = await agentService.LoadAgent(BuiltInAgentId.UtilityAssistant);
var fileAgent = new Agent
{
Id = agent?.Id ?? Guid.Empty.ToString(),
diff --git a/src/Infrastructure/BotSharp.Core/Files/Hooks/FileReaderHook.cs b/src/Infrastructure/BotSharp.Core/Files/Hooks/FileReaderHook.cs
index b9f57846..685148bb 100644
--- a/src/Infrastructure/BotSharp.Core/Files/Hooks/FileReaderHook.cs
+++ b/src/Infrastructure/BotSharp.Core/Files/Hooks/FileReaderHook.cs
@@ -2,7 +2,6 @@ namespace BotSharp.Core.Files.Hooks;
public class FileReaderHook : AgentHookBase
{
- private static string UTILITY_ASSISTANT = Guid.Empty.ToString();
private static string FUNCTION_NAME = "read_file";
public override string SelfId => string.Empty;
@@ -45,7 +44,7 @@ public class FileReaderHook : AgentHookBase
private (string, FunctionDef?) GetPromptAndFunction()
{
var db = _services.GetRequiredService();
- var agent = db.GetAgent(UTILITY_ASSISTANT);
+ var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant);
var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{FUNCTION_NAME}.fn"))?.Content ?? string.Empty;
var loadAttachmentFn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(FUNCTION_NAME));
return (prompt, loadAttachmentFn);
diff --git a/src/Infrastructure/BotSharp.Core/Files/Hooks/ImageGeneratorHook.cs b/src/Infrastructure/BotSharp.Core/Files/Hooks/ImageGeneratorHook.cs
index f3702b3e..dadba673 100644
--- a/src/Infrastructure/BotSharp.Core/Files/Hooks/ImageGeneratorHook.cs
+++ b/src/Infrastructure/BotSharp.Core/Files/Hooks/ImageGeneratorHook.cs
@@ -2,7 +2,6 @@ namespace BotSharp.Core.Files.Hooks;
public class ImageGeneratorHook : AgentHookBase
{
- private static string UTILITY_ASSISTANT = Guid.Empty.ToString();
private static string FUNCTION_NAME = "generate_image";
public override string SelfId => string.Empty;
@@ -45,7 +44,7 @@ public class ImageGeneratorHook : AgentHookBase
private (string, FunctionDef?) GetPromptAndFunction()
{
var db = _services.GetRequiredService();
- var agent = db.GetAgent(UTILITY_ASSISTANT);
+ var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant);
var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{FUNCTION_NAME}.fn"))?.Content ?? string.Empty;
var loadAttachmentFn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(FUNCTION_NAME));
return (prompt, loadAttachmentFn);
diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs
index 9f768696..54fda726 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs
@@ -57,7 +57,10 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
}
message.FunctionArgs = JsonSerializer.Serialize(inst);
- var ret = await routing.InvokeFunction(message.FunctionName, message);
+ if (message.FunctionName != null)
+ {
+ var ret = await routing.InvokeFunction(message.FunctionName, message);
+ }
var agentId = routing.Context.GetCurrentAgentId();
@@ -82,7 +85,7 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
}
else
{
- ret = await routing.InvokeAgent(agentId, _dialogs, onFunctionExecuting);
+ var ret = await routing.InvokeAgent(agentId, _dialogs, onFunctionExecuting);
}
var response = _dialogs.Last();
diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs
index 40372ed4..cc6e9846 100644
--- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs
+++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs
@@ -88,6 +88,7 @@ public class UserService : IUserService
var base64 = Encoding.UTF8.GetString(Convert.FromBase64String(authorization));
var (id, password) = base64.SplitAsTuple(":");
+ var hooks = _services.GetServices();
var db = _services.GetRequiredService();
var record = id.Contains("@") ? db.GetUserByEmail(id) : db.GetUserByUserName(id);
if (record == null)
@@ -96,7 +97,7 @@ public class UserService : IUserService
}
//verify password is correct or not.
- if (record != null)
+ if (record != null && !hooks.Any())
{
var hashPassword = Utilities.HashTextMd5($"{password}{record.Salt}");
if (hashPassword != record.Password)
@@ -107,7 +108,6 @@ public class UserService : IUserService
User? user = record;
var isAuthenticatedByHook = false;
- var hooks = _services.GetServices();
if (record == null || record.Source != "internal")
{
// check 3rd party user
@@ -146,7 +146,7 @@ public class UserService : IUserService
}
}
- if ((hooks != null && hooks.Any() && user == null) || record == null)
+ if ((hooks.Any() && user == null) || record == null)
{
return default;
}
diff --git a/src/Infrastructure/BotSharp.Core/data/agents/00000000-0000-0000-0000-000000000000/agent.json b/src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/agent.json
similarity index 90%
rename from src/Infrastructure/BotSharp.Core/data/agents/00000000-0000-0000-0000-000000000000/agent.json
rename to src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/agent.json
index c44219c1..ffbc26e4 100644
--- a/src/Infrastructure/BotSharp.Core/data/agents/00000000-0000-0000-0000-000000000000/agent.json
+++ b/src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/agent.json
@@ -1,5 +1,5 @@
{
- "id": "00000000-0000-0000-0000-000000000000",
+ "id": "6745151e-6d46-4a02-8de4-1c4f21c7da95",
"name": "Utility Assistant",
"description": "Utility assistant that can be used to complete many different tasks",
"type": "static",
diff --git a/src/Infrastructure/BotSharp.Core/data/agents/00000000-0000-0000-0000-000000000000/functions/generate_image.json b/src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/generate_image.json
similarity index 100%
rename from src/Infrastructure/BotSharp.Core/data/agents/00000000-0000-0000-0000-000000000000/functions/generate_image.json
rename to src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/generate_image.json
diff --git a/src/Infrastructure/BotSharp.Core/data/agents/00000000-0000-0000-0000-000000000000/functions/read_file.json b/src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/read_file.json
similarity index 100%
rename from src/Infrastructure/BotSharp.Core/data/agents/00000000-0000-0000-0000-000000000000/functions/read_file.json
rename to src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/read_file.json
diff --git a/src/Infrastructure/BotSharp.Core/data/agents/00000000-0000-0000-0000-000000000000/instruction.liquid b/src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/instruction.liquid
similarity index 100%
rename from src/Infrastructure/BotSharp.Core/data/agents/00000000-0000-0000-0000-000000000000/instruction.liquid
rename to src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/instruction.liquid
diff --git a/src/Infrastructure/BotSharp.Core/data/agents/00000000-0000-0000-0000-000000000000/templates/generate_image.fn.liquid b/src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/generate_image.fn.liquid
similarity index 100%
rename from src/Infrastructure/BotSharp.Core/data/agents/00000000-0000-0000-0000-000000000000/templates/generate_image.fn.liquid
rename to src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/generate_image.fn.liquid
diff --git a/src/Infrastructure/BotSharp.Core/data/agents/00000000-0000-0000-0000-000000000000/templates/read_file.fn.liquid b/src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/read_file.fn.liquid
similarity index 100%
rename from src/Infrastructure/BotSharp.Core/data/agents/00000000-0000-0000-0000-000000000000/templates/read_file.fn.liquid
rename to src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/read_file.fn.liquid
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs
index bba6441a..465f3d7b 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs
@@ -116,7 +116,7 @@ public class InstructModeController : ControllerBase
try
{
var completion = CompletionProvider.GetImageGeneration(_services, provider: input.Provider ?? "openai",
- modelId: input.ModelId ?? "dall-e", imageGenerate: true);
+ model: input.Model ?? "dall-e-3", imageGenerate: true);
var message = await completion.GetImageGeneration(new Agent()
{
Id = Guid.Empty.ToString(),
diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/AzureOpenAiPlugin.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/AzureOpenAiPlugin.cs
index 852db899..d52cba7a 100644
--- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/AzureOpenAiPlugin.cs
+++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/AzureOpenAiPlugin.cs
@@ -14,8 +14,8 @@ namespace BotSharp.Platform.AzureAi;
public class AzureOpenAiPlugin : IBotSharpPlugin
{
public string Id => "65185362-392c-44fd-a023-95a198824436";
- public string Name => "OpenAI/ Azure OpenAI";
- public string Description => "OpenAI/ Azure OpenAI Service including text generation, text to image and other AI services.";
+ public string Name => "Azure OpenAI";
+ public string Description => "Azure OpenAI Service including text generation, text to image and other AI services.";
public string IconUrl => "https://nanfor.com/cdn/shop/files/cursos-propios-Azure-openAI.jpg?v=1692877741";
public void RegisterDI(IServiceCollection services, IConfiguration config)
@@ -27,12 +27,8 @@ public class AzureOpenAiPlugin : IBotSharpPlugin
});
services.AddScoped();
- services.AddScoped();
services.AddScoped();
- services.AddScoped();
services.AddScoped();
- services.AddScoped();
services.AddScoped();
- services.AddScoped();
}
}
\ No newline at end of file
diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Models/OpenAiTextCompletionRequest.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Models/OpenAiTextCompletionRequest.cs
deleted file mode 100644
index 083c8c95..00000000
--- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Models/OpenAiTextCompletionRequest.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace BotSharp.Plugin.AzureOpenAI.Models;
-
-public class OpenAiTextCompletionRequest : TextCompletionRequest
-{
- [JsonPropertyName("model")]
- public string Model { get; set; } = string.Empty;
-}
diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Models/TextCompletionRequest.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Models/TextCompletionRequest.cs
index b6c64e48..dd7da746 100644
--- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Models/TextCompletionRequest.cs
+++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Models/TextCompletionRequest.cs
@@ -4,7 +4,6 @@ namespace BotSharp.Plugin.AzureOpenAI.Models;
public class TextCompletionRequest
{
-
[JsonPropertyName("prompt")]
public string Prompt { get; set; } = string.Empty;
diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs
index 9c52e97c..de257ded 100644
--- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs
@@ -12,7 +12,8 @@ public class ChatCompletionProvider : IChatCompletion
public virtual string Provider => "azure-openai";
- public ChatCompletionProvider(AzureOpenAiSettings settings,
+ public ChatCompletionProvider(
+ AzureOpenAiSettings settings,
ILogger logger,
IServiceProvider services)
{
diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/OpenAiChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/OpenAiChatCompletionProvider.cs
deleted file mode 100644
index d73f6384..00000000
--- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/OpenAiChatCompletionProvider.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace BotSharp.Plugin.AzureOpenAI.Providers.Chat;
-
-public class OpenAiChatCompletionProvider : ChatCompletionProvider
-{
- public override string Provider => "openai";
-
- public OpenAiChatCompletionProvider(AzureOpenAiSettings settings,
- ILogger logger,
- IServiceProvider services) : base(settings, logger, services)
- {
- }
-}
diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Embedding/OpenAiTextEmbeddingProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Embedding/OpenAiTextEmbeddingProvider.cs
deleted file mode 100644
index 0bdbd2ca..00000000
--- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Embedding/OpenAiTextEmbeddingProvider.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace BotSharp.Plugin.AzureOpenAI.Providers.Embedding;
-
-public class OpenAiTextEmbeddingProvider : TextEmbeddingProvider
-{
- public override string Provider => "openai";
-
- public OpenAiTextEmbeddingProvider(AzureOpenAiSettings settings,
- ILogger logger,
- IServiceProvider services) : base(settings, logger, services) { }
-}
diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Image/ImageGenerationProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Image/ImageGenerationProvider.cs
index c80781a1..2df6a4a7 100644
--- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Image/ImageGenerationProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Image/ImageGenerationProvider.cs
@@ -1,4 +1,3 @@
-using BotSharp.Abstraction.Files.Models;
using OpenAI.Images;
namespace BotSharp.Plugin.AzureOpenAI.Providers.Image;
@@ -10,7 +9,7 @@ public class ImageGenerationProvider : IImageGeneration
protected readonly ILogger _logger;
private const int DEFAULT_IMAGE_COUNT = 1;
- private const int IMAGE_COUNT_LIMIT = 3;
+ private const int IMAGE_COUNT_LIMIT = 5;
protected string _model;
@@ -44,7 +43,6 @@ public class ImageGenerationProvider : IImageGeneration
var response = imageClient.GenerateImages(prompt, imageCount, options);
var values = response.Value;
- var content = string.Empty;
var images = new List();
foreach (var value in values)
{
@@ -67,9 +65,9 @@ public class ImageGenerationProvider : IImageGeneration
}
images.Add(image);
- content += $"{image.Description}\r\n";
}
+ var content = string.Join("\r\n", images.Select(x => x.Description));
var responseMessage = new RoleDialogModel(AgentRole.Assistant, content)
{
CurrentAgentId = agent.Id,
diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Image/OpenAiImageGenerationProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Image/OpenAiImageGenerationProvider.cs
deleted file mode 100644
index d2093073..00000000
--- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Image/OpenAiImageGenerationProvider.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace BotSharp.Plugin.AzureOpenAI.Providers.Image;
-
-public class OpenAiImageGenerationProvider : ImageGenerationProvider
-{
- public override string Provider => "openai";
-
- public OpenAiImageGenerationProvider(AzureOpenAiSettings settings,
- ILogger logger,
- IServiceProvider services) : base(settings, logger, services)
- {
- }
-}
diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ProviderHelper.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ProviderHelper.cs
index 606372a0..7dbd4588 100644
--- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ProviderHelper.cs
+++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ProviderHelper.cs
@@ -1,19 +1,15 @@
using Azure.AI.OpenAI;
using Azure;
-using OpenAI;
-using System.ClientModel;
namespace BotSharp.Plugin.AzureOpenAI.Providers;
public class ProviderHelper
{
- public static OpenAIClient GetClient(string provider, string model, IServiceProvider services)
+ public static AzureOpenAIClient GetClient(string provider, string model, IServiceProvider services)
{
var settingsService = services.GetRequiredService();
var settings = settingsService.GetSetting(provider, model);
- var client = provider == "openai" ?
- new OpenAIClient(new ApiKeyCredential(settings.ApiKey)) :
- new AzureOpenAIClient(new Uri(settings.Endpoint), new AzureKeyCredential(settings.ApiKey));
+ var client = new AzureOpenAIClient(new Uri(settings.Endpoint), new AzureKeyCredential(settings.ApiKey));
return client;
}
diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Text/OpenAiTextCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Text/OpenAiTextCompletionProvider.cs
deleted file mode 100644
index 7c9f0200..00000000
--- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Text/OpenAiTextCompletionProvider.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using BotSharp.Abstraction.MLTasks.Settings;
-using BotSharp.Plugin.AzureOpenAI.Models;
-using System.Net.Http;
-using System.Net.Mime;
-using System.Text;
-using System.Text.Json;
-
-namespace BotSharp.Plugin.AzureOpenAI.Providers.Text;
-
-public class OpenAiTextCompletionProvider : TextCompletionProvider
-{
- private readonly IServiceProvider _services;
- private readonly ILogger _logger;
-
- public override string Provider => "openai";
-
- public OpenAiTextCompletionProvider(AzureOpenAiSettings settings,
- ILogger logger,
- IServiceProvider services) : base(settings, logger, services)
- {
- _services = services;
- _logger = logger;
- }
-
- protected override async Task GetTextCompletion(string apiUrl, string apiKey, string prompt, float temperature, int maxTokens = 256)
- {
- try
- {
- var http = _services.GetRequiredService();
- using var httpClient = http.CreateClient();
- AddHeader(httpClient, apiKey);
-
- var request = new OpenAiTextCompletionRequest
- {
- Model = _model,
- Prompt = prompt,
- MaxTokens = maxTokens,
- Temperature = temperature
- };
- var data = JsonSerializer.Serialize(request, _jsonOptions);
- var httpRequest = new HttpRequestMessage
- {
- Method = HttpMethod.Post,
- RequestUri = new Uri(apiUrl),
- Content = new StringContent(data, Encoding.UTF8, MediaTypeNames.Application.Json)
- };
- var rawResponse = await httpClient.SendAsync(httpRequest);
- rawResponse.EnsureSuccessStatusCode();
-
- var responseStr = await rawResponse.Content.ReadAsStringAsync();
- var response = JsonSerializer.Deserialize(responseStr, _jsonOptions);
- return response;
- }
- catch (Exception ex)
- {
- _logger.LogError($"Error when {Provider}-{_model} generating text... {ex.Message}");
- throw;
- }
- }
-
- protected override string BuildApiUrl(LlmModelSetting modelSetting)
- {
- var endpoint = modelSetting.Endpoint.EndsWith("/") ?
- modelSetting.Endpoint.Substring(0, modelSetting.Endpoint.Length - 1) : modelSetting.Endpoint;
- return endpoint ?? string.Empty;
- }
-
- protected override void AddHeader(HttpClient httpClient, string apiKey)
- {
- httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}");
- }
-}
diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Text/TextCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Text/TextCompletionProvider.cs
index fa6c2418..9b41b948 100644
--- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Text/TextCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Text/TextCompletionProvider.cs
@@ -1,5 +1,4 @@
using BotSharp.Abstraction.MLTasks.Settings;
-using BotSharp.Plugin.AzureOpenAI.Models;
using System.Net.Http;
using System.Net.Mime;
using System.Text;
@@ -96,7 +95,7 @@ public class TextCompletionProvider : ITextCompletion
_model = model;
}
- protected virtual async Task GetTextCompletion(string apiUrl, string apiKey, string prompt, float temperature, int maxTokens = 256)
+ private async Task GetTextCompletion(string apiUrl, string apiKey, string prompt, float temperature, int maxTokens = 256)
{
try
{
@@ -117,10 +116,10 @@ public class TextCompletionProvider : ITextCompletion
RequestUri = new Uri(apiUrl),
Content = new StringContent(data, Encoding.UTF8, MediaTypeNames.Application.Json)
};
- var rawResponse = await httpClient.SendAsync(httpRequest);
- rawResponse.EnsureSuccessStatusCode();
- var responseStr = await rawResponse.Content.ReadAsStringAsync();
+ var httpResponse = await httpClient.SendAsync(httpRequest);
+ httpResponse.EnsureSuccessStatusCode();
+ var responseStr = await httpResponse.Content.ReadAsStringAsync();
var response = JsonSerializer.Deserialize(responseStr, _jsonOptions);
return response;
}
@@ -131,7 +130,7 @@ public class TextCompletionProvider : ITextCompletion
}
}
- protected virtual string BuildApiUrl(LlmModelSetting modelSetting)
+ private string BuildApiUrl(LlmModelSetting modelSetting)
{
var url = string.Empty;
var endpoint = modelSetting.Endpoint.EndsWith("/") ?
@@ -141,7 +140,7 @@ public class TextCompletionProvider : ITextCompletion
return url;
}
- protected virtual void AddHeader(HttpClient httpClient, string apiKey)
+ private void AddHeader(HttpClient httpClient, string apiKey)
{
httpClient.DefaultRequestHeaders.Add("api-key", $"{apiKey}");
}
diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Using.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Using.cs
index 80a43499..71f5272d 100644
--- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Using.cs
+++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Using.cs
@@ -14,4 +14,6 @@ global using BotSharp.Abstraction.MLTasks;
global using BotSharp.Abstraction.Agents;
global using BotSharp.Abstraction.Files;
global using BotSharp.Abstraction.Utilities;
+global using BotSharp.Abstraction.Files.Models;
+global using BotSharp.Plugin.AzureOpenAI.Models;
global using BotSharp.Plugin.AzureOpenAI.Settings;
\ No newline at end of file
diff --git a/src/Plugins/BotSharp.Plugin.HttpHandler/BotSharp.Plugin.HttpHandler.csproj b/src/Plugins/BotSharp.Plugin.HttpHandler/BotSharp.Plugin.HttpHandler.csproj
index 80293b4f..2911dc2b 100644
--- a/src/Plugins/BotSharp.Plugin.HttpHandler/BotSharp.Plugin.HttpHandler.csproj
+++ b/src/Plugins/BotSharp.Plugin.HttpHandler/BotSharp.Plugin.HttpHandler.csproj
@@ -11,8 +11,8 @@
-
-
+
+
@@ -25,10 +25,10 @@
PreserveNewest
-
+
PreserveNewest
-
+
PreserveNewest
diff --git a/src/Plugins/BotSharp.Plugin.HttpHandler/Hooks/HttpHandlerHook.cs b/src/Plugins/BotSharp.Plugin.HttpHandler/Hooks/HttpHandlerHook.cs
index 891ebc19..a5106380 100644
--- a/src/Plugins/BotSharp.Plugin.HttpHandler/Hooks/HttpHandlerHook.cs
+++ b/src/Plugins/BotSharp.Plugin.HttpHandler/Hooks/HttpHandlerHook.cs
@@ -1,4 +1,5 @@
using BotSharp.Abstraction.Agents;
+using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Agents.Settings;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Repositories;
@@ -8,7 +9,6 @@ namespace BotSharp.Plugin.HttpHandler.Hooks;
public class HttpHandlerHook : AgentHookBase
{
- private static string UTILITY_ASSISTANT = Guid.Empty.ToString();
private static string FUNCTION_NAME = "handle_http_request";
public override string SelfId => string.Empty;
@@ -51,7 +51,7 @@ public class HttpHandlerHook : AgentHookBase
private (string, FunctionDef?) GetPromptAndFunction()
{
var db = _services.GetRequiredService();
- var agent = db.GetAgent(UTILITY_ASSISTANT);
+ var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant);
var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{FUNCTION_NAME}.fn"))?.Content ?? string.Empty;
var loadAttachmentFn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(FUNCTION_NAME));
return (prompt, loadAttachmentFn);
diff --git a/src/Plugins/BotSharp.Plugin.HttpHandler/data/agents/00000000-0000-0000-0000-000000000000/functions/handle_http_request.json b/src/Plugins/BotSharp.Plugin.HttpHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/handle_http_request.json
similarity index 100%
rename from src/Plugins/BotSharp.Plugin.HttpHandler/data/agents/00000000-0000-0000-0000-000000000000/functions/handle_http_request.json
rename to src/Plugins/BotSharp.Plugin.HttpHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/handle_http_request.json
diff --git a/src/Plugins/BotSharp.Plugin.HttpHandler/data/agents/00000000-0000-0000-0000-000000000000/templates/handle_http_request.fn.liquid b/src/Plugins/BotSharp.Plugin.HttpHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/handle_http_request.fn.liquid
similarity index 100%
rename from src/Plugins/BotSharp.Plugin.HttpHandler/data/agents/00000000-0000-0000-0000-000000000000/templates/handle_http_request.fn.liquid
rename to src/Plugins/BotSharp.Plugin.HttpHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/handle_http_request.fn.liquid
diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/MemVecDb/MemVectorDatabase.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/MemVecDb/MemVectorDatabase.cs
index cbe3c257..a595c5da 100644
--- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/MemVecDb/MemVectorDatabase.cs
+++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/MemVecDb/MemVectorDatabase.cs
@@ -37,7 +37,7 @@ public class MemVectorDatabase : IVectorDb
return texts;
}
- public async Task Upsert(string collectionName, int id, float[] vector, string text)
+ public async Task Upsert(string collectionName, string id, float[] vector, string text, Dictionary? payload = null)
{
_vectors[collectionName].Add(new VecRecord
{
diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/MemVecDb/VecRecord.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/MemVecDb/VecRecord.cs
index 79775698..3c4d024b 100644
--- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/MemVecDb/VecRecord.cs
+++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/MemVecDb/VecRecord.cs
@@ -2,7 +2,7 @@ namespace BotSharp.Plugin.KnowledgeBase.MemVecDb;
public class VecRecord
{
- public int Id { get; set; }
+ public string Id { get; set; }
public float[] Vector { get; set; }
public string Text { get; set; }
diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.cs
index 5f545078..bd4cf6f4 100644
--- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.cs
+++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.cs
@@ -32,7 +32,7 @@ public partial class KnowledgeService : IKnowledgeService
foreach (var line in lines)
{
var vec = await textEmbedding.GetVectorAsync(line);
- await db.Upsert("shared", idStart, vec, line);
+ await db.Upsert("shared", idStart.ToString(), vec, line);
idStart++;
Console.WriteLine($"Saved vector {idStart}/{lines.Count}: {line}\n");
}
@@ -55,7 +55,7 @@ public partial class KnowledgeService : IKnowledgeService
foreach (var line in lines)
{
var vec = await textEmbedding.GetVectorAsync(line);
- await db.Upsert(knowledge.AgentId, idStart, vec, line);
+ await db.Upsert(knowledge.AgentId, idStart.ToString(), vec, line);
idStart++;
Console.WriteLine($"Saved vector {idStart}/{lines.Count}: {line}\n");
}
diff --git a/src/Plugins/BotSharp.Plugin.MetaAI/Providers/FaissDb.cs b/src/Plugins/BotSharp.Plugin.MetaAI/Providers/FaissDb.cs
index 3fac532d..c74c86bb 100644
--- a/src/Plugins/BotSharp.Plugin.MetaAI/Providers/FaissDb.cs
+++ b/src/Plugins/BotSharp.Plugin.MetaAI/Providers/FaissDb.cs
@@ -22,7 +22,7 @@ public class FaissDb : IVectorDb
throw new NotImplementedException();
}
- public Task Upsert(string collectionName, int id, float[] vector, string text)
+ public Task Upsert(string collectionName, string id, float[] vector, string text, Dictionary? payload = null)
{
throw new NotImplementedException();
}
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/BotSharp.Plugin.OpenAI.csproj b/src/Plugins/BotSharp.Plugin.OpenAI/BotSharp.Plugin.OpenAI.csproj
new file mode 100644
index 00000000..70ba3a15
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/BotSharp.Plugin.OpenAI.csproj
@@ -0,0 +1,22 @@
+
+
+
+ $(TargetFramework)
+ enable
+ $(LangVersion)
+ $(BotSharpVersion)
+ $(GeneratePackageOnBuild)
+ $(GenerateDocumentationFile)
+ $(SolutionDir)packages
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Models/TextCompletionRequest.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Models/TextCompletionRequest.cs
new file mode 100644
index 00000000..7ebaadef
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Models/TextCompletionRequest.cs
@@ -0,0 +1,18 @@
+using System.Text.Json.Serialization;
+
+namespace BotSharp.Plugin.OpenAI.Models;
+
+public class TextCompletionRequest
+{
+ [JsonPropertyName("model")]
+ public string Model { get; set; } = string.Empty;
+
+ [JsonPropertyName("prompt")]
+ public string Prompt { get; set; } = string.Empty;
+
+ [JsonPropertyName("max_tokens")]
+ public int MaxTokens { get; set; } = 256;
+
+ [JsonPropertyName("temperature")]
+ public float Temperature { get; set; } = 0;
+}
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Models/TextCompletionResponse.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Models/TextCompletionResponse.cs
new file mode 100644
index 00000000..b3ecb24d
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Models/TextCompletionResponse.cs
@@ -0,0 +1,36 @@
+using System.Text.Json.Serialization;
+
+namespace BotSharp.Plugin.OpenAI.Models;
+
+public class TextCompletionResponse
+{
+ [JsonPropertyName("id")]
+ public string Id { get; set; }
+
+ [JsonPropertyName("choices")]
+ public IEnumerable Choices { get; set; } = new List();
+
+ [JsonPropertyName("usage")]
+ public TexCompletionUsage Usage { get; set; }
+}
+
+public class TexCompletionChoice
+{
+ [JsonPropertyName("text")]
+ public string Text { get; set; }
+
+ [JsonPropertyName("finish_reason")]
+ public string FinishReason { get; set; }
+}
+
+public class TexCompletionUsage
+{
+ [JsonPropertyName("prompt_tokens")]
+ public int PromptTokens { get; set; }
+
+ [JsonPropertyName("completion_tokens")]
+ public int CompletionTokens { get; set; }
+
+ [JsonPropertyName("total_tokens")]
+ public int TotalTokens { get; set; }
+}
\ No newline at end of file
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/OpenAiPlugin.cs b/src/Plugins/BotSharp.Plugin.OpenAI/OpenAiPlugin.cs
new file mode 100644
index 00000000..ce77e589
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/OpenAiPlugin.cs
@@ -0,0 +1,34 @@
+using BotSharp.Abstraction.Plugins;
+using BotSharp.Abstraction.Settings;
+using BotSharp.Plugin.OpenAI.Providers.Embedding;
+using BotSharp.Plugin.OpenAI.Providers.Image;
+using BotSharp.Plugin.OpenAI.Providers.Text;
+using BotSharp.Plugin.OpenAI.Providers.Chat;
+using Microsoft.Extensions.Configuration;
+
+namespace BotSharp.Plugin.OpenAI;
+
+///
+/// OpenAI Service
+///
+public class OpenAiPlugin : IBotSharpPlugin
+{
+ public string Id => "a743e90f-7cbc-4e47-b8a0-2f8e44f894c7";
+ public string Name => "OpenAI";
+ public string Description => "OpenAI Service including text generation, text to image and other AI services.";
+ public string IconUrl => "https://logosandtypes.com/wp-content/uploads/2022/07/openai.svg";
+
+ public void RegisterDI(IServiceCollection services, IConfiguration config)
+ {
+ services.AddScoped(provider =>
+ {
+ var settingService = provider.GetRequiredService();
+ return settingService.Bind("OpenAi");
+ });
+
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ }
+}
\ No newline at end of file
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs
new file mode 100644
index 00000000..4d9b577d
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs
@@ -0,0 +1,376 @@
+using OpenAI.Chat;
+
+namespace BotSharp.Plugin.OpenAI.Providers.Chat;
+
+public class ChatCompletionProvider : IChatCompletion
+{
+ protected readonly OpenAiSettings _settings;
+ protected readonly IServiceProvider _services;
+ protected readonly ILogger _logger;
+
+ protected string _model;
+
+ public virtual string Provider => "openai";
+
+ public ChatCompletionProvider(
+ OpenAiSettings settings,
+ ILogger logger,
+ IServiceProvider services)
+ {
+ _settings = settings;
+ _logger = logger;
+ _services = services;
+ }
+
+ public async Task GetChatCompletions(Agent agent, List conversations)
+ {
+ var contentHooks = _services.GetServices().ToList();
+
+ // Before chat completion hook
+ foreach (var hook in contentHooks)
+ {
+ await hook.BeforeGenerating(agent, conversations);
+ }
+
+ var client = ProviderHelper.GetClient(Provider, _model, _services);
+ var chatClient = client.GetChatClient(_model);
+ var (prompt, messages, options) = PrepareOptions(agent, conversations);
+
+ var response = chatClient.CompleteChat(messages, options);
+ var value = response.Value;
+ var reason = value.FinishReason;
+ var content = value.Content;
+ var text = content.FirstOrDefault()?.Text ?? string.Empty;
+
+ RoleDialogModel responseMessage;
+ if (reason == ChatFinishReason.FunctionCall)
+ {
+ responseMessage = new RoleDialogModel(AgentRole.Function, text)
+ {
+ CurrentAgentId = agent.Id,
+ MessageId = conversations.Last().MessageId,
+ FunctionName = value.FunctionCall.FunctionName,
+ FunctionArgs = value.FunctionCall.FunctionArguments
+ };
+
+ // Somethings LLM will generate a function name with agent name.
+ if (!string.IsNullOrEmpty(responseMessage.FunctionName))
+ {
+ responseMessage.FunctionName = responseMessage.FunctionName.Split('.').Last();
+ }
+ }
+ else if (reason == ChatFinishReason.ToolCalls)
+ {
+ var toolCall = value.ToolCalls.FirstOrDefault();
+ responseMessage = new RoleDialogModel(AgentRole.Function, text)
+ {
+ CurrentAgentId = agent.Id,
+ MessageId = conversations.Last().MessageId,
+ FunctionName = toolCall?.FunctionName,
+ FunctionArgs = toolCall?.FunctionArguments
+ };
+ }
+ else
+ {
+ responseMessage = new RoleDialogModel(AgentRole.Assistant, text)
+ {
+ CurrentAgentId = agent.Id,
+ MessageId = conversations.Last().MessageId
+ };
+ }
+
+ // After chat completion hook
+ foreach (var hook in contentHooks)
+ {
+ await hook.AfterGenerated(responseMessage, new TokenStatsModel
+ {
+ Prompt = prompt,
+ Provider = Provider,
+ Model = _model,
+ PromptCount = response.Value.Usage.InputTokens,
+ CompletionCount = response.Value.Usage.OutputTokens
+ });
+ }
+
+ return responseMessage;
+ }
+
+ public async Task GetChatCompletionsAsync(Agent agent,
+ List conversations,
+ Func onMessageReceived,
+ Func onFunctionExecuting)
+ {
+ var hooks = _services.GetServices().ToList();
+
+ // Before chat completion hook
+ foreach (var hook in hooks)
+ {
+ await hook.BeforeGenerating(agent, conversations);
+ }
+
+ var client = ProviderHelper.GetClient(Provider, _model, _services);
+ var chatClient = client.GetChatClient(_model);
+ var (prompt, messages, options) = PrepareOptions(agent, conversations);
+
+ var response = await chatClient.CompleteChatAsync(messages, options);
+ var value = response.Value;
+ var reason = value.FinishReason;
+ var content = value.Content;
+ var text = content.FirstOrDefault()?.Text ?? string.Empty;
+
+ var msg = new RoleDialogModel(AgentRole.Assistant, text)
+ {
+ CurrentAgentId = agent.Id
+ };
+
+ // After chat completion hook
+ foreach (var hook in hooks)
+ {
+ await hook.AfterGenerated(msg, new TokenStatsModel
+ {
+ Prompt = prompt,
+ Provider = Provider,
+ Model = _model,
+ PromptCount = response.Value.Usage.InputTokens,
+ CompletionCount = response.Value.Usage.OutputTokens
+ });
+ }
+
+ if (reason == ChatFinishReason.FunctionCall)
+ {
+ _logger.LogInformation($"[{agent.Name}]: {value.FunctionCall.FunctionName}({value.FunctionCall.FunctionArguments})");
+
+ var funcContextIn = new RoleDialogModel(AgentRole.Function, text)
+ {
+ CurrentAgentId = agent.Id,
+ FunctionName = value.FunctionCall?.FunctionName,
+ FunctionArgs = value.FunctionCall?.FunctionArguments
+ };
+
+ // Somethings LLM will generate a function name with agent name.
+ if (!string.IsNullOrEmpty(funcContextIn.FunctionName))
+ {
+ funcContextIn.FunctionName = funcContextIn.FunctionName.Split('.').Last();
+ }
+
+ // Execute functions
+ await onFunctionExecuting(funcContextIn);
+ }
+ else
+ {
+ // Text response received
+ await onMessageReceived(msg);
+ }
+
+ return true;
+ }
+
+ public async Task GetChatCompletionsStreamingAsync(Agent agent, List conversations, Func onMessageReceived)
+ {
+ var client = ProviderHelper.GetClient(Provider, _model, _services);
+ var chatClient = client.GetChatClient(_model);
+ var (prompt, messages, options) = PrepareOptions(agent, conversations);
+
+ var response = chatClient.CompleteChatStreamingAsync(messages, options);
+
+ await foreach (var choice in response)
+ {
+ if (choice.FinishReason == ChatFinishReason.FunctionCall)
+ {
+ Console.Write(choice.FunctionCallUpdate?.FunctionArgumentsUpdate);
+
+ await onMessageReceived(new RoleDialogModel(AgentRole.Assistant, choice.FunctionCallUpdate?.FunctionArgumentsUpdate));
+ continue;
+ }
+
+ if (choice.ContentUpdate.IsNullOrEmpty()) continue;
+
+ _logger.LogInformation(choice.ContentUpdate[0]?.Text);
+
+ await onMessageReceived(new RoleDialogModel(choice.Role.ToString(), choice.ContentUpdate[0]?.Text ?? string.Empty));
+ }
+
+ return true;
+ }
+
+
+ protected (string, IEnumerable, ChatCompletionOptions) PrepareOptions(Agent agent, List conversations)
+ {
+ var agentService = _services.GetRequiredService();
+ var fileService = _services.GetRequiredService();
+ var state = _services.GetRequiredService();
+ var settingsService = _services.GetRequiredService();
+ var settings = settingsService.GetSetting(Provider, _model);
+ var allowMultiModal = settings != null && settings.MultiModal;
+
+ var messages = new List();
+
+ var temperature = float.Parse(state.GetState("temperature", "0.0"));
+ var maxTokens = int.Parse(state.GetState("max_tokens", "1024"));
+ var options = new ChatCompletionOptions()
+ {
+ Temperature = temperature,
+ MaxTokens = maxTokens
+ };
+
+ foreach (var function in agent.Functions)
+ {
+ if (!agentService.RenderFunction(agent, function)) continue;
+
+ var property = agentService.RenderFunctionProperty(agent, function);
+
+ options.Tools.Add(ChatTool.CreateFunctionTool(
+ functionName: function.Name,
+ functionDescription: function.Description,
+ functionParameters: BinaryData.FromObjectAsJson(property)));
+ }
+
+ if (!string.IsNullOrEmpty(agent.Instruction))
+ {
+ var instruction = agentService.RenderedInstruction(agent);
+ messages.Add(new SystemChatMessage(instruction));
+ }
+
+ if (!string.IsNullOrEmpty(agent.Knowledges))
+ {
+ messages.Add(new SystemChatMessage(agent.Knowledges));
+ }
+
+ var samples = ProviderHelper.GetChatSamples(agent.Samples);
+ foreach (var sample in samples)
+ {
+ messages.Add(sample.Role == AgentRole.User ? new UserChatMessage(sample.Content) : new AssistantChatMessage(sample.Content));
+ }
+
+ foreach (var message in conversations)
+ {
+ if (message.Role == AgentRole.Function)
+ {
+ messages.Add(new AssistantChatMessage(string.Empty)
+ {
+ FunctionCall = new ChatFunctionCall(message.FunctionName, message.FunctionArgs ?? string.Empty)
+ });
+
+ messages.Add(new FunctionChatMessage(message.FunctionName, message.Content));
+ }
+ else if (message.Role == AgentRole.User)
+ {
+ var text = !string.IsNullOrWhiteSpace(message.Payload) ? message.Payload : message.Content;
+ var textPart = ChatMessageContentPart.CreateTextMessageContentPart(text);
+ var chat = new UserChatMessage(textPart)
+ {
+ ParticipantName = message.FunctionName
+ };
+
+ if (allowMultiModal)
+ {
+ if (!message.Files.IsNullOrEmpty())
+ {
+ foreach (var file in message.Files)
+ {
+ if (!string.IsNullOrEmpty(file.FileUrl))
+ {
+ var uri = new Uri(file.FileUrl);
+ var contentPart = ChatMessageContentPart.CreateImageMessageContentPart(uri, ImageChatMessageContentPartDetail.Low);
+ chat = new UserChatMessage(textPart, contentPart) { ParticipantName = message.FunctionName };
+ }
+ else if (!string.IsNullOrEmpty(file.FileData))
+ {
+ var (contentType, bytes) = fileService.GetFileInfoFromData(file.FileData);
+ var contentPart = ChatMessageContentPart.CreateImageMessageContentPart(BinaryData.FromBytes(bytes), contentType, ImageChatMessageContentPartDetail.Low);
+ chat = new UserChatMessage(textPart, contentPart) { ParticipantName = message.FunctionName };
+ }
+ else if (!string.IsNullOrEmpty(file.FileStorageUrl))
+ {
+ var contentType = fileService.GetFileContentType(file.FileStorageUrl);
+ using var stream = File.OpenRead(file.FileStorageUrl);
+ var contentPart = ChatMessageContentPart.CreateImageMessageContentPart(BinaryData.FromStream(stream), contentType, ImageChatMessageContentPartDetail.Low);
+ chat = new UserChatMessage(textPart, contentPart) { ParticipantName = message.FunctionName };
+ }
+ }
+ }
+ }
+ messages.Add(chat);
+ }
+ else if (message.Role == AgentRole.Assistant)
+ {
+ messages.Add(new AssistantChatMessage(message.Content));
+ }
+ }
+
+ var prompt = GetPrompt(messages, options);
+ return (prompt, messages, options);
+ }
+
+
+ private string GetPrompt(IEnumerable messages, ChatCompletionOptions options)
+ {
+ var prompt = string.Empty;
+
+ if (!messages.IsNullOrEmpty())
+ {
+ // System instruction
+ var verbose = string.Join("\r\n", messages
+ .Select(x => x as SystemChatMessage)
+ .Where(x => x != null)
+ .Select(x =>
+ {
+ if (!string.IsNullOrEmpty(x.ParticipantName))
+ {
+ // To display Agent name in log
+ return $"[{x.ParticipantName}]: {x.Content.FirstOrDefault()?.Text ?? string.Empty}";
+ }
+ return $"{AgentRole.System}: {x.Content.FirstOrDefault()?.Text ?? string.Empty}";
+ }));
+ prompt += $"{verbose}\r\n";
+
+ prompt += "\r\n[CONVERSATION]";
+ verbose = string.Join("\r\n", messages
+ .Where(x => x as SystemChatMessage == null)
+ .Select(x =>
+ {
+ var fnMessage = x as FunctionChatMessage;
+ if (fnMessage != null)
+ {
+ return $"{AgentRole.Function}: {fnMessage.Content.FirstOrDefault()?.Text ?? string.Empty}";
+ }
+
+ var userMessage = x as UserChatMessage;
+ if (userMessage != null)
+ {
+ var content = x.Content.FirstOrDefault()?.Text ?? string.Empty;
+ return !string.IsNullOrEmpty(userMessage.ParticipantName) && userMessage.ParticipantName != "route_to_agent" ?
+ $"{userMessage.ParticipantName}: {content}" :
+ $"{AgentRole.User}: {content}";
+ }
+
+ var assistMessage = x as AssistantChatMessage;
+ if (assistMessage != null)
+ {
+ return assistMessage.FunctionCall != null ?
+ $"{AgentRole.Assistant}: Call function {assistMessage.FunctionCall.FunctionName}({assistMessage.FunctionCall.FunctionArguments})" :
+ $"{AgentRole.Assistant}: {assistMessage.Content.FirstOrDefault()?.Text ?? string.Empty}";
+ }
+
+ return string.Empty;
+ }));
+ prompt += $"\r\n{verbose}\r\n";
+ }
+
+ if (!options.Tools.IsNullOrEmpty())
+ {
+ var functions = string.Join("\r\n", options.Tools.Select(fn =>
+ {
+ return $"\r\n{fn.FunctionName}: {fn.FunctionDescription}\r\n{fn.FunctionParameters}";
+ }));
+ prompt += $"\r\n[FUNCTIONS]{functions}\r\n";
+ }
+
+ return prompt;
+ }
+
+ public void SetModelName(string model)
+ {
+ _model = model;
+ }
+}
\ No newline at end of file
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Embedding/TextEmbeddingProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Embedding/TextEmbeddingProvider.cs
new file mode 100644
index 00000000..2a84b61a
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Embedding/TextEmbeddingProvider.cs
@@ -0,0 +1,73 @@
+using OpenAI.Embeddings;
+
+namespace BotSharp.Plugin.OpenAI.Providers.Embedding;
+
+public class TextEmbeddingProvider : ITextEmbedding
+{
+ protected readonly OpenAiSettings _settings;
+ protected readonly IServiceProvider _services;
+ protected readonly ILogger _logger;
+
+ private const int DEFAULT_DIMENSION = 1536;
+ protected string _model;
+
+ public virtual string Provider => "openai";
+
+ public int Dimension { get; set; }
+
+ public TextEmbeddingProvider(
+ OpenAiSettings settings,
+ ILogger logger,
+ IServiceProvider services)
+ {
+ _settings = settings;
+ _logger = logger;
+ _services = services;
+ }
+
+ public async Task GetVectorAsync(string text)
+ {
+ var client = ProviderHelper.GetClient(Provider, _model, _services);
+ var embeddingClient = client.GetEmbeddingClient(_model);
+ var options = PrepareOptions();
+ var response = await embeddingClient.GenerateEmbeddingAsync(text, options);
+ var value = response.Value;
+ return value.Vector.ToArray();
+ }
+
+ public async Task> GetVectorsAsync(List texts)
+ {
+ var client = ProviderHelper.GetClient(Provider, _model, _services);
+ var embeddingClient = client.GetEmbeddingClient(_model);
+ var options = PrepareOptions();
+ var response = await embeddingClient.GenerateEmbeddingsAsync(texts, options);
+ var value = response.Value;
+ return value.Select(x => x.Vector.ToArray()).ToList();
+ }
+
+ public void SetModelName(string model)
+ {
+ _model = model;
+ }
+
+ private EmbeddingGenerationOptions PrepareOptions()
+ {
+ return new EmbeddingGenerationOptions
+ {
+ Dimensions = GetDimension()
+ };
+ }
+
+ private int GetDimension()
+ {
+ var state = _services.GetRequiredService();
+ var stateDimension = state.GetState("embedding_dimension");
+ var defaultDimension = Dimension > 0 ? Dimension : DEFAULT_DIMENSION;
+
+ if (int.TryParse(stateDimension, out var dimension))
+ {
+ return dimension > 0 ? dimension : defaultDimension;
+ }
+ return defaultDimension;
+ }
+}
\ No newline at end of file
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageGenerationProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageGenerationProvider.cs
new file mode 100644
index 00000000..b8f512ae
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageGenerationProvider.cs
@@ -0,0 +1,222 @@
+using OpenAI.Images;
+
+namespace BotSharp.Plugin.OpenAI.Providers.Image;
+
+public class ImageGenerationProvider : IImageGeneration
+{
+ protected readonly OpenAiSettings _settings;
+ protected readonly IServiceProvider _services;
+ protected readonly ILogger _logger;
+
+ private const int DEFAULT_IMAGE_COUNT = 1;
+ private const int IMAGE_COUNT_LIMIT = 5;
+
+ protected string _model;
+
+ public virtual string Provider => "openai";
+
+ public ImageGenerationProvider(
+ OpenAiSettings settings,
+ ILogger logger,
+ IServiceProvider services)
+ {
+ _settings = settings;
+ _services = services;
+ _logger = logger;
+ }
+
+
+ public async Task GetImageGeneration(Agent agent, List conversations)
+ {
+ var contentHooks = _services.GetServices().ToList();
+
+ // Before
+ foreach (var hook in contentHooks)
+ {
+ await hook.BeforeGenerating(agent, conversations);
+ }
+
+ var client = ProviderHelper.GetClient(Provider, _model, _services);
+ var (prompt, imageCount, options) = PrepareOptions(conversations);
+ var imageClient = client.GetImageClient(_model);
+
+ var response = imageClient.GenerateImages(prompt, imageCount, options);
+ var values = response.Value;
+
+ var images = new List();
+ foreach (var value in values)
+ {
+ if (value == null) continue;
+
+ var image = new ImageGeneration { Description = value?.RevisedPrompt ?? string.Empty };
+ if (options.ResponseFormat == GeneratedImageFormat.Uri)
+ {
+ image.ImageUrl = value?.ImageUri?.AbsoluteUri ?? string.Empty;
+ }
+ else if (options.ResponseFormat == GeneratedImageFormat.Bytes)
+ {
+ var base64Str = string.Empty;
+ var bytes = value?.ImageBytes?.ToArray();
+ if (!bytes.IsNullOrEmpty())
+ {
+ base64Str = Convert.ToBase64String(bytes);
+ }
+ image.ImageData = base64Str;
+ }
+
+ images.Add(image);
+ }
+
+ var content = string.Join("\r\n", images.Select(x => x.Description));
+ var responseMessage = new RoleDialogModel(AgentRole.Assistant, content)
+ {
+ CurrentAgentId = agent.Id,
+ MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty,
+ GeneratedImages = images
+ };
+
+ // After
+ foreach (var hook in contentHooks)
+ {
+ await hook.AfterGenerated(responseMessage, new TokenStatsModel
+ {
+ Prompt = prompt,
+ Provider = Provider,
+ Model = _model,
+ PromptCount = prompt.Split(' ', StringSplitOptions.RemoveEmptyEntries).Count(),
+ CompletionCount = content.Split(' ', StringSplitOptions.RemoveEmptyEntries).Count()
+ });
+ }
+
+ return responseMessage;
+ }
+
+ private (string, int, ImageGenerationOptions) PrepareOptions(List conversations)
+ {
+ var prompt = conversations.LastOrDefault()?.Payload ?? conversations.LastOrDefault()?.Content ?? string.Empty;
+
+ var state = _services.GetRequiredService();
+ var size = state.GetState("image_size");
+ var quality = state.GetState("image_quality");
+ var style = state.GetState("image_style");
+ var format = state.GetState("image_format");
+ var count = GetImageCount(state.GetState("image_count", "1"));
+
+ var options = new ImageGenerationOptions
+ {
+ Size = GetImageSize(size),
+ Quality = GetImageQuality(quality),
+ Style = GetImageStyle(style),
+ ResponseFormat = GetImageFormat(format)
+ };
+ return (prompt, count, options);
+ }
+
+ public void SetModelName(string model)
+ {
+ _model = model;
+ }
+
+ private GeneratedImageSize GetImageSize(string size)
+ {
+ var value = !string.IsNullOrEmpty(size) ? size : "1024x1024";
+
+ GeneratedImageSize retSize;
+ switch (value)
+ {
+ case "256x256":
+ retSize = GeneratedImageSize.W256xH256;
+ break;
+ case "512x512":
+ retSize = GeneratedImageSize.W512xH512;
+ break;
+ case "1024x1024":
+ retSize = GeneratedImageSize.W1024xH1024;
+ break;
+ case "1024x1792":
+ retSize = GeneratedImageSize.W1024xH1792;
+ break;
+ case "1792x1024":
+ retSize = GeneratedImageSize.W1792xH1024;
+ break;
+ default:
+ retSize = GeneratedImageSize.W1024xH1024;
+ break;
+ }
+
+ return retSize;
+ }
+
+ private GeneratedImageQuality GetImageQuality(string quality)
+ {
+ var value = !string.IsNullOrEmpty(quality) ? quality : "standard";
+
+ GeneratedImageQuality retQuality;
+ switch (value)
+ {
+ case "standard":
+ retQuality = GeneratedImageQuality.Standard;
+ break;
+ case "hd":
+ retQuality = GeneratedImageQuality.High;
+ break;
+ default:
+ retQuality = GeneratedImageQuality.Standard;
+ break;
+ }
+
+ return retQuality;
+ }
+
+ private GeneratedImageStyle GetImageStyle(string style)
+ {
+ var value = !string.IsNullOrEmpty(style) ? style : "natural";
+
+ GeneratedImageStyle retStyle;
+ switch (value)
+ {
+ case "natural":
+ retStyle = GeneratedImageStyle.Natural;
+ break;
+ case "vivid":
+ retStyle = GeneratedImageStyle.Vivid;
+ break;
+ default:
+ retStyle = GeneratedImageStyle.Natural;
+ break;
+ }
+
+ return retStyle;
+ }
+
+ private GeneratedImageFormat GetImageFormat(string format)
+ {
+ var value = !string.IsNullOrEmpty(format) ? format : "uri";
+
+ GeneratedImageFormat retFormat;
+ switch (value)
+ {
+ case "uri":
+ retFormat = GeneratedImageFormat.Uri;
+ break;
+ case "bytes":
+ retFormat = GeneratedImageFormat.Bytes;
+ break;
+ default:
+ retFormat = GeneratedImageFormat.Uri;
+ break;
+ }
+
+ return retFormat;
+ }
+
+ private int GetImageCount(string count)
+ {
+ if (!int.TryParse(count, out var retCount))
+ {
+ return DEFAULT_IMAGE_COUNT;
+ }
+
+ return retCount > 0 && retCount <= IMAGE_COUNT_LIMIT ? retCount : DEFAULT_IMAGE_COUNT;
+ }
+}
\ No newline at end of file
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/ProviderHelper.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/ProviderHelper.cs
new file mode 100644
index 00000000..b385b380
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/ProviderHelper.cs
@@ -0,0 +1,41 @@
+using OpenAI;
+using System.ClientModel;
+
+namespace BotSharp.Plugin.OpenAI.Providers;
+
+public class ProviderHelper
+{
+ public static OpenAIClient GetClient(string provider, string model, IServiceProvider services)
+ {
+ var settingsService = services.GetRequiredService();
+ var settings = settingsService.GetSetting(provider, model);
+ var client = new OpenAIClient(new ApiKeyCredential(settings.ApiKey));
+ return client;
+ }
+
+ public static List GetChatSamples(List lines)
+ {
+ var samples = new List();
+
+ for (int i = 0; i < lines.Count; 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;
+ }
+}
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Text/TextCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Text/TextCompletionProvider.cs
new file mode 100644
index 00000000..c2180076
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Text/TextCompletionProvider.cs
@@ -0,0 +1,145 @@
+using BotSharp.Abstraction.MLTasks.Settings;
+using System.Net.Http;
+using System.Net.Mime;
+using System.Text.Json;
+using System.Text;
+
+namespace BotSharp.Plugin.OpenAI.Providers.Text;
+
+public class TextCompletionProvider : ITextCompletion
+{
+ private readonly IServiceProvider _services;
+ private readonly ILogger _logger;
+ private readonly OpenAiSettings _settings;
+ protected string _model;
+
+ protected readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ PropertyNameCaseInsensitive = true,
+ WriteIndented = true,
+ AllowTrailingCommas = true,
+ };
+
+ public virtual string Provider => "openai";
+
+ public TextCompletionProvider(
+ OpenAiSettings settings,
+ ILogger logger,
+ IServiceProvider services)
+ {
+ _services = services;
+ _logger = logger;
+ _settings = settings;
+ }
+
+ public async Task GetCompletion(string text, string agentId, string messageId)
+ {
+ var contentHooks = _services.GetServices().ToList();
+
+ // Before chat completion hook
+ var agent = new Agent()
+ {
+ Id = agentId,
+ };
+ var message = new RoleDialogModel(AgentRole.User, text)
+ {
+ CurrentAgentId = agentId,
+ MessageId = messageId
+ };
+
+ Task.WaitAll(contentHooks.Select(hook =>
+ hook.BeforeGenerating(agent,
+ new List
+ {
+ message
+ })).ToArray());
+
+ var state = _services.GetRequiredService();
+ var temperature = float.Parse(state.GetState("temperature", "0.0"));
+
+ var settingsService = _services.GetRequiredService();
+ var modelSetting = settingsService.GetSetting(Provider, _model);
+ var apiUrl = BuildApiUrl(modelSetting);
+ var apiKey = modelSetting.ApiKey;
+ var response = await GetTextCompletion(apiUrl, apiKey, text, temperature);
+
+ // OpenAI
+ var completion = "";
+ foreach (var t in response.Choices)
+ {
+ completion += t?.Text ?? string.Empty;
+ };
+
+ // After chat completion hook
+ var responseMessage = new RoleDialogModel(AgentRole.Assistant, completion)
+ {
+ CurrentAgentId = agentId,
+ MessageId = messageId
+ };
+ Task.WaitAll(contentHooks.Select(hook =>
+ hook.AfterGenerated(responseMessage, new TokenStatsModel
+ {
+ Prompt = text,
+ Provider = Provider,
+ Model = _model,
+ PromptCount = response.Usage?.PromptTokens ?? default,
+ CompletionCount = response.Usage?.CompletionTokens ?? default
+ })).ToArray());
+
+ return completion.Trim();
+ }
+
+ public void SetModelName(string model)
+ {
+ _model = model;
+ }
+
+ private async Task GetTextCompletion(string apiUrl, string apiKey, string prompt, float temperature, int maxTokens = 256)
+ {
+ try
+ {
+ var http = _services.GetRequiredService();
+ using var httpClient = http.CreateClient();
+ AddHeader(httpClient, apiKey);
+
+ var request = new TextCompletionRequest
+ {
+ Model = _model,
+ Prompt = prompt,
+ MaxTokens = maxTokens,
+ Temperature = temperature
+ };
+ var data = JsonSerializer.Serialize(request, _jsonOptions);
+ var httpRequest = new HttpRequestMessage
+ {
+ Method = HttpMethod.Post,
+ RequestUri = new Uri(apiUrl),
+ Content = new StringContent(data, Encoding.UTF8, MediaTypeNames.Application.Json)
+ };
+
+ var httpResponse = await httpClient.SendAsync(httpRequest);
+ httpResponse.EnsureSuccessStatusCode();
+ var responseStr = await httpResponse.Content.ReadAsStringAsync();
+ var response = JsonSerializer.Deserialize(responseStr, _jsonOptions);
+ return response;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError($"Error when {Provider}-{_model} generating text... {ex.Message}");
+ throw;
+ }
+ }
+
+ private string BuildApiUrl(LlmModelSetting modelSetting)
+ {
+ var endpoint = modelSetting.Endpoint.EndsWith("/") ?
+ modelSetting.Endpoint.Substring(0, modelSetting.Endpoint.Length - 1) : modelSetting.Endpoint;
+ return endpoint ?? string.Empty;
+ }
+
+ private void AddHeader(HttpClient httpClient, string apiKey)
+ {
+ httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}");
+ }
+}
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Settings/OpenAiSettings.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Settings/OpenAiSettings.cs
new file mode 100644
index 00000000..3d0fbf18
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Settings/OpenAiSettings.cs
@@ -0,0 +1,5 @@
+namespace BotSharp.Plugin.OpenAI.Settings;
+
+public class OpenAiSettings
+{
+}
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Using.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Using.cs
new file mode 100644
index 00000000..fe9a02b4
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Using.cs
@@ -0,0 +1,19 @@
+global using System;
+global using System.Collections.Generic;
+global using System.Linq;
+global using System.IO;
+global using System.Threading.Tasks;
+global using Microsoft.Extensions.DependencyInjection;
+global using Microsoft.Extensions.Logging;
+global using BotSharp.Abstraction.Agents.Enums;
+global using BotSharp.Abstraction.Agents.Models;
+global using BotSharp.Abstraction.Conversations;
+global using BotSharp.Abstraction.Conversations.Models;
+global using BotSharp.Abstraction.Loggers;
+global using BotSharp.Abstraction.MLTasks;
+global using BotSharp.Abstraction.Agents;
+global using BotSharp.Abstraction.Files;
+global using BotSharp.Abstraction.Files.Models;
+global using BotSharp.Abstraction.Utilities;
+global using BotSharp.Plugin.OpenAI.Models;
+global using BotSharp.Plugin.OpenAI.Settings;
\ No newline at end of file
diff --git a/src/Plugins/BotSharp.Plugin.Qdrant/BotSharp.Plugin.Qdrant.csproj b/src/Plugins/BotSharp.Plugin.Qdrant/BotSharp.Plugin.Qdrant.csproj
index 817c90c5..33fae3f9 100644
--- a/src/Plugins/BotSharp.Plugin.Qdrant/BotSharp.Plugin.Qdrant.csproj
+++ b/src/Plugins/BotSharp.Plugin.Qdrant/BotSharp.Plugin.Qdrant.csproj
@@ -11,8 +11,7 @@
-
-
+
diff --git a/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs b/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs
index 3748b45f..c571fba1 100644
--- a/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs
+++ b/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs
@@ -32,6 +32,7 @@ public class QdrantDb : IVectorDb
_client = new QdrantClient
(
host: _setting.Url,
+ https: true,
apiKey: _setting.ApiKey
);
}
@@ -41,7 +42,7 @@ public class QdrantDb : IVectorDb
public async Task> GetCollections()
{
// List all the collections
- var collections = await _client.ListCollectionsAsync();
+ var collections = await GetClient().ListCollectionsAsync();
return collections.ToList();
}
@@ -56,11 +57,6 @@ public class QdrantDb : IVectorDb
Size = (ulong)dim,
Distance = Distance.Cosine
});
-
- var agentService = _services.GetRequiredService();
- var agentDataDir = agentService.GetAgentDataDir(collectionName);
- var knowledgePath = Path.Combine(agentDataDir, "knowledge.txt");
- File.WriteAllLines(knowledgePath, new string[0]);
}
// Get collection info
@@ -71,26 +67,29 @@ public class QdrantDb : IVectorDb
}
}
- public async Task Upsert(string collectionName, int id, float[] vector, string text)
+ public async Task Upsert(string collectionName, string id, float[] vector, string text, Dictionary? payload = null)
{
// Insert vectors
- await GetClient().UpsertAsync(collectionName, points: new List
+ var point = new PointStruct()
{
- new PointStruct()
+ Id = new PointId()
{
- Id = new PointId()
- {
- Num = (ulong)id,
- },
- Vectors = vector
- }
- });
+ Uuid = id
+ },
+ Vectors = vector,
- // Store chunks in local file system
- var agentService = _services.GetRequiredService();
- var agentDataDir = agentService.GetAgentDataDir(collectionName);
- var knowledgePath = Path.Combine(agentDataDir, "knowledge.txt");
- File.AppendAllLines(knowledgePath, new[] { text });
+ Payload = { }
+ };
+
+ foreach (var item in payload)
+ {
+ point.Payload.Add(item.Key, item.Value);
+ }
+
+ var result = await GetClient().UpsertAsync(collectionName, points: new List
+ {
+ point
+ });
}
public async Task> Search(string collectionName, float[] vector, int limit = 5)
diff --git a/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelMemoryStoreProvider.cs b/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelMemoryStoreProvider.cs
index 7df2c0cd..962957f5 100644
--- a/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelMemoryStoreProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelMemoryStoreProvider.cs
@@ -48,7 +48,7 @@ namespace BotSharp.Plugin.SemanticKernel
}
- public async Task Upsert(string collectionName, int id, float[] vector, string text)
+ public async Task Upsert(string collectionName, string id, float[] vector, string text, Dictionary? payload = null)
{
#pragma warning disable SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
await _memoryStore.UpsertAsync(collectionName, MemoryRecord.LocalRecord(id.ToString(), text, null, vector));
diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj b/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj
index acaa4595..69909d90 100644
--- a/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj
+++ b/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj
@@ -17,8 +17,8 @@
-
-
+
+
diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs
index 151cf506..a0fae6f3 100644
--- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs
+++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs
@@ -111,9 +111,20 @@ public class PlaywrightInstance : IDisposable
e.Headers["content-type"].Contains("application/json") &&
e.Request.ResourceType == "fetch")
{
- Serilog.Log.Information($"Response: {e.Url}");
- var json = await e.JsonAsync();
- fetched(e.Url.ToLower(), JsonSerializer.Serialize(json));
+ Serilog.Log.Information($"fetched: {e.Url}");
+ JsonElement? json = null;
+ try
+ {
+ json = await e.JsonAsync();
+ }
+ catch(Exception ex)
+ {
+ Serilog.Log.Error(ex.ToString());
+ }
+ finally
+ {
+ fetched(e.Url.ToLower(), JsonSerializer.Serialize(json ?? new JsonElement()));
+ }
}
};
}
diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs
index 6d8006f6..1ca20d28 100644
--- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs
+++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs
@@ -5,7 +5,19 @@ public partial class PlaywrightWebDriver
public async Task DoAction(MessageInfo message, ElementActionArgs action, BrowserActionResult result)
{
var page = _instance.GetPage(message.ContextId);
+ if (string.IsNullOrEmpty(result.Selector))
+ {
+ Serilog.Log.Error($"Selector is not set.");
+ return;
+ }
+
ILocator locator = page.Locator(result.Selector);
+ var count = await locator.CountAsync();
+ if (count == 0)
+ {
+ Serilog.Log.Error($"Element not found: {result.Selector}");
+ return;
+ }
if (action.Action == BroswerActionEnum.Click)
{
diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs
index f0b9c738..8f122213 100644
--- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs
+++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs
@@ -29,6 +29,7 @@ public partial class PlaywrightWebDriver
var page = args.OpenNewTab ? await _instance.NewPage(message.ContextId, fetched: args.OnDataFetched) :
_instance.GetPage(message.ContextId);
+ Serilog.Log.Information($"goto page: {args.Url}");
var response = await page.GotoAsync(args.Url, new PageGotoOptions
{
Timeout = args.Timeout
diff --git a/src/WebStarter/WebStarter.csproj b/src/WebStarter/WebStarter.csproj
index 4764cbb1..1601a898 100644
--- a/src/WebStarter/WebStarter.csproj
+++ b/src/WebStarter/WebStarter.csproj
@@ -1,4 +1,4 @@
-
+
$(TargetFramework)
@@ -11,6 +11,7 @@
+
@@ -39,6 +40,7 @@
+
diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json
index 6439e25a..84c02399 100644
--- a/src/WebStarter/appsettings.json
+++ b/src/WebStarter/appsettings.json
@@ -279,6 +279,7 @@
"BotSharp.Logger",
"BotSharp.Plugin.MongoStorage",
"BotSharp.Plugin.Dashboard",
+ "BotSharp.Plugin.OpenAI",
"BotSharp.Plugin.AzureOpenAI",
"BotSharp.Plugin.AnthropicAI",
"BotSharp.Plugin.GoogleAI",