diff --git a/BotSharp.sln b/BotSharp.sln
index 8dda48e83..d281450e8 100644
--- a/BotSharp.sln
+++ b/BotSharp.sln
@@ -111,7 +111,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.PythonInter
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Graph", "Graph", "{97A0B191-64D7-4F8A-BFE8-1BFCC5E247E1}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.Graph", "src\Plugins\BotSharp.Plugin.Graph\BotSharp.Plugin.Graph.csproj", "{EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.Graph", "src\Plugins\BotSharp.Plugin.Graph\BotSharp.Plugin.Graph.csproj", "{EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.AudioHandler", "src\Plugins\BotSharp.Plugin.AudioHandler\BotSharp.Plugin.AudioHandler.csproj", "{F57F4862-F8D4-44A1-AC12-5C131B5C9785}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -449,6 +451,14 @@ Global
{EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Release|Any CPU.Build.0 = Release|Any CPU
{EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Release|x64.ActiveCfg = Release|Any CPU
{EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Release|x64.Build.0 = Release|Any CPU
+ {F57F4862-F8D4-44A1-AC12-5C131B5C9785}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F57F4862-F8D4-44A1-AC12-5C131B5C9785}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F57F4862-F8D4-44A1-AC12-5C131B5C9785}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F57F4862-F8D4-44A1-AC12-5C131B5C9785}.Debug|x64.Build.0 = Debug|Any CPU
+ {F57F4862-F8D4-44A1-AC12-5C131B5C9785}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F57F4862-F8D4-44A1-AC12-5C131B5C9785}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F57F4862-F8D4-44A1-AC12-5C131B5C9785}.Release|x64.ActiveCfg = Release|Any CPU
+ {F57F4862-F8D4-44A1-AC12-5C131B5C9785}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -503,6 +513,7 @@ Global
{05E6E405-5021-406E-8A5E-0A7CEC881F6D} = {C4C59872-3C8A-450D-83D5-2BE402D610D5}
{97A0B191-64D7-4F8A-BFE8-1BFCC5E247E1} = {2635EC9B-2E5F-4313-AC21-0B847F31F36C}
{EBFE97DA-D0BA-48BA-8B5D-083B60348D1D} = {97A0B191-64D7-4F8A-BFE8-1BFCC5E247E1}
+ {F57F4862-F8D4-44A1-AC12-5C131B5C9785} = {51AFE054-AE99-497D-A593-69BAEFB5106F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19}
diff --git a/src/BotSharp.AppHost/BotSharp.AppHost.csproj b/src/BotSharp.AppHost/BotSharp.AppHost.csproj
index 6e8a2ae32..2a2705db0 100644
--- a/src/BotSharp.AppHost/BotSharp.AppHost.csproj
+++ b/src/BotSharp.AppHost/BotSharp.AppHost.csproj
@@ -10,7 +10,7 @@
-
+
diff --git a/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj b/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj
index 94c9e27c5..697f94a56 100644
--- a/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj
+++ b/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj
@@ -1,4 +1,4 @@
-
+
$(TargetFramework)
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs
index 2018d6a79..8484c5fb0 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs
@@ -1,7 +1,6 @@
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Messaging;
using BotSharp.Abstraction.Messaging.Models.RichContent;
-using BotSharp.Abstraction.MLTasks;
namespace BotSharp.Abstraction.Conversations.Models;
@@ -75,6 +74,9 @@ public class RoleDialogModel : ITrackableMessage
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public RichContent? RichContent { get; set; }
+ ///
+ /// Rich content for secondary language
+ ///
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public RichContent? SecondaryRichContent { get; set; }
@@ -86,6 +88,9 @@ public class RoleDialogModel : ITrackableMessage
public FunctionCallFromLlm Instruction { get; set; }
+ ///
+ /// Files to be used in conversation
+ ///
public List Files { get; set; } = new List();
///
@@ -94,7 +99,8 @@ public class RoleDialogModel : ITrackableMessage
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("generated_images")]
public List GeneratedImages { get; set; } = new List();
- public float KnowledgeConfidence { get; set; } = 0.5f;
+
+
private RoleDialogModel()
{
}
@@ -134,8 +140,7 @@ public static RoleDialogModel From(RoleDialogModel source,
Payload = source.Payload,
StopCompletion = source.StopCompletion,
Instruction = source.Instruction,
- Data = source.Data,
- KnowledgeConfidence = source.KnowledgeConfidence
+ Data = source.Data
};
}
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/Converters/IPdf2ImageConverter.cs b/src/Infrastructure/BotSharp.Abstraction/Files/Converters/IPdf2ImageConverter.cs
index 87df61374..24b284029 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Files/Converters/IPdf2ImageConverter.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Files/Converters/IPdf2ImageConverter.cs
@@ -2,7 +2,7 @@ namespace BotSharp.Abstraction.Files.Converters;
public interface IPdf2ImageConverter
{
- public string Name { get; }
+ public string Provider { get; }
///
/// Convert pdf pages to images, and return a list of image file paths
diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/FileCoreSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Files/FileCoreSettings.cs
index 10ccd1a09..dc4ac40bb 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Files/FileCoreSettings.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Files/FileCoreSettings.cs
@@ -5,6 +5,11 @@ namespace BotSharp.Abstraction.Files;
public class FileCoreSettings
{
public string Storage { get; set; } = FileStorageEnum.LocalFileStorage;
- public string Pdf2TextConverter { get; set; }
- public string Pdf2ImageConverter { get; set; }
+ public SettingBase Pdf2TextConverter { get; set; }
+ public SettingBase Pdf2ImageConverter { get; set; }
}
+
+public class SettingBase
+{
+ public string Provider { get; set; }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/IFileInstructService.cs b/src/Infrastructure/BotSharp.Abstraction/Files/IFileInstructService.cs
index 78a400e1d..f8f4c605a 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Files/IFileInstructService.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Files/IFileInstructService.cs
@@ -3,11 +3,11 @@ namespace BotSharp.Abstraction.Files;
public interface IFileInstructService
{
#region Image
- Task ReadImages(string? provider, string? model, string text, IEnumerable images);
+ Task ReadImages(string? provider, string? model, string text, IEnumerable images);
Task GenerateImage(string? provider, string? model, string text);
- Task VaryImage(string? provider, string? model, BotSharpFile image);
- Task EditImage(string? provider, string? model, string text, BotSharpFile image);
- Task EditImage(string? provider, string? model, string text, BotSharpFile image, BotSharpFile mask);
+ Task VaryImage(string? provider, string? model, InstructFileModel image);
+ Task EditImage(string? provider, string? model, string text, InstructFileModel image);
+ Task EditImage(string? provider, string? model, string text, InstructFileModel image, InstructFileModel mask);
#endregion
#region Pdf
@@ -17,7 +17,11 @@ public interface IFileInstructService
///
/// Pdf files
///
- Task ReadPdf(string? provider, string? model, string? modelId, string prompt, List files);
+ Task ReadPdf(string? provider, string? model, string? modelId, string prompt, List files);
+ #endregion
+
+ #region Audio
+ Task SpeechToText(string? provider, string? model, InstructFileModel audio, string? text = null);
#endregion
#region Select file
diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/IFileStorageService.cs b/src/Infrastructure/BotSharp.Abstraction/Files/IFileStorageService.cs
index a3e5a32dc..373466c41 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Files/IFileStorageService.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Files/IFileStorageService.cs
@@ -6,6 +6,7 @@ public interface IFileStorageService
{
#region Common
string GetDirectory(string conversationId);
+ IEnumerable GetFiles(string relativePath, string? searchQuery = null);
byte[] GetFileBytes(string fileStorageUrl);
bool SaveFileStreamToPath(string filePath, Stream stream);
bool SaveFileBytesToPath(string filePath, byte[] bytes);
@@ -16,7 +17,6 @@ public interface IFileStorageService
string BuildDirectory(params string[] segments);
#endregion
-
#region Conversation
///
/// Get the message file screenshots for specific content types, e.g., pdf
@@ -24,7 +24,7 @@ public interface IFileStorageService
///
///
///
- Task> GetMessageFileScreenshots(string conversationId, IEnumerable messageIds);
+ Task> GetMessageFileScreenshotsAsync(string conversationId, IEnumerable messageIds);
///
/// Get the files that have been uploaded in the chat. No screenshot images are included.
@@ -37,7 +37,7 @@ public interface IFileStorageService
IEnumerable GetMessageFiles(string conversationId, IEnumerable messageIds, string source, IEnumerable? contentTypes = null);
string GetMessageFile(string conversationId, string messageId, string source, string index, string fileName);
IEnumerable GetMessagesWithFile(string conversationId, IEnumerable messageIds);
- bool SaveMessageFiles(string conversationId, string messageId, string source, List files);
+ bool SaveMessageFiles(string conversationId, string messageId, string source, List files);
///
/// Delete files under messages
@@ -54,10 +54,11 @@ public interface IFileStorageService
#region User
string GetUserAvatar();
- bool SaveUserAvatar(BotSharpFile file);
+ bool SaveUserAvatar(InputFileModel file);
#endregion
+
#region Speech
- Task SaveSpeechFileAsync(string conversationId, string fileName, BinaryData data);
- Task RetrieveSpeechFileAsync(string conversationId, string fileName);
+ bool SaveSpeechFile(string conversationId, string fileName, BinaryData data);
+ BinaryData GetSpeechFile(string conversationId, string fileName);
#endregion
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs b/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs
index 11a11e469..bc6706269 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs
@@ -1,7 +1,12 @@
namespace BotSharp.Abstraction.Files.Models;
-public class BotSharpFile : FileBase
+public class BotSharpFile : FileInformation
{
-
+ ///
+ /// File data => format: "data:image/png;base64,aaaaaaaa"
+ ///
+ [JsonPropertyName("file_data")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? FileData { get; set; } = string.Empty;
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/Models/FileBase.cs b/src/Infrastructure/BotSharp.Abstraction/Files/Models/FileBase.cs
index 3483921af..f952e7f90 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Files/Models/FileBase.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Files/Models/FileBase.cs
@@ -2,20 +2,6 @@ namespace BotSharp.Abstraction.Files.Models;
public class FileBase
{
- ///
- /// External file url
- ///
- [JsonPropertyName("file_url")]
- [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
- public string? FileUrl { get; set; } = string.Empty;
-
- ///
- /// Internal file storage url
- ///
- [JsonPropertyName("file_storage_url")]
- [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
- public string? FileStorageUrl { get; set; } = string.Empty;
-
///
/// File name without extension
///
@@ -29,18 +15,4 @@ public class FileBase
[JsonPropertyName("file_data")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? FileData { get; set; } = string.Empty;
-
- ///
- /// File content type
- ///
- [JsonPropertyName("content_type")]
- [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
- public string? ContentType { get; set; } = string.Empty;
-
- ///
- /// File extension without dot
- ///
- [JsonPropertyName("file_extension")]
- [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
- public string? FileExtension { get; set; } = string.Empty;
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/Models/FileInformation.cs b/src/Infrastructure/BotSharp.Abstraction/Files/Models/FileInformation.cs
new file mode 100644
index 000000000..fe767e163
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Files/Models/FileInformation.cs
@@ -0,0 +1,38 @@
+namespace BotSharp.Abstraction.Files.Models;
+
+public class FileInformation
+{
+ ///
+ /// External file url
+ ///
+ [JsonPropertyName("file_url")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? FileUrl { get; set; } = string.Empty;
+
+ ///
+ /// Internal file storage url
+ ///
+ [JsonPropertyName("file_storage_url")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? FileStorageUrl { get; set; } = string.Empty;
+
+ ///
+ /// File content type
+ ///
+ [JsonPropertyName("content_type")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? ContentType { get; set; } = string.Empty;
+
+ ///
+ /// File name without extension
+ ///
+ [JsonPropertyName("file_name")]
+ public string FileName { get; set; } = string.Empty;
+
+ ///
+ /// File extension without dot
+ ///
+ [JsonPropertyName("file_extension")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? FileExtension { get; set; } = string.Empty;
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/Models/InputFileModel.cs b/src/Infrastructure/BotSharp.Abstraction/Files/Models/InputFileModel.cs
new file mode 100644
index 000000000..7eb350e3d
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Files/Models/InputFileModel.cs
@@ -0,0 +1,16 @@
+namespace BotSharp.Abstraction.Files.Models;
+
+public class InputFileModel : FileBase
+{
+ ///
+ /// File name with extension
+ ///
+ [JsonPropertyName("file_name")]
+ public new string FileName { get; set; } = string.Empty;
+
+ ///
+ /// File data => format: "data:image/png;base64,aaaaaaaa"
+ ///
+ [JsonPropertyName("file_data")]
+ public new string FileData { get; set; } = string.Empty;
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/Models/InputMessageFiles.cs b/src/Infrastructure/BotSharp.Abstraction/Files/Models/InputMessageFiles.cs
deleted file mode 100644
index 29749bce3..000000000
--- a/src/Infrastructure/BotSharp.Abstraction/Files/Models/InputMessageFiles.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace BotSharp.Abstraction.Files.Models;
-
-public class InputMessageFiles
-{
- public List Files { get; set; } = new List();
- public BotSharpFile? Mask { get; set; }
-}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/Models/InstructFileModel.cs b/src/Infrastructure/BotSharp.Abstraction/Files/Models/InstructFileModel.cs
new file mode 100644
index 000000000..7eaddd5af
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Files/Models/InstructFileModel.cs
@@ -0,0 +1,18 @@
+namespace BotSharp.Abstraction.Files.Models;
+
+public class InstructFileModel : FileBase
+{
+ ///
+ /// File extension without dot
+ ///
+ [JsonPropertyName("file_extension")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? FileExtension { get; set; } = string.Empty;
+
+ ///
+ /// External file url
+ ///
+ [JsonPropertyName("file_url")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? FileUrl { get; set; } = string.Empty;
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/Models/MessageFileModel.cs b/src/Infrastructure/BotSharp.Abstraction/Files/Models/MessageFileModel.cs
index 2a0128b66..da2ddec66 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Files/Models/MessageFileModel.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Files/Models/MessageFileModel.cs
@@ -1,6 +1,6 @@
namespace BotSharp.Abstraction.Files.Models;
-public class MessageFileModel : FileBase
+public class MessageFileModel : FileInformation
{
[JsonPropertyName("message_id")]
public string MessageId { get; set; }
diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/Utilities/FileUtility.cs b/src/Infrastructure/BotSharp.Abstraction/Files/Utilities/FileUtility.cs
index df33906df..bba60b101 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Files/Utilities/FileUtility.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Files/Utilities/FileUtility.cs
@@ -1,4 +1,6 @@
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.StaticFiles;
+using System.IO;
namespace BotSharp.Abstraction.Files.Utilities;
@@ -26,11 +28,30 @@ public static (string, byte[]) GetFileInfoFromData(string data)
return (contentType, Convert.FromBase64String(base64Str));
}
- public static string GetFileContentType(string filePath)
+ public static string BuildFileDataFromFile(string fileName, byte[] bytes)
+ {
+ var contentType = GetFileContentType(fileName);
+ var base64 = Convert.ToBase64String(bytes);
+ return $"data:{contentType};base64,{base64}";
+ }
+
+ public static string BuildFileDataFromFile(IFormFile file)
+ {
+ using var stream = new MemoryStream();
+ file.CopyTo(stream);
+ stream.Position = 0;
+ var contentType = GetFileContentType(file.FileName);
+ var base64 = Convert.ToBase64String(stream.ToArray());
+ stream.Close();
+
+ return $"data:{contentType};base64,{base64}";
+ }
+
+ public static string GetFileContentType(string fileName)
{
string contentType;
var provider = new FileExtensionContentTypeProvider();
- if (!provider.TryGetContentType(filePath, out contentType))
+ if (!provider.TryGetContentType(fileName, out contentType))
{
contentType = string.Empty;
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Graph/IGraphDb.cs b/src/Infrastructure/BotSharp.Abstraction/Graph/IGraphDb.cs
index 64c5388b9..b15503e22 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Graph/IGraphDb.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Graph/IGraphDb.cs
@@ -4,7 +4,7 @@ namespace BotSharp.Abstraction.Graph;
public interface IGraphDb
{
- public string Name { get; }
+ public string Provider { get; }
Task Search(string query, GraphSearchOptions options);
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs
index 20825791f..1021ea1c0 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs
@@ -5,6 +5,9 @@ namespace BotSharp.Abstraction.Knowledges;
public interface IKnowledgeService
{
+ #region Vector
+ Task CreateVectorCollection(string collectionName, int dimension);
+ Task DeleteVectorCollection(string collectionName);
Task> GetVectorCollections();
Task> SearchVectorKnowledge(string query, string collectionName, VectorSearchOptions options);
Task FeedVectorKnowledge(string collectionName, KnowledgeCreationModel model);
@@ -12,6 +15,10 @@ public interface IKnowledgeService
Task DeleteVectorCollectionData(string collectionName, string id);
Task CreateVectorCollectionData(string collectionName, VectorCreateModel create);
Task UpdateVectorCollectionData(string collectionName, VectorUpdateModel update);
+ #endregion
+
+ #region Graph
Task SearchGraphKnowledge(string query, GraphSearchOptions options);
Task SearchKnowledge(string query, string collectionName, VectorSearchOptions vectorOptions, GraphSearchOptions graphOptions);
+ #endregion
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IPdf2TextConverter.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IPdf2TextConverter.cs
index b8f2d47b1..d6edad54c 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IPdf2TextConverter.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IPdf2TextConverter.cs
@@ -2,7 +2,7 @@ namespace BotSharp.Abstraction.Knowledges
{
public interface IPdf2TextConverter
{
- public string Name { get; }
+ public string Provider { get; }
Task ConvertPdfToText(string filePath, int? startPageNum, int? endPageNum);
}
}
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/ITextChopper.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/ITextChopper.cs
deleted file mode 100644
index 7d06a8a41..000000000
--- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/ITextChopper.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using BotSharp.Abstraction.Knowledges.Models;
-
-namespace BotSharp.Abstraction.Knowledges;
-
-///
-/// Chop large content into chunks
-///
-public interface ITextChopper
-{
- List Chop(string content, ChunkOption option);
-}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/Settings/KnowledgeBaseSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Settings/KnowledgeBaseSettings.cs
index 963a2b3b3..217dafb9e 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/Settings/KnowledgeBaseSettings.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Settings/KnowledgeBaseSettings.cs
@@ -4,8 +4,8 @@ namespace BotSharp.Abstraction.Knowledges.Settings;
public class KnowledgeBaseSettings
{
- public string VectorDb { get; set; }
- public string GraphDb { get; set; }
+ public SettingBase VectorDb { get; set; }
+ public SettingBase GraphDb { get; set; }
public DefaultKnowledgeBaseSetting Default { get; set; }
public List Collections { get; set; } = new();
@@ -23,9 +23,13 @@ public class VectorCollectionSetting
public KnowledgeTextEmbeddingSetting TextEmbedding { get; set; }
}
-public class KnowledgeTextEmbeddingSetting
+public class KnowledgeTextEmbeddingSetting : SettingBase
{
- public string Provider { get; set; }
public string Model { get; set; }
public int Dimension { get; set; }
+}
+
+public class SettingBase
+{
+ public string Provider { get; set; }
}
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.Abstraction/MLTasks/IAudioCompletion.cs b/src/Infrastructure/BotSharp.Abstraction/MLTasks/IAudioCompletion.cs
new file mode 100644
index 000000000..54ed2d7d3
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/MLTasks/IAudioCompletion.cs
@@ -0,0 +1,13 @@
+using System.IO;
+
+namespace BotSharp.Abstraction.MLTasks;
+
+public interface IAudioCompletion
+{
+ string Provider { get; }
+
+ Task GenerateTextFromAudioAsync(Stream audio, string audioFileName, string? text = null);
+ Task GenerateAudioFromTextAsync(string text);
+
+ void SetModelName(string model);
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MLTasks/ISpeechToText.cs b/src/Infrastructure/BotSharp.Abstraction/MLTasks/ISpeechToText.cs
deleted file mode 100644
index 9e0dd574e..000000000
--- a/src/Infrastructure/BotSharp.Abstraction/MLTasks/ISpeechToText.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace BotSharp.Abstraction.MLTasks;
-
-public interface ISpeechToText
-{
- string Provider { get; }
-
- Task GenerateTextFromAudioAsync(string filePath);
- // Task AudioToTextTranscript(Stream stream);
- Task SetModelName(string modelType);
-}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MLTasks/ITextToSpeech.cs b/src/Infrastructure/BotSharp.Abstraction/MLTasks/ITextToSpeech.cs
deleted file mode 100644
index 344fad0ea..000000000
--- a/src/Infrastructure/BotSharp.Abstraction/MLTasks/ITextToSpeech.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace BotSharp.Abstraction.MLTasks
-{
- public interface ITextToSpeech
- {
- ///
- /// The LLM provider like Microsoft Azure, OpenAI, ClaudAI
- ///
- string Provider { get; }
-
- ///
- /// Set model name, one provider can consume different model or version(s)
- ///
- /// deployment name
- void SetModelName(string model);
-
- Task GenerateSpeechFromTextAsync(string text, ITextToSpeechOptions? options = null);
- }
-
- public interface ITextToSpeechOptions
- {
-
- }
-}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs b/src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs
index cc7cfba07..008f528bc 100644
--- a/src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs
@@ -68,5 +68,6 @@ public enum LlmModelType
Text = 1,
Chat = 2,
Image = 3,
- Embedding = 4
+ Embedding = 4,
+ Audio = 5
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Models/KeyValue.cs b/src/Infrastructure/BotSharp.Abstraction/Models/KeyValue.cs
new file mode 100644
index 000000000..f981ba6e9
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Models/KeyValue.cs
@@ -0,0 +1,10 @@
+namespace BotSharp.Abstraction.Models;
+
+public class KeyValue
+{
+ [JsonPropertyName("key")]
+ public string Key { get; set; }
+
+ [JsonPropertyName("value")]
+ public string Value { get; set; }
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Models/MessageConfig.cs b/src/Infrastructure/BotSharp.Abstraction/Models/MessageConfig.cs
index 24e4152c4..9ea5917cc 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Models/MessageConfig.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Models/MessageConfig.cs
@@ -1,6 +1,6 @@
namespace BotSharp.Abstraction.Models;
-public class MessageConfig : InputMessageFiles
+public class MessageConfig
{
///
/// Completion Provider
@@ -15,7 +15,7 @@ public class MessageConfig : InputMessageFiles
public virtual string? Model { get; set; } = null;
///
- /// Model name
+ /// Model id
///
[JsonPropertyName("model_id")]
public virtual string? ModelId { get; set; } = null;
@@ -34,7 +34,7 @@ public class MessageConfig : InputMessageFiles
///
/// Conversation states from input
///
- public List States { get; set; } = new List();
+ public List States { get; set; } = new();
///
/// Agent task id
diff --git a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Extensions/VectorStorageExtension.cs b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Extensions/VectorStorageExtension.cs
new file mode 100644
index 000000000..ec519f046
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Extensions/VectorStorageExtension.cs
@@ -0,0 +1,29 @@
+using BotSharp.Abstraction.Knowledges.Enums;
+using BotSharp.Abstraction.VectorStorage.Models;
+
+namespace BotSharp.Abstraction.VectorStorage.Extensions;
+
+public static class VectorStorageExtension
+{
+ public static string ToQuestionAnswer(this VectorSearchResult data)
+ {
+ if (data?.Data == null) return string.Empty;
+
+ return $"Question: {data.Data[KnowledgePayloadName.Text]}\r\nAnswer: {data.Data[KnowledgePayloadName.Answer]}";
+ }
+
+ public static string ToPayloadPair(this VectorSearchResult data, IList payloads)
+ {
+ if (data?.Data == null || payloads.IsNullOrEmpty()) return string.Empty;
+
+ var results = data.Data.Where(x => payloads.Contains(x.Key))
+ .OrderBy(x => payloads.IndexOf(x.Key))
+ .Select(x =>
+ {
+ return $"{x.Key}: {x.Value}";
+ })
+ .ToList();
+
+ return string.Join("\r\n", results.Where(x => !string.IsNullOrWhiteSpace(x)));
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs
index 53b921a1a..cedacb46c 100644
--- a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs
@@ -4,12 +4,13 @@ namespace BotSharp.Abstraction.VectorStorage;
public interface IVectorDb
{
- string Name { get; }
+ string Provider { get; }
Task> GetCollections();
Task> GetPagedCollectionData(string collectionName, VectorFilter filter);
Task> GetCollectionData(string collectionName, IEnumerable ids, bool withPayload = false, bool withVector = false);
- Task CreateCollection(string collectionName, int dim);
+ Task CreateCollection(string collectionName, int dimension);
+ Task DeleteCollection(string collectionName);
Task Upsert(string collectionName, Guid id, float[] vector, string text, Dictionary? payload = null);
Task> Search(string collectionName, float[] vector, IEnumerable? fields, int limit = 5, float confidence = 0.5f, bool withVector = false);
Task DeleteCollectionData(string collectionName, Guid id);
diff --git a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorFilter.cs b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorFilter.cs
index 91abcc25f..85b9dec29 100644
--- a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorFilter.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorFilter.cs
@@ -4,4 +4,7 @@ public class VectorFilter : StringIdPagination
{
[JsonPropertyName("with_vector")]
public bool WithVector { get; set; }
-}
+
+ [JsonPropertyName("search_pairs")]
+ public IEnumerable? SearchPairs { get; set; }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorSearchResult.cs b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorSearchResult.cs
index ce39edbf9..f2c582ce1 100644
--- a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorSearchResult.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorSearchResult.cs
@@ -1,3 +1,5 @@
+using BotSharp.Abstraction.Knowledges.Enums;
+
namespace BotSharp.Abstraction.VectorStorage.Models;
public class VectorSearchResult : VectorCollectionData
diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj
index 05d485b74..ff10a5176 100644
--- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj
+++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj
@@ -1,4 +1,4 @@
-
+
$(TargetFramework)
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs
index f06e2ba2b..05653d9c6 100644
--- a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs
+++ b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs
@@ -6,6 +6,7 @@
using BotSharp.Abstraction.Settings;
using BotSharp.Abstraction.Templating;
using BotSharp.Core.Instructs;
+using BotSharp.Core.Knowledges.Services;
using BotSharp.Core.Messaging;
using BotSharp.Core.Routing.Planning;
using BotSharp.Core.Templating;
@@ -54,6 +55,8 @@ public void RegisterDI(IServiceCollection services, IConfiguration config)
services.AddScoped();
services.AddScoped();
services.AddScoped();
+
+ services.AddScoped();
}
public bool AttachMenu(List menu)
diff --git a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Audio.cs b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Audio.cs
new file mode 100644
index 000000000..e2aa844cd
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Audio.cs
@@ -0,0 +1,20 @@
+using System.IO;
+
+namespace BotSharp.Core.Files.Services;
+
+public partial class FileInstructService
+{
+ public async Task SpeechToText(string? provider, string? model, InstructFileModel audio, string? text = null)
+ {
+ var completion = CompletionProvider.GetAudioCompletion(_services, provider: provider ?? "openai", model: model ?? "whisper-1");
+ var audioBytes = await DownloadFile(audio);
+ using var stream = new MemoryStream();
+ stream.Write(audioBytes, 0, audioBytes.Length);
+ stream.Position = 0;
+
+ var fileName = $"{audio.FileName ?? "audio"}.{audio.FileExtension ?? "wav"}";
+ var content = await completion.GenerateTextFromAudioAsync(stream, fileName, text);
+ stream.Close();
+ return content;
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Image.cs b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Image.cs
index c9d35cb7e..cd65c16a9 100644
--- a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Image.cs
+++ b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Image.cs
@@ -4,7 +4,7 @@ namespace BotSharp.Core.Files.Services;
public partial class FileInstructService
{
- public async Task ReadImages(string? provider, string? model, string text, IEnumerable images)
+ public async Task ReadImages(string? provider, string? model, string text, IEnumerable images)
{
var completion = CompletionProvider.GetChatCompletion(_services, provider: provider ?? "openai", model: model ?? "gpt-4o", multiModal: true);
var message = await completion.GetChatCompletions(new Agent()
@@ -14,10 +14,10 @@ public async Task ReadImages(string? provider, string? model, s
{
new RoleDialogModel(AgentRole.User, text)
{
- Files = images?.ToList() ?? new List()
+ Files = images?.Select(x => new BotSharpFile { FileUrl = x.FileUrl, FileData = x.FileData }).ToList() ?? new List()
}
});
- return message;
+ return message.Content;
}
public async Task GenerateImage(string? provider, string? model, string text)
@@ -30,7 +30,7 @@ public async Task GenerateImage(string? provider, string? model
return message;
}
- public async Task VaryImage(string? provider, string? model, BotSharpFile image)
+ public async Task VaryImage(string? provider, string? model, InstructFileModel image)
{
if (string.IsNullOrWhiteSpace(image?.FileUrl) && string.IsNullOrWhiteSpace(image?.FileData))
{
@@ -43,16 +43,17 @@ public async Task VaryImage(string? provider, string? model, Bo
stream.Write(bytes, 0, bytes.Length);
stream.Position = 0;
+ var fileName = $"{image.FileName ?? "image"}.{image.FileExtension ?? "png"}";
var message = await completion.GetImageVariation(new Agent()
{
Id = Guid.Empty.ToString()
- }, new RoleDialogModel(AgentRole.User, string.Empty), stream, image.FileName ?? string.Empty);
+ }, new RoleDialogModel(AgentRole.User, string.Empty), stream, fileName);
stream.Close();
return message;
}
- public async Task EditImage(string? provider, string? model, string text, BotSharpFile image)
+ public async Task EditImage(string? provider, string? model, string text, InstructFileModel image)
{
if (string.IsNullOrWhiteSpace(image?.FileUrl) && string.IsNullOrWhiteSpace(image?.FileData))
{
@@ -65,16 +66,17 @@ public async Task EditImage(string? provider, string? model, st
stream.Write(bytes, 0, bytes.Length);
stream.Position = 0;
+ var fileName = $"{image.FileName ?? "image"}.{image.FileExtension ?? "png"}";
var message = await completion.GetImageEdits(new Agent()
{
Id = Guid.Empty.ToString()
- }, new RoleDialogModel(AgentRole.User, text), stream, image.FileName ?? string.Empty);
+ }, new RoleDialogModel(AgentRole.User, text), stream, fileName);
stream.Close();
return message;
}
- public async Task EditImage(string? provider, string? model, string text, BotSharpFile image, BotSharpFile mask)
+ public async Task EditImage(string? provider, string? model, string text, InstructFileModel image, InstructFileModel mask)
{
if ((string.IsNullOrWhiteSpace(image?.FileUrl) && string.IsNullOrWhiteSpace(image?.FileData)) ||
(string.IsNullOrWhiteSpace(mask?.FileUrl) && string.IsNullOrWhiteSpace(mask?.FileData)))
@@ -94,10 +96,12 @@ public async Task EditImage(string? provider, string? model, st
maskStream.Write(maskBytes, 0, maskBytes.Length);
maskStream.Position = 0;
+ var imageName = $"{image.FileName ?? "image"}.{image.FileExtension ?? "png"}";
+ var maskName = $"{mask.FileName ?? "mask"}.{mask.FileExtension ?? "png"}";
var message = await completion.GetImageEdits(new Agent()
{
Id = Guid.Empty.ToString()
- }, new RoleDialogModel(AgentRole.User, text), imageStream, image.FileName ?? string.Empty, maskStream, mask.FileName ?? string.Empty);
+ }, new RoleDialogModel(AgentRole.User, text), imageStream, imageName, maskStream, maskName);
imageStream.Close();
maskStream.Close();
@@ -105,7 +109,7 @@ public async Task EditImage(string? provider, string? model, st
}
#region Private methods
- private async Task DownloadFile(BotSharpFile file)
+ private async Task DownloadFile(InstructFileModel file)
{
var bytes = new byte[0];
if (!string.IsNullOrEmpty(file.FileUrl))
diff --git a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Pdf.cs b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Pdf.cs
index 2aec257b4..29dfdaf9a 100644
--- a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Pdf.cs
+++ b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Pdf.cs
@@ -4,7 +4,7 @@ namespace BotSharp.Core.Files.Services;
public partial class FileInstructService
{
- public async Task ReadPdf(string? provider, string? model, string? modelId, string prompt, List files)
+ public async Task ReadPdf(string? provider, string? model, string? modelId, string prompt, List files)
{
var content = string.Empty;
@@ -50,7 +50,7 @@ public async Task ReadPdf(string? provider, string? model, string? model
}
#region Private methods
- private async Task> DownloadFiles(string dir, List files, string extension = "pdf")
+ private async Task> DownloadFiles(string dir, List files, string extension = "pdf")
{
if (string.IsNullOrWhiteSpace(dir) || files.IsNullOrEmpty())
{
@@ -80,9 +80,9 @@ private async Task> DownloadFiles(string dir, List> ConvertPdfToImages(IEnumerable f
{
var images = new List();
var settings = _services.GetRequiredService();
- var converter = _services.GetServices().FirstOrDefault(x => x.Name == settings.Pdf2ImageConverter);
+ var converter = _services.GetServices().FirstOrDefault(x => x.Provider == settings.Pdf2ImageConverter.Provider);
if (converter == null || files.IsNullOrEmpty())
{
return images;
diff --git a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.cs b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.cs
index acd0ddaae..416d9f307 100644
--- a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.cs
+++ b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.cs
@@ -1,3 +1,4 @@
+
namespace BotSharp.Core.Files.Services;
public partial class FileInstructService : IFileInstructService
diff --git a/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.Audio.cs b/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.Audio.cs
index e90c7812d..1aaba5755 100644
--- a/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.Audio.cs
+++ b/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.Audio.cs
@@ -1,28 +1,38 @@
using System.IO;
-namespace BotSharp.Core.Files.Services
+namespace BotSharp.Core.Files.Services;
+
+public partial class LocalFileStorageService
{
- public partial class LocalFileStorageService
+ public bool SaveSpeechFile(string conversationId, string fileName, BinaryData data)
{
- public async Task SaveSpeechFileAsync(string conversationId, string fileName, BinaryData data)
+ try
{
var dir = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, TEXT_TO_SPEECH_FOLDER);
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
+
var filePath = Path.Combine(dir, fileName);
- if (File.Exists(filePath)) return;
- using var file = File.Create(filePath);
- using var input = data.ToStream();
- await input.CopyToAsync(file);
- }
+ if (File.Exists(filePath)) return false;
- public async Task RetrieveSpeechFileAsync(string conversationId, string fileName)
+ using var fs = File.Create(filePath);
+ using var ds = data.ToStream();
+ ds.CopyTo(fs);
+ return true;
+ }
+ catch (Exception ex)
{
- var path = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, TEXT_TO_SPEECH_FOLDER, fileName);
- using var file = new FileStream(path, FileMode.Open, FileAccess.Read);
- return await BinaryData.FromStreamAsync(file);
+ _logger.LogWarning($"Error when saving speech file. {fileName} ({conversationId})\r\n{ex.Message}\r\n{ex.InnerException}");
+ return false;
}
}
+
+ public BinaryData GetSpeechFile(string conversationId, string fileName)
+ {
+ var path = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, TEXT_TO_SPEECH_FOLDER, fileName);
+ using var file = new FileStream(path, FileMode.Open, FileAccess.Read);
+ return BinaryData.FromStream(file);
+ }
}
diff --git a/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.Common.cs b/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.Common.cs
index c49edbce6..b16c2c2cf 100644
--- a/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.Common.cs
+++ b/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.Common.cs
@@ -14,6 +14,22 @@ public string GetDirectory(string conversationId)
return dir;
}
+ public IEnumerable GetFiles(string relativePath, string? searchPattern = null)
+ {
+ if (string.IsNullOrWhiteSpace(relativePath))
+ {
+ return Enumerable.Empty();
+ }
+
+ var path = Path.Combine(_baseDir, relativePath);
+
+ if (!string.IsNullOrWhiteSpace(searchPattern))
+ {
+ return Directory.GetFiles(path, searchPattern);
+ }
+ return Directory.GetFiles(path);
+ }
+
public byte[] GetFileBytes(string fileStorageUrl)
{
using var stream = File.OpenRead(fileStorageUrl);
diff --git a/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.Conversation.cs b/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.Conversation.cs
index 8c2ed8310..5638179cf 100644
--- a/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.Conversation.cs
+++ b/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.Conversation.cs
@@ -6,7 +6,7 @@ namespace BotSharp.Core.Files.Services;
public partial class LocalFileStorageService
{
- public async Task> GetMessageFileScreenshots(string conversationId, IEnumerable messageIds)
+ public async Task> GetMessageFileScreenshotsAsync(string conversationId, IEnumerable messageIds)
{
var files = new List();
if (string.IsNullOrEmpty(conversationId) || messageIds.IsNullOrEmpty())
@@ -118,7 +118,7 @@ public IEnumerable GetMessagesWithFile(string conversationId,
return foundMsgs;
}
- public bool SaveMessageFiles(string conversationId, string messageId, string source, List files)
+ public bool SaveMessageFiles(string conversationId, string messageId, string source, List files)
{
if (files.IsNullOrEmpty()) return false;
@@ -276,7 +276,7 @@ private async Task> ConvertPdfToImages(string pdfLoc, string
private IPdf2ImageConverter? GetPdf2ImageConverter()
{
var settings = _services.GetRequiredService();
- var converter = _services.GetServices().FirstOrDefault(x => x.Name == settings.Pdf2ImageConverter);
+ var converter = _services.GetServices().FirstOrDefault(x => x.Provider == settings.Pdf2ImageConverter.Provider);
return converter;
}
diff --git a/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.User.cs b/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.User.cs
index 43ff9eed3..d1e962cd4 100644
--- a/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.User.cs
+++ b/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.User.cs
@@ -16,7 +16,7 @@ public string GetUserAvatar()
return found;
}
- public bool SaveUserAvatar(BotSharpFile file)
+ public bool SaveUserAvatar(InputFileModel file)
{
if (file == null || string.IsNullOrEmpty(file.FileData)) return false;
diff --git a/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.cs b/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.cs
index 94ee2beb9..750803c42 100644
--- a/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.cs
+++ b/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.cs
@@ -10,12 +10,6 @@ public partial class LocalFileStorageService : IFileStorageService
private readonly ILogger _logger;
private readonly string _baseDir;
- private readonly IEnumerable _audioTypes = new List
- {
- "mp3",
- "wav"
- };
-
private const string CONVERSATION_FOLDER = "conversations";
private const string FILE_FOLDER = "files";
private const string USER_FILE_FOLDER = "user";
diff --git a/src/Infrastructure/BotSharp.Core/Infrastructures/CompletionProvider.cs b/src/Infrastructure/BotSharp.Core/Infrastructures/CompletionProvider.cs
index 874a063e1..d6746e09a 100644
--- a/src/Infrastructure/BotSharp.Core/Infrastructures/CompletionProvider.cs
+++ b/src/Infrastructure/BotSharp.Core/Infrastructures/CompletionProvider.cs
@@ -28,6 +28,10 @@ public static object GetCompletion(IServiceProvider services,
{
return GetImageCompletion(services, provider: provider, model: model);
}
+ else if (settings.Type == LlmModelType.Audio)
+ {
+ return GetAudioCompletion(services, provider: provider, model: model);
+ }
else
{
return GetChatCompletion(services, provider: provider, model: model, agentConfig: agentConfig);
@@ -108,7 +112,7 @@ public static ITextEmbedding GetTextEmbedding(IServiceProvider services,
if (completer == null)
{
var logger = services.GetRequiredService>();
- logger.LogError($"Can't resolve completion provider by {provider}");
+ logger.LogError($"Can't resolve text-embedding provider by {provider}");
}
@@ -120,35 +124,19 @@ public static ITextEmbedding GetTextEmbedding(IServiceProvider services,
return completer;
}
- public static ITextToSpeech GetTextToSpeech(
+ public static IAudioCompletion GetAudioCompletion(
IServiceProvider services,
string provider,
string model)
{
- var completions = services.GetServices();
+ var completions = services.GetServices();
var completer = completions.FirstOrDefault(x => x.Provider == provider);
if (completer == null)
{
var logger = services.GetRequiredService>();
- logger.LogError($"Can't resolve text2speech provider by {provider}");
+ logger.LogError($"Can't resolve audio-completion provider by {provider}");
}
- completer.SetModelName(model);
- return completer;
- }
- public static ISpeechToText GetSpeechToText(
- IServiceProvider services,
- string provider,
- string model
- )
- {
- var completions = services.GetServices();
- var completer = completions.FirstOrDefault(x => x.Provider == provider);
- if (completer == null)
- {
- var logger = services.GetRequiredService>();
- logger.LogError($"Can't resolve speech2text provider by {provider}");
- }
completer.SetModelName(model);
return completer;
}
diff --git a/src/Infrastructure/BotSharp.Core/Knowledges/Helpers/KnowledgeSettingHelper.cs b/src/Infrastructure/BotSharp.Core/Knowledges/Helpers/KnowledgeSettingHelper.cs
new file mode 100644
index 000000000..a278d7bff
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/Knowledges/Helpers/KnowledgeSettingHelper.cs
@@ -0,0 +1,36 @@
+using BotSharp.Abstraction.Knowledges.Settings;
+using BotSharp.Abstraction.MLTasks;
+
+namespace BotSharp.Core.Knowledges.Helpers;
+
+public static class KnowledgeSettingHelper
+{
+ public static ITextEmbedding GetTextEmbeddingSetting(IServiceProvider services, string collectionName)
+ {
+ var settings = services.GetRequiredService();
+ var found = settings.Collections.FirstOrDefault(x => x.Name == collectionName)?.TextEmbedding;
+ if (found == null)
+ {
+ found = settings.Default.TextEmbedding;
+ }
+
+ var embedding = services.GetServices().FirstOrDefault(x => x.Provider == found.Provider);
+ var dimension = found.Dimension;
+
+ if (found.Dimension <= 0)
+ {
+ dimension = GetLlmTextEmbeddingDimension(services, found.Provider, found.Model);
+ }
+
+ embedding.SetModelName(found.Model);
+ embedding.SetDimension(dimension);
+ return embedding;
+ }
+
+ private static int GetLlmTextEmbeddingDimension(IServiceProvider services, string provider, string model)
+ {
+ var settings = services.GetRequiredService();
+ var found = settings.GetSetting(provider, model);
+ return found?.Dimension ?? 0;
+ }
+}
diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/TextChopperService.cs b/src/Infrastructure/BotSharp.Core/Knowledges/Helpers/TextChopper.cs
similarity index 78%
rename from src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/TextChopperService.cs
rename to src/Infrastructure/BotSharp.Core/Knowledges/Helpers/TextChopper.cs
index 88c77641a..68b585393 100644
--- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/TextChopperService.cs
+++ b/src/Infrastructure/BotSharp.Core/Knowledges/Helpers/TextChopper.cs
@@ -1,17 +1,18 @@
+using BotSharp.Abstraction.Knowledges.Models;
using System.Text.RegularExpressions;
-namespace BotSharp.Plugin.KnowledgeBase.Services;
+namespace BotSharp.Core.Knowledges.Helpers;
-public class TextChopperService : ITextChopper
+public static class TextChopper
{
- public List Chop(string content, ChunkOption option)
+ public static List Chop(string content, ChunkOption option)
{
content = Regex.Replace(content, @"\.{2,}", " ");
content = Regex.Replace(content, @"_{2,}", " ");
return option.SplitByWord ? ChopByWord(content, option) : ChopByChar(content, option);
}
- private List ChopByWord(string content, ChunkOption option)
+ private static List ChopByWord(string content, ChunkOption option)
{
var chunks = new List();
@@ -34,7 +35,7 @@ private List ChopByWord(string content, ChunkOption option)
return chunks;
}
- private List ChopByChar(string content, ChunkOption option)
+ private static List ChopByChar(string content, ChunkOption option)
{
var chunks = new List();
var currentPos = 0;
diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Create.cs b/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Create.cs
similarity index 65%
rename from src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Create.cs
rename to src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Create.cs
index 9b185867e..d8b82fb51 100644
--- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Create.cs
+++ b/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Create.cs
@@ -1,11 +1,15 @@
-namespace BotSharp.Plugin.KnowledgeBase.Services;
+using BotSharp.Abstraction.Knowledges.Models;
+using BotSharp.Abstraction.VectorStorage.Models;
+using BotSharp.Core.Knowledges.Helpers;
+
+namespace BotSharp.Core.Knowledges.Services;
public partial class KnowledgeService
{
public async Task FeedVectorKnowledge(string collectionName, KnowledgeCreationModel knowledge)
{
var index = 0;
- var lines = _textChopper.Chop(knowledge.Content, new ChunkOption
+ var lines = TextChopper.Chop(knowledge.Content, new ChunkOption
{
Size = 1024,
Conjunction = 32,
@@ -25,6 +29,25 @@ public async Task FeedVectorKnowledge(string collectionName, KnowledgeCreationMo
}
}
+ public async Task CreateVectorCollection(string collectionName, int dimension)
+ {
+ try
+ {
+ if (string.IsNullOrWhiteSpace(collectionName))
+ {
+ return false;
+ }
+
+ var db = GetVectorDb();
+ return await db.CreateCollection(collectionName, dimension);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning($"Error when creating a vector collection ({collectionName}). {ex.Message}\r\n{ex.InnerException}");
+ return false;
+ }
+ }
+
public async Task CreateVectorCollectionData(string collectionName, VectorCreateModel create)
{
try
diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Delete.cs b/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Delete.cs
similarity index 51%
rename from src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Delete.cs
rename to src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Delete.cs
index e78990a79..404b4e2ca 100644
--- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Delete.cs
+++ b/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Delete.cs
@@ -1,7 +1,26 @@
-namespace BotSharp.Plugin.KnowledgeBase.Services;
+namespace BotSharp.Core.Knowledges.Services;
public partial class KnowledgeService
{
+ public async Task DeleteVectorCollection(string collectionName)
+ {
+ try
+ {
+ if (string.IsNullOrWhiteSpace(collectionName))
+ {
+ return false;
+ }
+
+ var db = GetVectorDb();
+ return await db.DeleteCollection(collectionName);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning($"Error when deleting collection ({collectionName}). {ex.Message}\r\n{ex.InnerException}");
+ return false;
+ }
+ }
+
public async Task DeleteVectorCollectionData(string collectionName, string id)
{
try
diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs b/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Get.cs
similarity index 96%
rename from src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs
rename to src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Get.cs
index ee36780ec..69093659a 100644
--- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs
+++ b/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Get.cs
@@ -1,6 +1,8 @@
using BotSharp.Abstraction.Graph.Models;
+using BotSharp.Abstraction.Knowledges.Models;
+using BotSharp.Abstraction.VectorStorage.Models;
-namespace BotSharp.Plugin.KnowledgeBase.Services;
+namespace BotSharp.Core.Knowledges.Services;
public partial class KnowledgeService
{
diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Update.cs b/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Update.cs
similarity index 91%
rename from src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Update.cs
rename to src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Update.cs
index 0fa1f5cd8..b9652f617 100644
--- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Update.cs
+++ b/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Update.cs
@@ -1,4 +1,6 @@
-namespace BotSharp.Plugin.KnowledgeBase.Services;
+using BotSharp.Abstraction.VectorStorage.Models;
+
+namespace BotSharp.Core.Knowledges.Services;
public partial class KnowledgeService
{
diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.cs b/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.cs
similarity index 64%
rename from src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.cs
rename to src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.cs
index eec498910..2c0853d0a 100644
--- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.cs
+++ b/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.cs
@@ -1,38 +1,41 @@
-namespace BotSharp.Plugin.KnowledgeBase.Services;
+using BotSharp.Abstraction.Graph;
+using BotSharp.Abstraction.Knowledges.Settings;
+using BotSharp.Abstraction.MLTasks;
+using BotSharp.Abstraction.VectorStorage;
+using BotSharp.Core.Knowledges.Helpers;
+
+namespace BotSharp.Core.Knowledges.Services;
public partial class KnowledgeService : IKnowledgeService
{
private readonly IServiceProvider _services;
private readonly KnowledgeBaseSettings _settings;
- private readonly ITextChopper _textChopper;
private readonly ILogger _logger;
public KnowledgeService(
IServiceProvider services,
KnowledgeBaseSettings settings,
- ITextChopper textChopper,
ILogger logger)
{
_services = services;
_settings = settings;
- _textChopper = textChopper;
_logger = logger;
}
private IVectorDb GetVectorDb()
{
- var db = _services.GetServices().FirstOrDefault(x => x.Name == _settings.VectorDb);
+ var db = _services.GetServices().FirstOrDefault(x => x.Provider == _settings.VectorDb.Provider);
return db;
}
private IGraphDb GetGraphDb()
{
- var db = _services.GetServices().FirstOrDefault(x => x.Name == _settings.GraphDb);
+ var db = _services.GetServices().FirstOrDefault(x => x.Provider == _settings.GraphDb.Provider);
return db;
}
private ITextEmbedding GetTextEmbedding(string collection)
{
- return KnowledgeSettingUtility.GetTextEmbeddingSetting(_services, collection);
+ return KnowledgeSettingHelper.GetTextEmbeddingSetting(_services, collection);
}
}
diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetPlanner.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetPlanner.cs
index 72549e517..0fcaa5a16 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetPlanner.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetPlanner.cs
@@ -1,4 +1,3 @@
-using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Routing.Enums;
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Core.Routing.Planning;
diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/database_knowledge.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/database_knowledge.liquid
index 0332af76c..4c9f51c37 100644
--- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/database_knowledge.liquid
+++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/database_knowledge.liquid
@@ -1,8 +1,10 @@
-You are a knowledge generator assistant. Based on the provided mysql table structure, including tablename, fieldname,data type and comments, generate the related knowledge for DBA and BA. When user ask the question, they don't know the table name.
-the summarized question/answer should:
+You are a knowledge generator assistant. Based on the provided mysql table structure, including tablename, fieldname, data type and comments, generate the related knowledge for DBA and BA. When users ask the question, they don't know the table name.
+
+The summarized question/answer should:
1. help user to identify the location of tables to find further information
2. identify the table structure and data relationship based on the task description
-3. summarize all the table to table relationship information based on the FOREIGN KEY, and include both table in the answer
+3. summarize all the tables to table relationship information based on the FOREIGN KEY, and include both tables in the answer
+
Go through all the columns and generate multiple question & answer pairs.
The output should be question/answer pair list in JSON: [{"question":"","answer":""}]. And the new line should be replaced with \r\n.
diff --git a/src/Infrastructure/BotSharp.Logger/Hooks/VerboseLogHook.cs b/src/Infrastructure/BotSharp.Logger/Hooks/VerboseLogHook.cs
index cf6985e9e..afb8dd019 100644
--- a/src/Infrastructure/BotSharp.Logger/Hooks/VerboseLogHook.cs
+++ b/src/Infrastructure/BotSharp.Logger/Hooks/VerboseLogHook.cs
@@ -23,10 +23,13 @@ public async Task BeforeGenerating(Agent agent, List conversati
{
if (!_convSettings.ShowVerboseLog) return;
- var dialog = conversations.Last();
- var log = $"{dialog.Role}: {dialog.Content} [msg_id: {dialog.MessageId}] ==>";
- _logger.LogInformation(log);
-
+ var dialog = conversations.LastOrDefault();
+ if (dialog != null)
+ {
+ var log = $"{dialog.Role}: {dialog.Content} [msg_id: {dialog.MessageId}] ==>";
+ _logger.LogInformation(log);
+ }
+
await Task.CompletedTask;
}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj b/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj
index 240022258..9094107da 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj
+++ b/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj
@@ -1,4 +1,4 @@
-
+
$(TargetFramework)
@@ -19,6 +19,7 @@
+
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
index 9b964dffd..565677f7c 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
@@ -294,9 +294,7 @@ await conv.SendMessage(agentId, inputMsg,
}
[HttpPost("/conversation/{agentId}/{conversationId}/sse")]
- public async Task SendMessageSse([FromRoute] string agentId,
- [FromRoute] string conversationId,
- [FromBody] NewMessageModel input)
+ public async Task SendMessageSse([FromRoute] string agentId, [FromRoute] string conversationId, [FromBody] NewMessageModel input)
{
var conv = _services.GetRequiredService();
var inputMsg = new RoleDialogModel(AgentRole.User, input.Text)
@@ -391,7 +389,7 @@ public IActionResult UploadAttachments([FromRoute] string conversationId,
}
[HttpPost("/agent/{agentId}/conversation/{conversationId}/upload")]
- public async Task UploadConversationMessageFiles([FromRoute] string agentId, [FromRoute] string conversationId, [FromBody] NewMessageModel input)
+ public async Task UploadConversationMessageFiles([FromRoute] string agentId, [FromRoute] string conversationId, [FromBody] InputMessageFiles input)
{
var convService = _services.GetRequiredService();
convService.SetConversationId(conversationId, input.States);
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs
index 48fdafe19..bc02e8464 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs
@@ -1,4 +1,5 @@
using BotSharp.Abstraction.Agents.Models;
+using BotSharp.Abstraction.Files.Utilities;
using BotSharp.Abstraction.Instructs;
using BotSharp.Abstraction.Instructs.Models;
using BotSharp.Core.Infrastructures;
@@ -20,8 +21,7 @@ public InstructModeController(IServiceProvider services, ILogger InstructCompletion([FromRoute] string agentId,
- [FromBody] InstructMessageModel input)
+ public async Task InstructCompletion([FromRoute] string agentId, [FromBody] InstructMessageModel input)
{
var state = _services.GetRequiredService();
input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External));
@@ -79,7 +79,7 @@ public async Task ChatCompletion([FromBody] IncomingMessageModel input)
#region Read image
[HttpPost("/instruct/multi-modal")]
- public async Task MultiModalCompletion([FromBody] IncomingMessageModel input)
+ public async Task MultiModalCompletion([FromBody] MultiModalRequest input)
{
var state = _services.GetRequiredService();
input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External));
@@ -87,21 +87,50 @@ public async Task MultiModalCompletion([FromBody] IncomingMessageModel i
try
{
var fileInstruct = _services.GetRequiredService();
- var message = await fileInstruct.ReadImages(input.Provider, input.Model, input.Text, input.Files);
- return message.Content;
+ var content = await fileInstruct.ReadImages(input.Provider, input.Model, input.Text, input.Files);
+ return content;
}
catch (Exception ex)
{
- var error = $"Error in analyzing files. {ex.Message}";
+ var error = $"Error in reading images. {ex.Message}";
_logger.LogError(error);
return error;
}
}
+
+ [HttpPost("/instruct/multi-modal/upload")]
+ public async Task MultiModalCompletion(IFormFile file, [FromForm] string text, [FromForm] string? provider = null,
+ [FromForm] string? model = null, [FromForm] List? states = null)
+ {
+ var state = _services.GetRequiredService();
+ states?.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External));
+ var viewModel = new MultiModalViewModel();
+
+ try
+ {
+ var data = FileUtility.BuildFileDataFromFile(file);
+ var files = new List
+ {
+ new InstructFileModel { FileData = data }
+ };
+ var fileInstruct = _services.GetRequiredService();
+ var content = await fileInstruct.ReadImages(provider, model, text, files);
+ viewModel.Content = content;
+ return viewModel;
+ }
+ catch (Exception ex)
+ {
+ var error = $"Error in reading image upload. {ex.Message}";
+ _logger.LogError(error);
+ viewModel.Message = error;
+ return viewModel;
+ }
+ }
#endregion
#region Generate image
[HttpPost("/instruct/image-generation")]
- public async Task ImageGeneration([FromBody] IncomingMessageModel input)
+ public async Task ImageGeneration([FromBody] ImageGenerationRequest input)
{
var state = _services.GetRequiredService();
input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External));
@@ -127,7 +156,7 @@ public async Task ImageGeneration([FromBody] IncomingM
#region Edit image
[HttpPost("/instruct/image-variation")]
- public async Task ImageVariation([FromBody] IncomingMessageModel input)
+ public async Task ImageVariation([FromBody] ImageVariationRequest input)
{
var state = _services.GetRequiredService();
input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External));
@@ -135,16 +164,16 @@ public async Task ImageVariation([FromBody] IncomingMe
try
{
- var image = input.Files.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x.FileUrl) || !string.IsNullOrWhiteSpace(x.FileData));
- if (image == null)
+ if (input.File == null)
{
return new ImageGenerationViewModel { Message = "Error! Cannot find an image!" };
}
var fileInstruct = _services.GetRequiredService();
- var message = await fileInstruct.VaryImage(input.Provider, input.Model, image);
+ var message = await fileInstruct.VaryImage(input.Provider, input.Model, input.File);
imageViewModel.Content = message.Content;
imageViewModel.Images = message.GeneratedImages.Select(x => ImageViewModel.ToViewModel(x)).ToList();
+
return imageViewModel;
}
catch (Exception ex)
@@ -156,8 +185,43 @@ public async Task ImageVariation([FromBody] IncomingMe
}
}
+ [HttpPost("/instruct/image-variation/upload")]
+ public async Task ImageVariation(IFormFile file, [FromForm] string? provider = null,
+ [FromForm] string? model = null, [FromForm] List? states = null)
+ {
+ var state = _services.GetRequiredService();
+ states?.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External));
+ var imageViewModel = new ImageGenerationViewModel();
+
+ try
+ {
+ using var stream = new MemoryStream();
+ file.CopyTo(stream);
+ stream.Position = 0;
+
+ var completion = CompletionProvider.GetImageCompletion(_services, provider: provider ?? "openai", model: model ?? "dall-e-2");
+ var message = await completion.GetImageVariation(new Agent()
+ {
+ Id = Guid.Empty.ToString()
+ }, new RoleDialogModel(AgentRole.User, string.Empty), stream, file.FileName);
+
+ imageViewModel.Content = message.Content;
+ imageViewModel.Images = message.GeneratedImages.Select(x => ImageViewModel.ToViewModel(x)).ToList();
+ stream.Close();
+
+ return imageViewModel;
+ }
+ catch (Exception ex)
+ {
+ var error = $"Error in image variation upload. {ex.Message}";
+ _logger.LogError(error);
+ imageViewModel.Message = error;
+ return imageViewModel;
+ }
+ }
+
[HttpPost("/instruct/image-edit")]
- public async Task ImageEdit([FromBody] IncomingMessageModel input)
+ public async Task ImageEdit([FromBody] ImageEditRequest input)
{
var fileInstruct = _services.GetRequiredService();
var state = _services.GetRequiredService();
@@ -166,12 +230,11 @@ public async Task ImageEdit([FromBody] IncomingMessage
try
{
- var image = input.Files.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x.FileUrl) || !string.IsNullOrWhiteSpace(x.FileData));
- if (image == null)
+ if (input.File == null)
{
- return new ImageGenerationViewModel { Message = "Error! Cannot find an image!" };
+ return new ImageGenerationViewModel { Message = "Error! Cannot find a valid image file!" };
}
- var message = await fileInstruct.EditImage(input.Provider, input.Model, input.Text, image);
+ var message = await fileInstruct.EditImage(input.Provider, input.Model, input.Text, input.File);
imageViewModel.Content = message.Content;
imageViewModel.Images = message.GeneratedImages.Select(x => ImageViewModel.ToViewModel(x)).ToList();
return imageViewModel;
@@ -185,8 +248,44 @@ public async Task ImageEdit([FromBody] IncomingMessage
}
}
+ [HttpPost("/instruct/image-edit/upload")]
+ public async Task ImageEdit(IFormFile file, [FromForm] string text, [FromForm] string? provider = null,
+ [FromForm] string? model = null, [FromForm] List? states = null)
+ {
+ var fileInstruct = _services.GetRequiredService();
+ var state = _services.GetRequiredService();
+ states?.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External));
+ var imageViewModel = new ImageGenerationViewModel();
+
+ try
+ {
+ using var stream = new MemoryStream();
+ file.CopyTo(stream);
+ stream.Position = 0;
+
+ var completion = CompletionProvider.GetImageCompletion(_services, provider: provider ?? "openai", model: model ?? "dall-e-2");
+ var message = await completion.GetImageEdits(new Agent()
+ {
+ Id = Guid.Empty.ToString()
+ }, new RoleDialogModel(AgentRole.User, text), stream, file.FileName);
+
+ imageViewModel.Content = message.Content;
+ imageViewModel.Images = message.GeneratedImages.Select(x => ImageViewModel.ToViewModel(x)).ToList();
+ stream.Close();
+
+ return imageViewModel;
+ }
+ catch (Exception ex)
+ {
+ var error = $"Error in image edit upload. {ex.Message}";
+ _logger.LogError(error);
+ imageViewModel.Message = error;
+ return imageViewModel;
+ }
+ }
+
[HttpPost("/instruct/image-mask-edit")]
- public async Task ImageMaskEdit([FromBody] IncomingMessageModel input)
+ public async Task ImageMaskEdit([FromBody] ImageMaskEditRequest input)
{
var fileInstruct = _services.GetRequiredService();
var state = _services.GetRequiredService();
@@ -195,11 +294,11 @@ public async Task ImageMaskEdit([FromBody] IncomingMes
try
{
- var image = input.Files.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x.FileUrl) || !string.IsNullOrWhiteSpace(x.FileData));
+ var image = input.File;
var mask = input.Mask;
if (image == null || mask == null)
{
- return new ImageGenerationViewModel { Message = "Error! Cannot find an image or mask!" };
+ return new ImageGenerationViewModel { Message = "Error! Cannot find a valid image or mask!" };
}
var message = await fileInstruct.EditImage(input.Provider, input.Model, input.Text, image, mask);
imageViewModel.Content = message.Content;
@@ -214,11 +313,52 @@ public async Task ImageMaskEdit([FromBody] IncomingMes
return imageViewModel;
}
}
+
+ [HttpPost("/instruct/image-mask-edit/upload")]
+ public async Task ImageMaskEdit(IFormFile image, IFormFile mask, [FromForm] string text, [FromForm] string? provider = null,
+ [FromForm] string? model = null, [FromForm] List? states = null)
+ {
+ var fileInstruct = _services.GetRequiredService();
+ var state = _services.GetRequiredService();
+ states?.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External));
+ var imageViewModel = new ImageGenerationViewModel();
+
+ try
+ {
+ using var imageStream = new MemoryStream();
+ image.CopyTo(imageStream);
+ imageStream.Position = 0;
+
+ using var maskStream = new MemoryStream();
+ mask.CopyTo(maskStream);
+ maskStream.Position = 0;
+
+ var completion = CompletionProvider.GetImageCompletion(_services, provider: provider ?? "openai", model: model ?? "dall-e-2");
+ var message = await completion.GetImageEdits(new Agent()
+ {
+ Id = Guid.Empty.ToString()
+ }, new RoleDialogModel(AgentRole.User, text), imageStream, image.FileName, maskStream, mask.FileName);
+
+ imageViewModel.Content = message.Content;
+ imageViewModel.Images = message.GeneratedImages.Select(x => ImageViewModel.ToViewModel(x)).ToList();
+ imageStream.Close();
+ maskStream.Close();
+
+ return imageViewModel;
+ }
+ catch (Exception ex)
+ {
+ var error = $"Error in image mask edit upload. {ex.Message}";
+ _logger.LogError(error);
+ imageViewModel.Message = error;
+ return imageViewModel;
+ }
+ }
#endregion
#region Pdf
[HttpPost("/instruct/pdf-completion")]
- public async Task PdfCompletion([FromBody] IncomingMessageModel input)
+ public async Task PdfCompletion([FromBody] MultiModalRequest input)
{
var state = _services.GetRequiredService();
input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External));
@@ -239,5 +379,109 @@ public async Task PdfCompletion([FromBody] IncomingMessa
return viewModel;
}
}
+
+ [HttpPost("/instruct/pdf-completion/upload")]
+ public async Task PdfCompletion(IFormFile file, [FromForm] string text, [FromForm] string? provider = null,
+ [FromForm] string? model = null, [FromForm] string? modelId = null, [FromForm] List? states = null)
+ {
+ var state = _services.GetRequiredService();
+ states?.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External));
+ var viewModel = new PdfCompletionViewModel();
+
+ try
+ {
+ var data = FileUtility.BuildFileDataFromFile(file);
+ var files = new List
+ {
+ new InstructFileModel { FileData = data }
+ };
+
+ var fileInstruct = _services.GetRequiredService();
+ var content = await fileInstruct.ReadPdf(provider, model, modelId, text, files);
+ viewModel.Content = content;
+ return viewModel;
+ }
+ catch (Exception ex)
+ {
+ var error = $"Error in pdf completion upload. {ex.Message}";
+ _logger.LogError(error);
+ viewModel.Message = error;
+ return viewModel;
+ }
+ }
+ #endregion
+
+ #region Audio
+ [HttpPost("/instruct/speech-to-text")]
+ public async Task SpeechToText([FromBody] SpeechToTextRequest input)
+ {
+ var fileInstruct = _services.GetRequiredService();
+ var state = _services.GetRequiredService();
+ input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External));
+ var viewModel = new SpeechToTextViewModel();
+
+ try
+ {
+ var audio = input.File;
+ if (audio == null)
+ {
+ return new SpeechToTextViewModel { Message = "Error! Cannot find a valid audio file!" };
+ }
+ var content = await fileInstruct.SpeechToText(input.Provider, input.Model, audio);
+ viewModel.Content = content;
+ return viewModel;
+ }
+ catch (Exception ex)
+ {
+ var error = $"Error in speech to text. {ex.Message}";
+ _logger.LogError(error);
+ viewModel.Message = error;
+ return viewModel;
+ }
+ }
+
+ [HttpPost("/instruct/speech-to-text/upload")]
+ public async Task SpeechToText(IFormFile file, [FromForm] string? provider = null, [FromForm] string? model = null,
+ [FromForm] string? text = null, [FromForm] List? states = null)
+ {
+ var fileInstruct = _services.GetRequiredService();
+ var state = _services.GetRequiredService();
+ states?.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External));
+ var viewModel = new SpeechToTextViewModel();
+
+ try
+ {
+ using var stream = new MemoryStream();
+ file.CopyTo(stream);
+ stream.Position = 0;
+
+ var completion = CompletionProvider.GetAudioCompletion(_services, provider: provider ?? "openai", model: model ?? "whisper-1");
+ var content = await completion.GenerateTextFromAudioAsync(stream, file.FileName, text);
+ viewModel.Content = content;
+ stream.Close();
+ return viewModel;
+ }
+ catch (Exception ex)
+ {
+ var error = $"Error in speech-to-text upload. {ex.Message}";
+ _logger.LogError(error);
+ viewModel.Message = error;
+ return viewModel;
+ }
+ }
+
+ [HttpPost("/instruct/text-to-speech")]
+ public async Task TextToSpeech([FromBody] TextToSpeechRequest input)
+ {
+ var state = _services.GetRequiredService();
+ input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External));
+
+ var completion = CompletionProvider.GetAudioCompletion(_services, provider: input.Provider ?? "openai", model: input.Model ?? "tts-1");
+ var binaryData = await completion.GenerateAudioFromTextAsync(input.Text);
+ var stream = binaryData.ToStream();
+ stream.Position = 0;
+
+ return new FileStreamResult(stream, "audio/mpeg") { FileDownloadName = "output.mp3" };
+ }
#endregion
}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs
index deb21378a..827c2d7ce 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs
@@ -18,12 +18,25 @@ public KnowledgeBaseController(IKnowledgeService knowledgeService, IServiceProvi
_services = services;
}
+ #region Vector
[HttpGet("knowledge/vector/collections")]
public async Task> GetVectorCollections()
{
return await _knowledgeService.GetVectorCollections();
}
+ [HttpPost("knowledge/vector/{collection}/create-collection/{dimension}")]
+ public async Task CreateVectorCollection([FromRoute] string collection, [FromRoute] int dimension)
+ {
+ return await _knowledgeService.CreateVectorCollection(collection, dimension);
+ }
+
+ [HttpDelete("knowledge/vector/{collection}/delete-collection")]
+ public async Task GetVectorCollections([FromRoute] string collection)
+ {
+ return await _knowledgeService.DeleteVectorCollection(collection);
+ }
+
[HttpPost("/knowledge/vector/{collection}/search")]
public async Task> SearchVectorKnowledge([FromRoute] string collection, [FromBody] SearchVectorKnowledgeRequest request)
{
@@ -91,7 +104,7 @@ public async Task DeleteVectorCollectionData([FromRoute] string collection
public async Task UploadVectorKnowledge([FromRoute] string collection, IFormFile file, [FromForm] int? startPageNum, [FromForm] int? endPageNum)
{
var setttings = _services.GetRequiredService();
- var textConverter = _services.GetServices