Merge pull request #633 from iceljc/features/add-knowledge-docs

Features/add knowledge docs
This commit is contained in:
iceljc 2024-09-11 16:22:39 -05:00 committed by GitHub
commit 057190a245
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
64 changed files with 1461 additions and 430 deletions

View file

@ -37,7 +37,7 @@ public interface IFileStorageService
IEnumerable<MessageFileModel> GetMessageFiles(string conversationId, IEnumerable<string> messageIds, string source, IEnumerable<string>? contentTypes = null);
string GetMessageFile(string conversationId, string messageId, string source, string index, string fileName);
IEnumerable<MessageFileModel> GetMessagesWithFile(string conversationId, IEnumerable<string> messageIds);
bool SaveMessageFiles(string conversationId, string messageId, string source, List<InputFileModel> files);
bool SaveMessageFiles(string conversationId, string messageId, string source, List<FileDataModel> files);
/// <summary>
/// Delete files under messages
@ -54,11 +54,31 @@ public interface IFileStorageService
#region User
string GetUserAvatar();
bool SaveUserAvatar(InputFileModel file);
bool SaveUserAvatar(FileDataModel file);
#endregion
#region Speech
bool SaveSpeechFile(string conversationId, string fileName, BinaryData data);
BinaryData GetSpeechFile(string conversationId, string fileName);
#endregion
#region Knowledge
bool SaveKnowledgeBaseFile(string collectionName, string vectorStoreProvider, string fileId, string fileName, Stream stream);
/// <summary>
/// Delete files in a knowledge collection. If fileId is null, remove all files in the collection.
/// </summary>
/// <param name="collectionName"></param>
/// <param name="fileId"></param>
/// <returns></returns>
bool DeleteKnowledgeFile(string collectionName, string vectorStoreProvider, string? fileId = null);
bool SaveKnolwedgeBaseFileMeta(string collectionName, string vectorStoreProvider,string fileId, KnowledgeDocMetaData metaData);
KnowledgeDocMetaData? GetKnowledgeBaseFileMeta(string collectionName, string vectorStoreProvider, string fileId);
IEnumerable<KnowledgeFileModel> GetKnowledgeBaseFiles(string collectionName, string vectorStoreProvider);
FileBinaryDataModel? GetKnowledgeBaseFileBinaryData(string collectionName, string vectorStoreProvider, string fileId);
#endregion
}

View file

@ -0,0 +1,7 @@
namespace BotSharp.Abstraction.Files.Models;
public class ExternalFileModel : FileDataModel
{
[JsonPropertyName("file_url")]
public string FileUrl { get; set; } = string.Empty;
}

View file

@ -0,0 +1,7 @@
namespace BotSharp.Abstraction.Files.Models;
public class FileBinaryDataModel
{
public string FileName { get; set; }
public BinaryData FileBinaryData { get; set; }
}

View file

@ -1,6 +1,6 @@
namespace BotSharp.Abstraction.Files.Models;
public class InputFileModel : FileBase
public class FileDataModel : FileBase
{
/// <summary>
/// File name with extension

View file

@ -0,0 +1,10 @@
namespace BotSharp.Abstraction.Files.Models;
public class KnowledgeFileModel
{
public string FileId { get; set; }
public string FileName { get; set; }
public string FileExtension { get; set; }
public string ContentType { get; set; }
public string FileUrl { get; set; }
}

View file

@ -0,0 +1,7 @@
namespace BotSharp.Abstraction.Knowledges.Enums;
public static class KnowledgeCollectionType
{
public static string QuestionAnswer = "question-answer";
public static string Document = "document";
}

View file

@ -6,11 +6,10 @@ namespace BotSharp.Abstraction.Knowledges;
public interface IKnowledgeService
{
#region Vector
Task<bool> CreateVectorCollection(string collectionName, int dimension);
Task<bool> CreateVectorCollection(string collectionName, string collectionType, int dimension, string provider, string model);
Task<bool> DeleteVectorCollection(string collectionName);
Task<IEnumerable<string>> GetVectorCollections();
Task<IEnumerable<string>> GetVectorCollections(string type);
Task<IEnumerable<VectorSearchResult>> SearchVectorKnowledge(string query, string collectionName, VectorSearchOptions options);
Task FeedVectorKnowledge(string collectionName, KnowledgeCreationModel model);
Task<StringIdPagedItems<VectorSearchResult>> GetPagedVectorCollectionData(string collectionName, VectorFilter filter);
Task<bool> DeleteVectorCollectionData(string collectionName, string id);
Task<bool> CreateVectorCollectionData(string collectionName, VectorCreateModel create);
@ -19,6 +18,16 @@ public interface IKnowledgeService
#region Graph
Task<GraphSearchResult> SearchGraphKnowledge(string query, GraphSearchOptions options);
Task<KnowledgeSearchResult> SearchKnowledge(string query, string collectionName, VectorSearchOptions vectorOptions, GraphSearchOptions graphOptions);
#endregion
#region Document
Task<UploadKnowledgeResponse> UploadKnowledgeDocuments(string collectionName, IEnumerable<ExternalFileModel> files);
Task<bool> DeleteKnowledgeDocument(string collectionName, string fileId);
Task<IEnumerable<KnowledgeFileModel>> GetKnowledgeDocuments(string collectionName);
Task<FileBinaryDataModel?> GetKnowledgeDocumentBinaryData(string collectionName, string fileId);
#endregion
#region Common
Task<bool> RefreshVectorKnowledgeConfigs(VectorCollectionConfigsModel configs);
#endregion
}

View file

@ -0,0 +1,27 @@
using BotSharp.Abstraction.VectorStorage.Models;
namespace BotSharp.Abstraction.Knowledges.Models;
public class KnowledgeDocMetaData
{
[JsonPropertyName("collection")]
public string Collection { get; set; }
[JsonPropertyName("file_id")]
public string FileId { get; set; }
[JsonPropertyName("file_name")]
public string FileName { get; set; }
[JsonPropertyName("content_type")]
public string ContentType { get; set; }
[JsonPropertyName("vector_data_ids")]
public IEnumerable<string> VectorDataIds { get; set; } = new List<string>();
[JsonPropertyName("create_date")]
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
[JsonPropertyName("create_user_id")]
public string CreateUserId { get; set; }
}

View file

@ -1,10 +0,0 @@
using BotSharp.Abstraction.Graph.Models;
using BotSharp.Abstraction.VectorStorage.Models;
namespace BotSharp.Abstraction.Knowledges.Models;
public class KnowledgeSearchResult
{
public IEnumerable<VectorSearchResult> VectorResult { get; set; }
public GraphSearchResult GraphResult { get; set; }
}

View file

@ -0,0 +1,18 @@
namespace BotSharp.Abstraction.Knowledges.Models;
public class UploadKnowledgeResponse
{
[JsonPropertyName("success")]
public IEnumerable<string> Success { get; set; } = new List<string>();
[JsonPropertyName("failed")]
public IEnumerable<string> Failed { get; set; } = new List<string>();
[JsonPropertyName("is_success")]
public bool IsSuccess {
get
{
return !Success.IsNullOrEmpty() && Failed.IsNullOrEmpty();
}
}
}

View file

@ -8,7 +8,6 @@ public class KnowledgeBaseSettings
public SettingBase GraphDb { get; set; }
public DefaultKnowledgeBaseSetting Default { get; set; }
public List<VectorCollectionSetting> Collections { get; set; } = new();
}
public class DefaultKnowledgeBaseSetting
@ -17,12 +16,6 @@ public class DefaultKnowledgeBaseSetting
public KnowledgeTextEmbeddingSetting TextEmbedding { get; set; }
}
public class VectorCollectionSetting
{
public string Name { get; set; }
public KnowledgeTextEmbeddingSetting TextEmbedding { get; set; }
}
public class KnowledgeTextEmbeddingSetting : SettingBase
{
public string Model { get; set; }

View file

@ -4,6 +4,7 @@ using BotSharp.Abstraction.Repositories.Filters;
using BotSharp.Abstraction.Tasks.Models;
using BotSharp.Abstraction.Translation.Models;
using BotSharp.Abstraction.Users.Models;
using BotSharp.Abstraction.VectorStorage.Models;
namespace BotSharp.Abstraction.Repositories;
@ -99,4 +100,16 @@ public interface IBotSharpRepository
bool SaveTranslationMemories(IEnumerable<TranslationMemoryInput> inputs);
#endregion
#region KnowledgeBase
/// <summary>
/// Save knowledge collection configs. If reset is true, it will remove everything and then save the new configs.
/// </summary>
/// <param name="configs"></param>
/// <param name="reset"></param>
/// <returns></returns>
bool AddKnowledgeCollectionConfigs(List<VectorCollectionConfig> configs, bool reset = false);
bool DeleteKnowledgeCollectionConfig(string collectionName);
IEnumerable<VectorCollectionConfig> GetKnowledgeCollectionConfigs(VectorCollectionConfigFilter filter);
#endregion
}

View file

@ -44,6 +44,13 @@ public static class StringExtensions
return str1.Equals(str2, option);
}
public static string CleanStr(this string? str)
{
if (string.IsNullOrWhiteSpace(str)) return string.Empty;
return str.Replace(" ", "").Replace("\t", "").Replace("\n", "").Replace("\r", "");
}
public static string JsonContent(this string text)
{
var m = Regex.Match(text, @"\{(?:[^{}]|(?<open>\{)|(?<-open>\}))+(?(open)(?!))\}");

View file

@ -13,5 +13,5 @@ public interface IVectorDb
Task<bool> DeleteCollection(string collectionName);
Task<bool> Upsert(string collectionName, Guid id, float[] vector, string text, Dictionary<string, string>? payload = null);
Task<IEnumerable<VectorCollectionData>> Search(string collectionName, float[] vector, IEnumerable<string>? fields, int limit = 5, float confidence = 0.5f, bool withVector = false);
Task<bool> DeleteCollectionData(string collectionName, Guid id);
Task<bool> DeleteCollectionData(string collectionName, List<Guid> ids);
}

View file

@ -0,0 +1,13 @@
namespace BotSharp.Abstraction.VectorStorage.Models;
public class VectorCollectionConfigFilter
{
public IEnumerable<string>? CollectionNames { get; set; }
public IEnumerable<string>? CollectionTypes { get; set; }
public IEnumerable<string>? VectorStroageProviders { get; set; }
public static VectorCollectionConfigFilter Empty()
{
return new VectorCollectionConfigFilter();
}
}

View file

@ -0,0 +1,46 @@
namespace BotSharp.Abstraction.VectorStorage.Models;
public class VectorCollectionConfigsModel
{
[JsonPropertyName("collections")]
public List<VectorCollectionConfig> Collections { get; set; } = new();
}
public class VectorCollectionConfig
{
/// <summary>
/// Must be unique
/// </summary>
[JsonPropertyName("name")]
public string Name { get; set; }
/// <summary>
/// Collection type, e.g., question-answer, document
/// </summary>
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("vector_storage")]
public VectorStorageConfig VectorStorage { get; set; }
[JsonPropertyName("text_embedding")]
public KnowledgeEmbeddingConfig TextEmbedding { get; set; }
}
public class KnowledgeEmbeddingConfig
{
[JsonPropertyName("provider")]
public string Provider { get; set; }
[JsonPropertyName("model")]
public string Model { get; set; }
[JsonPropertyName("dimension")]
public int Dimension { get; set; }
}
public class VectorStorageConfig
{
[JsonPropertyName("provider")]
public string Provider { get; set; }
}

View file

@ -5,6 +5,9 @@ public class VectorFilter : StringIdPagination
[JsonPropertyName("with_vector")]
public bool WithVector { get; set; }
/// <summary>
/// For keyword search
/// </summary>
[JsonPropertyName("search_pairs")]
public IEnumerable<KeyValue>? SearchPairs { get; set; }
}

View file

@ -118,7 +118,7 @@ public partial class LocalFileStorageService
return foundMsgs;
}
public bool SaveMessageFiles(string conversationId, string messageId, string source, List<InputFileModel> files)
public bool SaveMessageFiles(string conversationId, string messageId, string source, List<FileDataModel> files)
{
if (files.IsNullOrEmpty()) return false;

View file

@ -0,0 +1,191 @@
using BotSharp.Abstraction.Knowledges.Models;
using System.IO;
namespace BotSharp.Core.Files.Services;
public partial class LocalFileStorageService
{
public bool SaveKnowledgeBaseFile(string collectionName, string vectorStoreProvider, string fileId, string fileName, Stream stream)
{
if (string.IsNullOrWhiteSpace(collectionName)
|| string.IsNullOrWhiteSpace(vectorStoreProvider)
|| string.IsNullOrWhiteSpace(fileId))
{
return false;
}
try
{
var docDir = BuildKnowledgeCollectionDocumentDir(collectionName, vectorStoreProvider);
var dir = Path.Combine(docDir, fileId);
if (ExistDirectory(dir))
{
Directory.Delete(dir);
}
Directory.CreateDirectory(dir);
var filePath = Path.Combine(dir, fileName);
using var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write);
stream.CopyTo(fs);
fs.Flush();
fs.Close();
return true;
}
catch (Exception ex)
{
_logger.LogWarning($"Error when saving knowledge file " +
$"(Vector store provider: {vectorStoreProvider}, Collection: {collectionName}, File name: {fileName})." +
$"\r\n{ex.Message}\r\n{ex.InnerException}");
return false;
}
}
public bool DeleteKnowledgeFile(string collectionName, string vectorStoreProvider, string? fileId = null)
{
if (string.IsNullOrWhiteSpace(collectionName)
|| string.IsNullOrWhiteSpace(vectorStoreProvider))
{
return false;
}
var dir = BuildKnowledgeCollectionDocumentDir(collectionName, vectorStoreProvider);
if (!ExistDirectory(dir)) return false;
if (string.IsNullOrEmpty(fileId))
{
Directory.Delete(dir, true);
}
else
{
var fileDir = Path.Combine(dir, fileId);
if (ExistDirectory(fileDir))
{
Directory.Delete(fileDir, true);
}
}
return true;
}
public bool SaveKnolwedgeBaseFileMeta(string collectionName, string vectorStoreProvider, string fileId, KnowledgeDocMetaData metaData)
{
if (string.IsNullOrWhiteSpace(collectionName)
|| string.IsNullOrWhiteSpace(vectorStoreProvider)
|| string.IsNullOrWhiteSpace(fileId))
{
return false;
}
var docDir = BuildKnowledgeCollectionDocumentDir(collectionName, vectorStoreProvider);
var dir = Path.Combine(docDir, fileId);
if (!ExistDirectory(dir))
{
Directory.CreateDirectory(dir);
}
var metaFile = Path.Combine(dir, KNOWLEDGE_DOC_META_FILE);
var content = JsonSerializer.Serialize(metaData, _jsonOptions);
File.WriteAllText(metaFile, content);
return true;
}
public KnowledgeDocMetaData? GetKnowledgeBaseFileMeta(string collectionName, string vectorStoreProvider, string fileId)
{
if (string.IsNullOrWhiteSpace(collectionName)
|| string.IsNullOrWhiteSpace(vectorStoreProvider)
|| string.IsNullOrWhiteSpace(fileId))
{
return null;
}
var docDir = BuildKnowledgeCollectionDocumentDir(collectionName, vectorStoreProvider);
var metaFile = Path.Combine(docDir, fileId, KNOWLEDGE_DOC_META_FILE);
if (!File.Exists(metaFile))
{
return null;
}
var content = File.ReadAllText(metaFile);
var metaData = JsonSerializer.Deserialize<KnowledgeDocMetaData>(content, _jsonOptions);
return metaData;
}
public IEnumerable<KnowledgeFileModel> GetKnowledgeBaseFiles(string collectionName, string vectorStoreProvider)
{
if (string.IsNullOrWhiteSpace(collectionName)
|| string.IsNullOrWhiteSpace(vectorStoreProvider))
{
return Enumerable.Empty<KnowledgeFileModel>();
}
var docDir = BuildKnowledgeCollectionDocumentDir(collectionName, vectorStoreProvider);
if (!ExistDirectory(docDir))
{
return Enumerable.Empty<KnowledgeFileModel>();
}
var files = new List<KnowledgeFileModel>();
foreach (var folder in Directory.GetDirectories(docDir))
{
var metaFile = Path.Combine(folder, KNOWLEDGE_DOC_META_FILE);
if (!File.Exists(metaFile)) continue;
var content = File.ReadAllText(metaFile);
var metaData = JsonSerializer.Deserialize<KnowledgeDocMetaData>(content, _jsonOptions);
if (metaData == null) continue;
var fileName = Path.GetFileNameWithoutExtension(metaData.FileName);
var fileExtension = Path.GetExtension(metaData.FileName);
files.Add(new KnowledgeFileModel
{
FileId = metaData.FileId,
FileName = metaData.FileName,
FileExtension = fileExtension.Substring(1),
ContentType = FileUtility.GetFileContentType(metaData.FileName),
FileUrl = BuildKnowledgeFileUrl(collectionName, metaData.FileId)
});
}
return files;
}
public FileBinaryDataModel? GetKnowledgeBaseFileBinaryData(string collectionName, string vectorStoreProvider, string fileId)
{
if (string.IsNullOrWhiteSpace(collectionName)
|| string.IsNullOrWhiteSpace(vectorStoreProvider)
|| string.IsNullOrWhiteSpace(fileId))
{
return null;
}
var docDir = BuildKnowledgeCollectionDocumentDir(collectionName, vectorStoreProvider);
var fileDir = Path.Combine(docDir, fileId);
if (!ExistDirectory(fileDir)) return null;
var metaFile = Path.Combine(fileDir, KNOWLEDGE_DOC_META_FILE);
var content = File.ReadAllText(metaFile);
var metaData = JsonSerializer.Deserialize<KnowledgeDocMetaData>(content, _jsonOptions);
using var stream = new FileStream(fileDir, FileMode.Open, FileAccess.Read);
stream.Position = 0;
return new FileBinaryDataModel
{
FileName = metaData.FileName,
FileBinaryData = BinaryData.FromStream(stream)
};
}
#region Private methods
private string BuildKnowledgeCollectionDocumentDir(string collectionName, string vectorStoreProvider)
{
return Path.Combine(_baseDir, KNOWLEDGE_FOLDER, KNOWLEDGE_DOC_FOLDER, vectorStoreProvider, collectionName);
}
private string BuildKnowledgeFileUrl(string collectionName, string fileId)
{
return $"/knowledge/file/{collectionName}/file/{fileId}";
}
#endregion
}

View file

@ -16,7 +16,7 @@ public partial class LocalFileStorageService
return found;
}
public bool SaveUserAvatar(InputFileModel file)
public bool SaveUserAvatar(FileDataModel file)
{
if (file == null || string.IsNullOrEmpty(file.FileData)) return false;

View file

@ -19,6 +19,17 @@ public partial class LocalFileStorageService : IFileStorageService
private const string USER_AVATAR_FOLDER = "avatar";
private const string SESSION_FOLDER = "sessions";
private const string TEXT_TO_SPEECH_FOLDER = "speeches";
private const string KNOWLEDGE_FOLDER = "knowledgebase";
private const string KNOWLEDGE_DOC_FOLDER = "document";
private const string KNOWLEDGE_DOC_META_FILE = "meta.json";
private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true,
AllowTrailingCommas = true
};
public LocalFileStorageService(
BotSharpDatabaseSettings dbSettings,

View file

@ -3,6 +3,7 @@ using BotSharp.Abstraction.Plugins.Models;
using BotSharp.Abstraction.Tasks.Models;
using BotSharp.Abstraction.Translation.Models;
using BotSharp.Abstraction.Users.Models;
using BotSharp.Abstraction.VectorStorage.Models;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace BotSharp.Core.Repository;
@ -232,4 +233,15 @@ public class BotSharpDbContext : Database, IBotSharpRepository
public bool SaveTranslationMemories(IEnumerable<TranslationMemoryInput> inputs) =>
throw new NotImplementedException();
#endregion
#region Knowledge
public bool AddKnowledgeCollectionConfigs(List<VectorCollectionConfig> configs, bool reset = false) =>
throw new NotImplementedException();
public bool DeleteKnowledgeCollectionConfig(string collectionName) =>
throw new NotImplementedException();
public IEnumerable<VectorCollectionConfig> GetKnowledgeCollectionConfigs(VectorCollectionConfigFilter filter) =>
throw new NotImplementedException();
#endregion
}

View file

@ -0,0 +1,112 @@
using BotSharp.Abstraction.VectorStorage.Models;
using System.IO;
namespace BotSharp.Core.Repository;
public partial class FileRepository
{
public bool AddKnowledgeCollectionConfigs(List<VectorCollectionConfig> configs, bool reset = false)
{
var vectorDir = BuildKnowledgeCollectionConfigDir();
if (!Directory.Exists(vectorDir))
{
Directory.CreateDirectory(vectorDir);
}
var configFile = Path.Combine(vectorDir, COLLECTION_CONFIG_FILE);
if (reset)
{
File.WriteAllText(configFile, JsonSerializer.Serialize(configs ?? new(), _options));
return true;
}
if (!File.Exists(configFile))
{
File.Create(configFile);
}
var str = File.ReadAllText(configFile);
var savedConfigs = JsonSerializer.Deserialize<List<VectorCollectionConfig>>(str, _options) ?? new();
// Update if collection already exists, otherwise insert
foreach (var config in configs)
{
if (string.IsNullOrWhiteSpace(config.Name)) continue;
var found = savedConfigs.FirstOrDefault(x => x.Name == config.Name);
if (found != null)
{
found.TextEmbedding = config.TextEmbedding;
found.Type = config.Type;
}
else
{
savedConfigs.Add(config);
}
}
File.WriteAllText(configFile, JsonSerializer.Serialize(savedConfigs ?? new(), _options));
return true;
}
public bool DeleteKnowledgeCollectionConfig(string collectionName)
{
if (string.IsNullOrWhiteSpace(collectionName)) return false;
var vectorDir = BuildKnowledgeCollectionConfigDir();
var configFile = Path.Combine(vectorDir, COLLECTION_CONFIG_FILE);
if (!File.Exists(configFile)) return false;
var str = File.ReadAllText(configFile);
var savedConfigs = JsonSerializer.Deserialize<List<VectorCollectionConfig>>(str, _options) ?? new();
savedConfigs = savedConfigs.Where(x => x.Name != collectionName).ToList();
File.WriteAllText(configFile, JsonSerializer.Serialize(savedConfigs ?? new(), _options));
return true;
}
public IEnumerable<VectorCollectionConfig> GetKnowledgeCollectionConfigs(VectorCollectionConfigFilter filter)
{
if (filter == null)
{
return Enumerable.Empty<VectorCollectionConfig>();
}
var vectorDir = BuildKnowledgeCollectionConfigDir();
var configFile = Path.Combine(vectorDir, COLLECTION_CONFIG_FILE);
if (!File.Exists(configFile))
{
return Enumerable.Empty<VectorCollectionConfig>();
}
// Get data
var content = File.ReadAllText(configFile);
var configs = JsonSerializer.Deserialize<List<VectorCollectionConfig>>(content, _options) ?? new();
// Apply filters
if (!filter.CollectionNames.IsNullOrEmpty())
{
configs = configs.Where(x => filter.CollectionNames.Contains(x.Name)).ToList();
}
if (!filter.CollectionTypes.IsNullOrEmpty())
{
configs = configs.Where(x => filter.CollectionTypes.Contains(x.Type)).ToList();
}
if (!filter.VectorStroageProviders.IsNullOrEmpty())
{
configs = configs.Where(x => filter.VectorStroageProviders.Contains(x.VectorStorage?.Provider)).ToList();
}
return configs;
}
#region Private methods
private string BuildKnowledgeCollectionConfigDir()
{
return Path.Combine(_dbSettings.FileRepository, KNOWLEDGE_FOLDER, VECTOR_FOLDER);
}
#endregion
}

View file

@ -40,6 +40,9 @@ public partial class FileRepository : IBotSharpRepository
private const string AGENT_RESPONSES_FOLDER = "responses";
private const string AGENT_TASKS_FOLDER = "tasks";
private const string USERS_FOLDER = "users";
private const string KNOWLEDGE_FOLDER = "knowledgebase";
private const string VECTOR_FOLDER = "vector";
private const string COLLECTION_CONFIG_FILE = "collection-config.json";
public FileRepository(
IServiceProvider services,

View file

@ -62,7 +62,11 @@ namespace BotSharp.OpenAPI.BackgroundServices
if (cleanSetting == null || !cleanSetting.Enable) return;
var conversationService = scope.ServiceProvider.GetRequiredService<IConversationService>();
var conversationIds = await conversationService.GetIdleConversations(cleanSetting.BatchSize, cleanSetting.MessageLimit, cleanSetting.BufferHours, cleanSetting.ExcludeAgentIds);
var batchSize = cleanSetting.BatchSize;
var limit = cleanSetting.MessageLimit;
var bufferHours = cleanSetting.BufferHours;
var excludeAgentIds = cleanSetting.ExcludeAgentIds ?? new List<string>();
var conversationIds = await conversationService.GetIdleConversations(batchSize, limit, bufferHours, excludeAgentIds);
if (!conversationIds.IsNullOrEmpty())
{

View file

@ -366,8 +366,7 @@ public class ConversationController : ControllerBase
#region Files and attachments
[HttpPost("/conversation/{conversationId}/attachments")]
public IActionResult UploadAttachments([FromRoute] string conversationId,
IFormFile[] files)
public IActionResult UploadAttachments([FromRoute] string conversationId, IFormFile[] files)
{
if (files != null && files.Length > 0)
{

View file

@ -1,3 +1,4 @@
using BotSharp.Abstraction.Files.Constants;
using BotSharp.Abstraction.Graph.Models;
using BotSharp.Abstraction.Knowledges.Models;
using BotSharp.Abstraction.VectorStorage.Models;
@ -20,19 +21,19 @@ public class KnowledgeBaseController : ControllerBase
#region Vector
[HttpGet("knowledge/vector/collections")]
public async Task<IEnumerable<string>> GetVectorCollections()
public async Task<IEnumerable<string>> GetVectorCollections([FromQuery] string type)
{
return await _knowledgeService.GetVectorCollections();
return await _knowledgeService.GetVectorCollections(type);
}
[HttpPost("knowledge/vector/{collection}/create-collection/{dimension}")]
public async Task<bool> CreateVectorCollection([FromRoute] string collection, [FromRoute] int dimension)
[HttpPost("knowledge/vector/create-collection")]
public async Task<bool> CreateVectorCollection([FromBody] CreateVectorCollectionRequest request)
{
return await _knowledgeService.CreateVectorCollection(collection, dimension);
return await _knowledgeService.CreateVectorCollection(request.CollectionName, request.CollectionType, request.Dimension, request.Provider, request.Model);
}
[HttpDelete("knowledge/vector/{collection}/delete-collection")]
public async Task<bool> GetVectorCollections([FromRoute] string collection)
public async Task<bool> DeleteVectorCollection([FromRoute] string collection)
{
return await _knowledgeService.DeleteVectorCollection(collection);
}
@ -99,29 +100,6 @@ public class KnowledgeBaseController : ControllerBase
{
return await _knowledgeService.DeleteVectorCollectionData(collection, id);
}
[HttpPost("/knowledge/vector/{collection}/upload")]
public async Task<IActionResult> UploadVectorKnowledge([FromRoute] string collection, IFormFile file, [FromForm] int? startPageNum, [FromForm] int? endPageNum)
{
var setttings = _services.GetRequiredService<FileCoreSettings>();
var textConverter = _services.GetServices<IPdf2TextConverter>().FirstOrDefault(x => x.Provider == setttings.Pdf2TextConverter.Provider);
var filePath = Path.GetTempFileName();
using (var stream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
{
await file.CopyToAsync(stream);
await stream.FlushAsync();
}
var content = await textConverter.ConvertPdfToText(filePath, startPageNum, endPageNum);
await _knowledgeService.FeedVectorKnowledge(collection, new KnowledgeCreationModel
{
Content = content
});
System.IO.File.Delete(filePath);
return Ok(new { count = 1, file.Length });
}
#endregion
@ -143,29 +121,56 @@ public class KnowledgeBaseController : ControllerBase
#endregion
#region Knowledge
[HttpPost("/knowledge/search")]
public async Task<KnowledgeSearchViewModel> SearchKnowledge([FromBody] SearchKnowledgeRequest request)
#region Document
[HttpPost("/knowledge/document/{collection}/upload")]
public async Task<UploadKnowledgeResponse> UploadKnowledgeDocuments([FromRoute] string collection, [FromBody] VectorKnowledgeUploadRequest request)
{
var vectorOptions = new VectorSearchOptions
{
Fields = request.VectorParams.Fields,
Limit = request.VectorParams.Limit ?? 5,
Confidence = request.VectorParams.Confidence ?? 0.5f,
WithVector = request.VectorParams.WithVector
};
var response = await _knowledgeService.UploadKnowledgeDocuments(collection, request.Files);
return response;
}
var graphOptions = new GraphSearchOptions
{
Method = request.GraphParams.Method
};
[HttpDelete("/knowledge/document/{collection}/delete/{fileId}")]
public async Task<bool> DeleteKnowledgeDocument([FromRoute] string collection, [FromRoute] string fileId)
{
var response = await _knowledgeService.DeleteKnowledgeDocument(collection, fileId);
return response;
}
var result = await _knowledgeService.SearchKnowledge(request.Text, request.VectorParams.Collection, vectorOptions, graphOptions);
return new KnowledgeSearchViewModel
[HttpGet("/knowledge/document/{collection}/list")]
public async Task<IEnumerable<KnowledgeFileViewModel>> GetKnowledgeDocuments([FromRoute] string collection)
{
var files = await _knowledgeService.GetKnowledgeDocuments(collection);
return files.Select(x => KnowledgeFileViewModel.From(x));
}
[HttpGet("/knowledge/document/{collection}/file/{fileId}")]
public async Task<IActionResult> GetKnowledgeDocument([FromRoute] string collection, [FromRoute] string fileId)
{
var file = await _knowledgeService.GetKnowledgeDocumentBinaryData(collection, fileId);
return BuildFileResult(file);
}
#endregion
#region Common
[HttpPost("/knowledge/vector/refresh-configs")]
public async Task<string> RefreshVectorCollectionConfigs([FromBody] VectorCollectionConfigsModel request)
{
var saved = await _knowledgeService.RefreshVectorKnowledgeConfigs(request);
return saved ? "Success" : "Fail";
}
#endregion
#region Private methods
private FileContentResult BuildFileResult(FileBinaryDataModel? file)
{
if (file == null)
{
VectorResult = result?.VectorResult?.Select(x => VectorKnowledgeViewModel.From(x)),
GraphResult = result?.GraphResult != null ? new GraphKnowledgeViewModel { Result = result.GraphResult.Result } : null
};
return File(new byte[0], "application/octet-stream", "error.txt");
}
return File(file.FileBinaryData.ToArray(), "application/octet-stream", file.FileName);
}
#endregion
}

View file

@ -138,7 +138,7 @@ public class UserController : ControllerBase
public bool UploadUserAvatar([FromBody] UserAvatarModel input)
{
var fileStorage = _services.GetRequiredService<IFileStorageService>();
var file = new InputFileModel
var file = new FileDataModel
{
FileName = input.FileName,
FileData = input.FileData,

View file

@ -3,5 +3,5 @@ namespace BotSharp.OpenAPI.ViewModels.Conversations;
public class InputMessageFiles
{
public List<MessageState> States { get; set; } = new();
public List<InputFileModel> Files { get; set; } = new();
public List<FileDataModel> Files { get; set; } = new();
}

View file

@ -0,0 +1,21 @@
using System.Text.Json.Serialization;
namespace BotSharp.OpenAPI.ViewModels.Knowledges;
public class CreateVectorCollectionRequest
{
[JsonPropertyName("collection_name")]
public string CollectionName { get; set; }
[JsonPropertyName("collection_type")]
public string CollectionType { get; set; }
[JsonPropertyName("provider")]
public string Provider { get; set; }
[JsonPropertyName("model")]
public string Model { get; set; }
[JsonPropertyName("dimension")]
public int Dimension { get; set; }
}

View file

@ -0,0 +1,33 @@
using System.Text.Json.Serialization;
namespace BotSharp.OpenAPI.ViewModels.Knowledges;
public class KnowledgeFileViewModel
{
[JsonPropertyName("file_id")]
public string FileId { get; set; }
[JsonPropertyName("file_name")]
public string FileName { get; set; }
[JsonPropertyName("file_extension")]
public string FileExtension { get; set; }
[JsonPropertyName("content_type")]
public string ContentType { get; set; }
[JsonPropertyName("file_url")]
public string FileUrl { get; set; }
public static KnowledgeFileViewModel From(KnowledgeFileModel model)
{
return new KnowledgeFileViewModel
{
FileId = model.FileId,
FileName = model.FileName,
FileExtension = model.FileExtension,
ContentType = model.ContentType,
FileUrl = model.FileUrl
};
}
}

View file

@ -1,12 +0,0 @@
using System.Text.Json.Serialization;
namespace BotSharp.OpenAPI.ViewModels.Knowledges;
public class KnowledgeSearchViewModel
{
[JsonPropertyName("vector_result")]
public IEnumerable<VectorKnowledgeViewModel>? VectorResult { get; set; }
[JsonPropertyName("graph_result")]
public GraphKnowledgeViewModel? GraphResult { get; set; }
}

View file

@ -1,43 +0,0 @@
using System.Text.Json.Serialization;
namespace BotSharp.OpenAPI.ViewModels.Knowledges;
public class SearchKnowledgeRequest
{
[JsonPropertyName("text")]
public string Text { get; set; } = string.Empty;
#region Vector
[JsonPropertyName("vector_params")]
public VectorParam VectorParams { get; set; }
#endregion
#region Graph
[JsonPropertyName("graph_params")]
public GraphParam GraphParams { get; set; }
#endregion
}
public class VectorParam
{
[JsonPropertyName("collection")]
public string Collection { get; set; }
[JsonPropertyName("fields")]
public IEnumerable<string>? Fields { get; set; }
[JsonPropertyName("limit")]
public int? Limit { get; set; } = 5;
[JsonPropertyName("confidence")]
public float? Confidence { get; set; } = 0.5f;
[JsonPropertyName("with_vector")]
public bool WithVector { get; set; }
}
public class GraphParam
{
[JsonPropertyName("method")]
public string Method { get; set; } = string.Empty;
}

View file

@ -0,0 +1,6 @@
namespace BotSharp.OpenAPI.ViewModels.Knowledges;
public class VectorKnowledgeUploadRequest
{
public IEnumerable<ExternalFileModel> Files { get; set; } = new List<ExternalFileModel>();
}

View file

@ -14,7 +14,7 @@ public class NativeWhisperProvider : IAudioCompletion
private readonly IFileStorageService _fileStorage;
private readonly ILogger<NativeWhisperProvider> _logger;
public string Provider => "native";
public string Provider => "native-whisper";
public NativeWhisperProvider(
BotSharpDatabaseSettings dbSettings,

View file

@ -99,9 +99,9 @@ public class EditImageFn : IFunctionCallback
{
if (image == null) return;
var files = new List<InputFileModel>()
var files = new List<FileDataModel>()
{
new InputFileModel
new FileDataModel
{
FileName = $"{Guid.NewGuid()}.png",
FileData = $"data:{MediaTypeNames.Image.Png};base64,{image.ImageData}"

View file

@ -77,7 +77,7 @@ public class GenerateImageFn : IFunctionCallback
{
if (images.IsNullOrEmpty()) return;
var files = images.Where(x => !string.IsNullOrEmpty(x?.ImageData)).Select(x => new InputFileModel
var files = images.Where(x => !string.IsNullOrEmpty(x?.ImageData)).Select(x => new FileDataModel
{
FileName = $"{Guid.NewGuid()}.png",
FileData = $"data:{MediaTypeNames.Image.Png};base64,{x.ImageData}"

View file

@ -5,21 +5,36 @@ public static class KnowledgeSettingHelper
public static ITextEmbedding GetTextEmbeddingSetting(IServiceProvider services, string collectionName)
{
var settings = services.GetRequiredService<KnowledgeBaseSettings>();
var found = settings.Collections.FirstOrDefault(x => x.Name == collectionName)?.TextEmbedding;
var db = services.GetRequiredService<IBotSharpRepository>();
// Get collection config from db
var configs = db.GetKnowledgeCollectionConfigs(new VectorCollectionConfigFilter
{
CollectionNames = [collectionName],
VectorStroageProviders = [settings.VectorDb.Provider]
});
var found = configs?.FirstOrDefault()?.TextEmbedding;
var provider = found?.Provider ?? string.Empty;
var model = found?.Model ?? string.Empty;
var dimension = found?.Dimension ?? 0;
if (found == null)
{
found = settings.Default.TextEmbedding;
provider = settings.Default.TextEmbedding.Provider;
model = settings.Default.TextEmbedding.Model;
dimension = settings.Default.TextEmbedding.Dimension;
}
var embedding = services.GetServices<ITextEmbedding>().FirstOrDefault(x => x.Provider == found.Provider);
var dimension = found.Dimension;
// Set up text embedding
var embedding = services.GetServices<ITextEmbedding>().FirstOrDefault(x => x.Provider == provider);
if (found.Dimension <= 0)
if (dimension <= 0)
{
dimension = GetLlmTextEmbeddingDimension(services, found.Provider, found.Model);
dimension = GetLlmTextEmbeddingDimension(services, provider, model);
}
embedding.SetModelName(found.Model);
embedding.SetModelName(model);
embedding.SetDimension(dimension);
return embedding;
}

View file

@ -14,40 +14,48 @@ public static class TextChopper
private static List<string> ChopByWord(string content, ChunkOption option)
{
var chunks = new List<string>();
var words = content.Split(' ').Where(x => !string.IsNullOrWhiteSpace(x)).ToList();
var words = content.Split(' ')
.Where(x => !string.IsNullOrWhiteSpace(x))
.ToList();
var chunk = "";
var chunk = string.Empty;
for (int i = 0; i < words.Count; i++)
{
chunk += words[i] + " ";
if (chunk.Length > option.Size)
{
chunks.Add(chunk.Trim());
chunk = "";
chunk = string.Empty;
i -= option.Conjunction;
}
}
if (chunks.IsNullOrEmpty() && !string.IsNullOrEmpty(chunk))
{
chunks.Add(chunk);
}
return chunks;
}
private static List<string> ChopByChar(string content, ChunkOption option)
{
var chunks = new List<string>();
var chunk = string.Empty;
var currentPos = 0;
while (currentPos < content.Length)
{
var len = content.Length - currentPos > option.Size ?
option.Size :
content.Length - currentPos;
var chunk = content.Substring(currentPos, len);
var len = content.Length - currentPos > option.Size ? option.Size : content.Length - currentPos;
chunk = content.Substring(currentPos, len);
chunks.Add(chunk);
// move backward
currentPos += option.Size - option.Conjunction;
}
if (chunks.IsNullOrEmpty() && !string.IsNullOrEmpty(chunk))
{
chunks.Add(chunk);
}
return chunks;
}
}

View file

@ -37,7 +37,8 @@ public class KnowledgeBasePlugin : IBotSharpPlugin
SubMenu = new List<PluginMenuDef>
{
new PluginMenuDef("Q & A", link: "page/knowledge-base/question-answer"),
new PluginMenuDef("Relationships", link: "page/knowledge-base/relationships")
new PluginMenuDef("Relationships", link: "page/knowledge-base/relationships"),
new PluginMenuDef("Documents", link: "page/knowledge-base/documents")
}
});
return true;

View file

@ -74,7 +74,7 @@ public class MemoryVectorDb : IVectorDb
return true;
}
public async Task<bool> DeleteCollectionData(string collectionName, Guid id)
public async Task<bool> DeleteCollectionData(string collectionName, List<Guid> ids)
{
return await Task.FromResult(false);
}

View file

@ -0,0 +1,12 @@
namespace BotSharp.Plugin.KnowledgeBase.Services;
public partial class KnowledgeService
{
public async Task<bool> RefreshVectorKnowledgeConfigs(VectorCollectionConfigsModel configs)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var collections = configs.Collections ?? new();
var saved = db.AddKnowledgeCollectionConfigs(collections, reset: true);
return await Task.FromResult(saved);
}
}

View file

@ -1,69 +0,0 @@
namespace BotSharp.Plugin.KnowledgeBase.Services;
public partial class KnowledgeService
{
public async Task FeedVectorKnowledge(string collectionName, KnowledgeCreationModel knowledge)
{
var index = 0;
var lines = TextChopper.Chop(knowledge.Content, new ChunkOption
{
Size = 1024,
Conjunction = 32,
SplitByWord = true,
});
var db = GetVectorDb();
var textEmbedding = GetTextEmbedding(collectionName);
await db.CreateCollection(collectionName, textEmbedding.GetDimension());
foreach (var line in lines)
{
var vec = await textEmbedding.GetVectorAsync(line);
await db.Upsert(collectionName, Guid.NewGuid(), vec, line);
index++;
Console.WriteLine($"Saved vector {index}/{lines.Count}: {line}\n");
}
}
public async Task<bool> 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<bool> CreateVectorCollectionData(string collectionName, VectorCreateModel create)
{
try
{
if (string.IsNullOrWhiteSpace(collectionName) || string.IsNullOrWhiteSpace(create.Text))
{
return false;
}
var textEmbedding = GetTextEmbedding(collectionName);
var vector = await textEmbedding.GetVectorAsync(create.Text);
var db = GetVectorDb();
var guid = Guid.NewGuid();
return await db.Upsert(collectionName, guid, vector, create.Text, create.Payload);
}
catch (Exception ex)
{
_logger.LogWarning($"Error when creating vector collection data. {ex.Message}\r\n{ex.InnerException}");
return false;
}
}
}

View file

@ -1,42 +0,0 @@
namespace BotSharp.Plugin.KnowledgeBase.Services;
public partial class KnowledgeService
{
public async Task<bool> 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<bool> DeleteVectorCollectionData(string collectionName, string id)
{
try
{
if (!Guid.TryParse(id, out var guid))
{
return false;
}
var db = GetVectorDb();
return await db.DeleteCollectionData(collectionName, guid);
}
catch (Exception ex)
{
_logger.LogWarning($"Error when deleting vector collection data ({collectionName}-{id}). {ex.Message}\r\n{ex.InnerException}");
return false;
}
}
}

View file

@ -0,0 +1,250 @@
using BotSharp.Abstraction.Files;
using BotSharp.Abstraction.Files.Models;
using BotSharp.Abstraction.Files.Utilities;
using System.Net.Http;
using System.Net.Mime;
namespace BotSharp.Plugin.KnowledgeBase.Services;
public partial class KnowledgeService
{
public async Task<UploadKnowledgeResponse> UploadKnowledgeDocuments(string collectionName, IEnumerable<ExternalFileModel> files)
{
if (string.IsNullOrWhiteSpace(collectionName) || files.IsNullOrEmpty())
{
return new UploadKnowledgeResponse
{
Success = [],
Failed = files?.Select(x => x.FileName) ?? new List<string>()
};
}
var fileStoreage = _services.GetRequiredService<IFileStorageService>();
var userId = await GetUserId();
var vectorStoreProvider = _settings.VectorDb.Provider;
var successFiles = new List<string>();
var failedFiles = new List<string>();
foreach (var file in files)
{
if (string.IsNullOrWhiteSpace(file.FileData)
&& string.IsNullOrWhiteSpace(file.FileUrl))
{
continue;
}
try
{
// Get document info
var (contentType, bytes) = await GetFileInfo(file);
var contents = await GetFileContent(contentType, bytes);
// Save document
var fileId = Guid.NewGuid().ToString();
var saved = SaveDocument(collectionName, vectorStoreProvider, fileId, file.FileName, bytes);
if (!saved)
{
failedFiles.Add(file.FileName);
continue;
}
// Save to vector db
var dataIds = await SaveToVectorDb(collectionName, fileId, file.FileName, contents);
if (!dataIds.IsNullOrEmpty())
{
fileStoreage.SaveKnolwedgeBaseFileMeta(collectionName.CleanStr(), vectorStoreProvider.CleanStr(), fileId, new KnowledgeDocMetaData
{
Collection = collectionName,
FileId = fileId,
FileName = file.FileName,
ContentType = contentType,
VectorDataIds = dataIds,
CreateDate = DateTime.UtcNow,
CreateUserId = userId
});
successFiles.Add(file.FileName);
}
else
{
failedFiles.Add(file.FileName);
}
}
catch (Exception ex)
{
_logger.LogError($"Error when processing knowledge file ({file.FileName}). {ex.Message}\r\n{ex.InnerException}");
failedFiles.Add(file.FileName);
continue;
}
}
return new UploadKnowledgeResponse
{
Success = successFiles,
Failed = failedFiles
};
}
public async Task<bool> DeleteKnowledgeDocument(string collectionName, string fileId)
{
if (string.IsNullOrWhiteSpace(collectionName) || string.IsNullOrWhiteSpace(fileId))
{
return false;
}
try
{
var fileStorage = _services.GetRequiredService<IFileStorageService>();
var vectorDb = GetVectorDb();
var vectorStoreProvider = _settings.VectorDb.Provider;
// Get doc meta data
var metaData = fileStorage.GetKnowledgeBaseFileMeta(collectionName.CleanStr(), vectorStoreProvider.CleanStr(), fileId);
// Delete doc
fileStorage.DeleteKnowledgeFile(collectionName.CleanStr(), vectorStoreProvider.CleanStr(), fileId);
if (metaData != null && !metaData.VectorDataIds.IsNullOrEmpty())
{
var guids = metaData.VectorDataIds.Where(x => Guid.TryParse(x, out _)).Select(x => Guid.Parse(x)).ToList();
await vectorDb.DeleteCollectionData(collectionName, guids);
}
return true;
}
catch (Exception ex)
{
_logger.LogWarning($"Error when deleting knowledge document " +
$"(Collection: {collectionName}, File id: {fileId})" +
$"\r\n{ex.Message}\r\n{ex.InnerException}");
return false;
}
}
public async Task<IEnumerable<KnowledgeFileModel>> GetKnowledgeDocuments(string collectionName)
{
if (string.IsNullOrWhiteSpace(collectionName))
{
return Enumerable.Empty<KnowledgeFileModel>();
}
var fileStorage = _services.GetRequiredService<IFileStorageService>();
var vectorStoreProvider = _settings.VectorDb.Provider;
// Get doc meta data
var files = fileStorage.GetKnowledgeBaseFiles(collectionName.CleanStr(), vectorStoreProvider.CleanStr());
return files;
}
public async Task<FileBinaryDataModel?> GetKnowledgeDocumentBinaryData(string collectionName, string fileId)
{
var fileStorage = _services.GetRequiredService<IFileStorageService>();
var vectorStoreProvider = _settings.VectorDb.Provider;
// Get doc binary data
var file = fileStorage.GetKnowledgeBaseFileBinaryData(collectionName.CleanStr(), vectorStoreProvider.CleanStr(), fileId);
return file;
}
#region Private methods
/// <summary>
/// Get file content type and file bytes
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
private async Task<(string, byte[])> GetFileInfo(ExternalFileModel file)
{
if (file == null)
{
return (string.Empty, new byte[0]);
}
if (!string.IsNullOrWhiteSpace(file.FileUrl))
{
var http = _services.GetRequiredService<IHttpClientFactory>();
var contentType = FileUtility.GetFileContentType(file.FileName);
using var client = http.CreateClient();
var bytes = await client.GetByteArrayAsync(file.FileUrl);
return (contentType, bytes);
}
else if (!string.IsNullOrWhiteSpace(file.FileData))
{
var (contentType, bytes) = FileUtility.GetFileInfoFromData(file.FileData);
return (contentType, bytes);
}
return (string.Empty, new byte[0]);
}
private async Task<IEnumerable<string>> GetFileContent(string contentType, byte[] bytes)
{
var results = new List<string>();
if (contentType.IsEqualTo(MediaTypeNames.Text.Plain))
{
using var stream = new MemoryStream(bytes);
using var reader = new StreamReader(stream);
var content = await reader.ReadToEndAsync();
reader.Close();
stream.Close();
var lines = TextChopper.Chop(content, new ChunkOption
{
Size = 1024,
Conjunction = 32,
SplitByWord = true,
});
results.AddRange(lines);
}
else if (contentType.IsEqualTo(MediaTypeNames.Application.Pdf))
{
// to do
}
return results;
}
private bool SaveDocument(string collectionName, string vectorStoreProvider, string fileId, string fileName, byte[] bytes)
{
var fileStoreage = _services.GetRequiredService<IFileStorageService>();
using var stream = new MemoryStream(bytes);
stream.Position = 0;
var saved = fileStoreage.SaveKnowledgeBaseFile(collectionName.CleanStr(), vectorStoreProvider.CleanStr(), fileId, fileName, stream);
stream.Close();
return saved;
}
private async Task<IEnumerable<string>> SaveToVectorDb(string collectionName, string fileId, string fileName, IEnumerable<string> contents)
{
if (contents.IsNullOrEmpty())
{
return Enumerable.Empty<string>();
}
var dataIds = new List<string>();
var vectorDb = GetVectorDb();
var textEmbedding = GetTextEmbedding(collectionName);
for (int i = 0; i < contents.Count(); i++)
{
var content = contents.ElementAt(i);
var vector = await textEmbedding.GetVectorAsync(content);
var dataId = Guid.NewGuid();
var saved = await vectorDb.Upsert(collectionName, dataId, vector, content, new Dictionary<string, string>
{
{ "fileName", fileName },
{ "fileId", fileId },
{ "textNumber", $"{i + 1}" }
});
if (!saved) continue;
dataIds.Add(dataId.ToString());
}
return dataIds;
}
#endregion
}

View file

@ -1,103 +0,0 @@
namespace BotSharp.Plugin.KnowledgeBase.Services;
public partial class KnowledgeService
{
public async Task<IEnumerable<string>> GetVectorCollections()
{
try
{
var db = GetVectorDb();
return await db.GetCollections();
}
catch (Exception ex)
{
_logger.LogWarning($"Error when getting vector db collections. {ex.Message}\r\n{ex.InnerException}");
return Enumerable.Empty<string>();
}
}
public async Task<StringIdPagedItems<VectorSearchResult>> GetPagedVectorCollectionData(string collectionName, VectorFilter filter)
{
try
{
var db = GetVectorDb();
var pagedResult = await db.GetPagedCollectionData(collectionName, filter);
return new StringIdPagedItems<VectorSearchResult>
{
Count = pagedResult.Count,
Items = pagedResult.Items.Select(x => VectorSearchResult.CopyFrom(x)),
NextId = pagedResult.NextId,
};
}
catch (Exception ex)
{
_logger.LogWarning($"Error when getting vector knowledge collection data ({collectionName}). {ex.Message}\r\n{ex.InnerException}");
return new StringIdPagedItems<VectorSearchResult>();
}
}
public async Task<IEnumerable<VectorSearchResult>> SearchVectorKnowledge(string query, string collectionName, VectorSearchOptions options)
{
try
{
var textEmbedding = GetTextEmbedding(collectionName);
var vector = await textEmbedding.GetVectorAsync(query);
// Vector search
var db = GetVectorDb();
var found = await db.Search(collectionName, vector, options.Fields, limit: options.Limit ?? 5, confidence: options.Confidence ?? 0.5f, withVector: options.WithVector);
var results = found.Select(x => VectorSearchResult.CopyFrom(x)).ToList();
return results;
}
catch (Exception ex)
{
_logger.LogWarning($"Error when searching vector knowledge ({collectionName}). {ex.Message}\r\n{ex.InnerException}");
return new List<VectorSearchResult>();
}
}
public async Task<GraphSearchResult> SearchGraphKnowledge(string query, GraphSearchOptions options)
{
try
{
var db = GetGraphDb();
var found = await db.Search(query, options);
return new GraphSearchResult
{
Result = found.Result
};
}
catch (Exception ex)
{
_logger.LogWarning($"Error when searching graph knowledge (Query: {query}). {ex.Message}\r\n{ex.InnerException}");
return new GraphSearchResult();
}
}
public async Task<KnowledgeSearchResult> SearchKnowledge(string query, string collectionName, VectorSearchOptions vectorOptions, GraphSearchOptions graphOptions)
{
try
{
var textEmbedding = GetTextEmbedding(collectionName);
var vector = await textEmbedding.GetVectorAsync(query);
var vectorDb = GetVectorDb();
var vectorRes = await vectorDb.Search(collectionName, vector, vectorOptions.Fields, limit: vectorOptions.Limit ?? 5,
confidence: vectorOptions.Confidence ?? 0.5f, withVector: vectorOptions.WithVector);
var graphDb = GetGraphDb();
var graphRes = await graphDb.Search(query, graphOptions);
return new KnowledgeSearchResult
{
VectorResult = vectorRes.Select(x => VectorSearchResult.CopyFrom(x)),
GraphResult = new GraphSearchResult { Result = graphRes.Result }
};
}
catch (Exception ex)
{
_logger.LogWarning($"Error when searching knowledge (Vector collection: {collectionName}) (Query: {query}). {ex.Message}\r\n{ex.InnerException}");
return new KnowledgeSearchResult();
}
}
}

View file

@ -0,0 +1,22 @@
namespace BotSharp.Plugin.KnowledgeBase.Services;
public partial class KnowledgeService
{
public async Task<GraphSearchResult> SearchGraphKnowledge(string query, GraphSearchOptions options)
{
try
{
var db = GetGraphDb();
var found = await db.Search(query, options);
return new GraphSearchResult
{
Result = found.Result
};
}
catch (Exception ex)
{
_logger.LogWarning($"Error when searching graph knowledge (Query: {query}). {ex.Message}\r\n{ex.InnerException}");
return new GraphSearchResult();
}
}
}

View file

@ -1,31 +0,0 @@
namespace BotSharp.Plugin.KnowledgeBase.Services;
public partial class KnowledgeService
{
public async Task<bool> UpdateVectorCollectionData(string collectionName, VectorUpdateModel update)
{
try
{
if (string.IsNullOrWhiteSpace(collectionName) || string.IsNullOrWhiteSpace(update.Text) || !Guid.TryParse(update.Id, out var guid))
{
return false;
}
var db = GetVectorDb();
var found = await db.GetCollectionData(collectionName, new List<Guid> { guid });
if (found.IsNullOrEmpty())
{
return false;
}
var textEmbedding = GetTextEmbedding(collectionName);
var vector = await textEmbedding.GetVectorAsync(update.Text);
return await db.Upsert(collectionName, guid, vector, update.Text, update.Payload);
}
catch (Exception ex)
{
_logger.LogWarning($"Error when updating vector collection data. {ex.Message}\r\n{ex.InnerException}");
return false;
}
}
}

View file

@ -0,0 +1,218 @@
using BotSharp.Abstraction.Files;
namespace BotSharp.Plugin.KnowledgeBase.Services;
public partial class KnowledgeService
{
#region Collection
public async Task<bool> CreateVectorCollection(string collectionName, string collectionType, int dimension, string provider, string model)
{
try
{
if (string.IsNullOrWhiteSpace(collectionName))
{
return false;
}
var vectorDb = GetVectorDb();
var created = await vectorDb.CreateCollection(collectionName, dimension);
if (created)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var userId = await GetUserId();
db.AddKnowledgeCollectionConfigs(new List<VectorCollectionConfig>
{
new VectorCollectionConfig
{
Name = collectionName,
Type = collectionType,
VectorStorage = new VectorStorageConfig
{
Provider = _settings.VectorDb.Provider
},
TextEmbedding = new KnowledgeEmbeddingConfig
{
Provider = provider,
Model = model,
Dimension = dimension
}
}
});
}
return created;
}
catch (Exception ex)
{
_logger.LogWarning($"Error when creating a vector collection ({collectionName}). {ex.Message}\r\n{ex.InnerException}");
return false;
}
}
public async Task<IEnumerable<string>> GetVectorCollections(string type)
{
try
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var collectionNames = db.GetKnowledgeCollectionConfigs(new VectorCollectionConfigFilter
{
CollectionTypes = new[] { type },
VectorStroageProviders = new[] { _settings.VectorDb.Provider }
}).Select(x => x.Name).ToList();
var vectorDb = GetVectorDb();
var vectorCollections = await vectorDb.GetCollections();
return vectorCollections.Where(x => collectionNames.Contains(x));
}
catch (Exception ex)
{
_logger.LogWarning($"Error when getting vector db collections. {ex.Message}\r\n{ex.InnerException}");
return Enumerable.Empty<string>();
}
}
public async Task<bool> DeleteVectorCollection(string collectionName)
{
try
{
if (string.IsNullOrWhiteSpace(collectionName))
{
return false;
}
var vectorDb = GetVectorDb();
var deleted = await vectorDb.DeleteCollection(collectionName);
if (deleted)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var fileStorage = _services.GetRequiredService<IFileStorageService>();
var vectorStoreProvider = _settings.VectorDb.Provider;
db.DeleteKnowledgeCollectionConfig(collectionName);
fileStorage.DeleteKnowledgeFile(collectionName.CleanStr(), vectorStoreProvider.CleanStr());
}
return deleted;
}
catch (Exception ex)
{
_logger.LogWarning($"Error when deleting collection ({collectionName}). {ex.Message}\r\n{ex.InnerException}");
return false;
}
}
#endregion
#region Collection data
public async Task<bool> CreateVectorCollectionData(string collectionName, VectorCreateModel create)
{
try
{
if (string.IsNullOrWhiteSpace(collectionName) || string.IsNullOrWhiteSpace(create.Text))
{
return false;
}
var textEmbedding = GetTextEmbedding(collectionName);
var vector = await textEmbedding.GetVectorAsync(create.Text);
var db = GetVectorDb();
var guid = Guid.NewGuid();
return await db.Upsert(collectionName, guid, vector, create.Text, create.Payload);
}
catch (Exception ex)
{
_logger.LogWarning($"Error when creating vector collection data. {ex.Message}\r\n{ex.InnerException}");
return false;
}
}
public async Task<bool> UpdateVectorCollectionData(string collectionName, VectorUpdateModel update)
{
try
{
if (string.IsNullOrWhiteSpace(collectionName) || string.IsNullOrWhiteSpace(update.Text) || !Guid.TryParse(update.Id, out var guid))
{
return false;
}
var db = GetVectorDb();
var found = await db.GetCollectionData(collectionName, new List<Guid> { guid });
if (found.IsNullOrEmpty())
{
return false;
}
var textEmbedding = GetTextEmbedding(collectionName);
var vector = await textEmbedding.GetVectorAsync(update.Text);
return await db.Upsert(collectionName, guid, vector, update.Text, update.Payload);
}
catch (Exception ex)
{
_logger.LogWarning($"Error when updating vector collection data. {ex.Message}\r\n{ex.InnerException}");
return false;
}
}
public async Task<bool> DeleteVectorCollectionData(string collectionName, string id)
{
try
{
if (!Guid.TryParse(id, out var guid))
{
return false;
}
var db = GetVectorDb();
return await db.DeleteCollectionData(collectionName, new List<Guid> { guid });
}
catch (Exception ex)
{
_logger.LogWarning($"Error when deleting vector collection data ({collectionName}-{id}). {ex.Message}\r\n{ex.InnerException}");
return false;
}
}
public async Task<StringIdPagedItems<VectorSearchResult>> GetPagedVectorCollectionData(string collectionName, VectorFilter filter)
{
try
{
var db = GetVectorDb();
var pagedResult = await db.GetPagedCollectionData(collectionName, filter);
return new StringIdPagedItems<VectorSearchResult>
{
Count = pagedResult.Count,
Items = pagedResult.Items.Select(x => VectorSearchResult.CopyFrom(x)),
NextId = pagedResult.NextId,
};
}
catch (Exception ex)
{
_logger.LogWarning($"Error when getting vector knowledge collection data ({collectionName}). {ex.Message}\r\n{ex.InnerException}");
return new StringIdPagedItems<VectorSearchResult>();
}
}
public async Task<IEnumerable<VectorSearchResult>> SearchVectorKnowledge(string query, string collectionName, VectorSearchOptions options)
{
try
{
var textEmbedding = GetTextEmbedding(collectionName);
var vector = await textEmbedding.GetVectorAsync(query);
// Vector search
var db = GetVectorDb();
var found = await db.Search(collectionName, vector, options.Fields, limit: options.Limit ?? 5, confidence: options.Confidence ?? 0.5f, withVector: options.WithVector);
var results = found.Select(x => VectorSearchResult.CopyFrom(x)).ToList();
return results;
}
catch (Exception ex)
{
_logger.LogWarning($"Error when searching vector knowledge ({collectionName}). {ex.Message}\r\n{ex.InnerException}");
return Enumerable.Empty<VectorSearchResult>();
}
}
#endregion
}

View file

@ -3,15 +3,18 @@ namespace BotSharp.Plugin.KnowledgeBase.Services;
public partial class KnowledgeService : IKnowledgeService
{
private readonly IServiceProvider _services;
private readonly IUserIdentity _user;
private readonly KnowledgeBaseSettings _settings;
private readonly ILogger<KnowledgeService> _logger;
public KnowledgeService(
IServiceProvider services,
IUserIdentity user,
KnowledgeBaseSettings settings,
ILogger<KnowledgeService> logger)
{
_services = services;
_user = user;
_settings = settings;
_logger = logger;
}
@ -28,8 +31,15 @@ public partial class KnowledgeService : IKnowledgeService
return db;
}
private ITextEmbedding GetTextEmbedding(string collection)
private ITextEmbedding GetTextEmbedding(string collectionName)
{
return KnowledgeSettingHelper.GetTextEmbeddingSetting(_services, collection);
return KnowledgeSettingHelper.GetTextEmbeddingSetting(_services, collectionName);
}
private async Task<string> GetUserId()
{
var userService = _services.GetRequiredService<IUserService>();
var user = await userService.GetUser(_user.Id);
return user.Id;
}
}

View file

@ -48,7 +48,7 @@ public class FaissDb : IVectorDb
throw new NotImplementedException();
}
public Task<bool> DeleteCollectionData(string collectionName, Guid id)
public Task<bool> DeleteCollectionData(string collectionName, List<Guid> ids)
{
throw new NotImplementedException();
}

View file

@ -0,0 +1,9 @@
namespace BotSharp.Plugin.MongoStorage.Collections;
public class KnowledgeCollectionConfigDocument : MongoBase
{
public string Name { get; set; }
public string Type { get; set; }
public KnowledgeVectorStorageConfigMongoModel VectorStorage { get; set; }
public KnowledgeEmbeddingConfigMongoModel TextEmbedding { get; set; }
}

View file

@ -0,0 +1,30 @@
using BotSharp.Abstraction.VectorStorage.Models;
namespace BotSharp.Plugin.MongoStorage.Models;
public class KnowledgeEmbeddingConfigMongoModel
{
public string Provider { get; set; }
public string Model { get; set; }
public int Dimension { get; set; }
public static KnowledgeEmbeddingConfigMongoModel ToMongoModel(KnowledgeEmbeddingConfig model)
{
return new KnowledgeEmbeddingConfigMongoModel
{
Provider = model.Provider,
Model = model.Model,
Dimension = model.Dimension
};
}
public static KnowledgeEmbeddingConfig ToDomainModel(KnowledgeEmbeddingConfigMongoModel model)
{
return new KnowledgeEmbeddingConfig
{
Provider = model.Provider,
Model = model.Model,
Dimension = model.Dimension
};
}
}

View file

@ -0,0 +1,24 @@
using BotSharp.Abstraction.VectorStorage.Models;
namespace BotSharp.Plugin.MongoStorage.Models;
public class KnowledgeVectorStorageConfigMongoModel
{
public string Provider { get; set; }
public static KnowledgeVectorStorageConfigMongoModel ToMongoModel(VectorStorageConfig model)
{
return new KnowledgeVectorStorageConfigMongoModel
{
Provider = model.Provider
};
}
public static VectorStorageConfig ToDomainModel(KnowledgeVectorStorageConfigMongoModel model)
{
return new VectorStorageConfig
{
Provider = model.Provider
};
}
}

View file

@ -153,4 +153,7 @@ public class MongoDbContext
public IMongoCollection<TranslationMemoryDocument> TranslationMemories
=> Database.GetCollection<TranslationMemoryDocument>($"{_collectionPrefix}_TranslationMemories");
public IMongoCollection<KnowledgeCollectionConfigDocument> KnowledgeCollectionConfigs
=> Database.GetCollection<KnowledgeCollectionConfigDocument>($"{_collectionPrefix}_KnowledgeCollectionConfigs");
}

View file

@ -0,0 +1,114 @@
using BotSharp.Abstraction.VectorStorage.Models;
namespace BotSharp.Plugin.MongoStorage.Repository;
public partial class MongoRepository
{
public bool AddKnowledgeCollectionConfigs(List<VectorCollectionConfig> configs, bool reset = false)
{
var filter = Builders<KnowledgeCollectionConfigDocument>.Filter.Empty;
var docs = configs?.Where(x => !string.IsNullOrWhiteSpace(x.Name))
.Select(x => new KnowledgeCollectionConfigDocument
{
Id = Guid.NewGuid().ToString(),
Name = x.Name,
Type = x.Type,
VectorStorage = KnowledgeVectorStorageConfigMongoModel.ToMongoModel(x.VectorStorage),
TextEmbedding = KnowledgeEmbeddingConfigMongoModel.ToMongoModel(x.TextEmbedding)
})?.ToList() ?? new List<KnowledgeCollectionConfigDocument>();
if (reset)
{
_dc.KnowledgeCollectionConfigs.DeleteMany(filter);
_dc.KnowledgeCollectionConfigs.InsertMany(docs);
return true;
}
// Update if collection already exists, otherwise insert.
var insertDocs = new List<KnowledgeCollectionConfigDocument>();
var updateDocs = new List<KnowledgeCollectionConfigDocument>();
var names = docs.Select(x => x.Name).ToList();
filter = Builders<KnowledgeCollectionConfigDocument>.Filter.In(x => x.Name, names);
var savedConfigs = _dc.KnowledgeCollectionConfigs.Find(filter).ToList();
foreach (var doc in docs)
{
var found = savedConfigs.FirstOrDefault(x => x.Name == doc.Name);
if (found != null)
{
found.Type = doc.Type;
found.VectorStorage = doc.VectorStorage;
found.TextEmbedding = doc.TextEmbedding;
updateDocs.Add(found);
}
else
{
insertDocs.Add(doc);
}
}
if (!insertDocs.IsNullOrEmpty())
{
_dc.KnowledgeCollectionConfigs.InsertMany(docs);
}
if (!updateDocs.IsNullOrEmpty())
{
foreach (var doc in updateDocs)
{
filter = Builders<KnowledgeCollectionConfigDocument>.Filter.Eq(x => x.Id, doc.Id);
_dc.KnowledgeCollectionConfigs.ReplaceOne(filter, doc);
}
}
return true;
}
public bool DeleteKnowledgeCollectionConfig(string collectionName)
{
if (string.IsNullOrWhiteSpace(collectionName)) return false;
var filter = Builders<KnowledgeCollectionConfigDocument>.Filter.Eq(x => x.Name, collectionName);
var deleted = _dc.KnowledgeCollectionConfigs.DeleteMany(filter);
return deleted.DeletedCount > 0;
}
public IEnumerable<VectorCollectionConfig> GetKnowledgeCollectionConfigs(VectorCollectionConfigFilter filter)
{
if (filter == null)
{
return Enumerable.Empty<VectorCollectionConfig>();
}
var builder = Builders<KnowledgeCollectionConfigDocument>.Filter;
var filters = new List<FilterDefinition<KnowledgeCollectionConfigDocument>> { builder.Empty };
// Apply filters
if (!filter.CollectionNames.IsNullOrEmpty())
{
filters.Add(builder.In(x => x.Name, filter.CollectionNames));
}
if (!filter.CollectionTypes.IsNullOrEmpty())
{
filters.Add(builder.In(x => x.Type, filter.CollectionTypes));
}
if (!filter.VectorStroageProviders.IsNullOrEmpty())
{
filters.Add(builder.In(x => x.VectorStorage.Provider, filter.VectorStroageProviders));
}
// Get data
var configs = _dc.KnowledgeCollectionConfigs.Find(Builders<KnowledgeCollectionConfigDocument>.Filter.And(filters)).ToList();
return configs.Select(x => new VectorCollectionConfig
{
Name = x.Name,
Type = x.Type,
VectorStorage = KnowledgeVectorStorageConfigMongoModel.ToDomainModel(x.VectorStorage),
TextEmbedding = KnowledgeEmbeddingConfigMongoModel.ToDomainModel(x.TextEmbedding)
});
}
}

View file

@ -21,7 +21,6 @@ public class PrimaryStagePlanFn : IFunctionCallback
var state = _services.GetRequiredService<IConversationStateService>();
var knowledgeService = _services.GetRequiredService<IKnowledgeService>();
var knowledgeSettings = _services.GetRequiredService<KnowledgeBaseSettings>();
var fn = _services.GetRequiredService<IRoutingService>();
state.SetState("max_tokens", "4096");
var task = JsonSerializer.Deserialize<PrimaryRequirementRequest>(message.FunctionArgs);

View file

@ -1,5 +1,6 @@
using BotSharp.Abstraction.Utilities;
using BotSharp.Abstraction.VectorStorage.Models;
using Microsoft.Extensions.Logging;
using Qdrant.Client;
using Qdrant.Client.Grpc;
@ -10,12 +11,15 @@ public class QdrantDb : IVectorDb
private QdrantClient _client;
private readonly QdrantSetting _setting;
private readonly IServiceProvider _services;
private readonly ILogger<QdrantDb> _logger;
public QdrantDb(
QdrantSetting setting,
ILogger<QdrantDb> logger,
IServiceProvider services)
{
_setting = setting;
_logger = logger;
_services = services;
}
@ -35,21 +39,28 @@ public class QdrantDb : IVectorDb
return _client;
}
public async Task<bool> CreateCollection(string collectionName, int dim)
public async Task<bool> CreateCollection(string collectionName, int dimension)
{
var client = GetClient();
var exist = await DoesCollectionExist(client, collectionName);
if (exist) return false;
// Create a new collection
await client.CreateCollectionAsync(collectionName, new VectorParams()
try
{
Size = (ulong)dim,
Distance = Distance.Cosine
});
return true;
// Create a new collection
await client.CreateCollectionAsync(collectionName, new VectorParams()
{
Size = (ulong)dimension,
Distance = Distance.Cosine
});
return true;
}
catch (Exception ex)
{
_logger.LogWarning($"Error when create collection (Name: {collectionName}, Dimension: {dimension}).");
return false;
}
}
public async Task<bool> DeleteCollection(string collectionName)
@ -145,8 +156,6 @@ public class QdrantDb : IVectorDb
});
}
public async Task<bool> Upsert(string collectionName, Guid id, float[] vector, string text, Dictionary<string, string>? payload = null)
{
// Insert vectors
@ -216,10 +225,12 @@ public class QdrantDb : IVectorDb
return results;
}
public async Task<bool> DeleteCollectionData(string collectionName, Guid id)
public async Task<bool> DeleteCollectionData(string collectionName, List<Guid> ids)
{
if (ids.IsNullOrEmpty()) return false;
var client = GetClient();
var result = await client.DeleteAsync(collectionName, id);
var result = await client.DeleteAsync(collectionName, ids);
return result.Status == UpdateStatus.Completed;
}

View file

@ -4,6 +4,7 @@ using BotSharp.Abstraction.VectorStorage.Models;
using Microsoft.SemanticKernel.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace BotSharp.Plugin.SemanticKernel
@ -84,16 +85,15 @@ namespace BotSharp.Plugin.SemanticKernel
return true;
}
public async Task<bool> DeleteCollectionData(string collectionName, Guid id)
public async Task<bool> DeleteCollectionData(string collectionName, List<Guid> ids)
{
var exist = await _memoryStore.DoesCollectionExistAsync(collectionName);
if (ids.IsNullOrEmpty()) return false;
if (exist)
{
await _memoryStore.RemoveAsync(collectionName, id.ToString());
return true;
}
return false;
var exist = await _memoryStore.DoesCollectionExistAsync(collectionName);
if (!exist) return false;
await _memoryStore.RemoveBatchAsync(collectionName, ids.Select(x => x.ToString()));
return true;
}
}
}

View file

@ -115,7 +115,7 @@ public partial class TencentCosService
return foundMsgs;
}
public bool SaveMessageFiles(string conversationId, string messageId, string source, List<InputFileModel> files)
public bool SaveMessageFiles(string conversationId, string messageId, string source, List<FileDataModel> files)
{
if (files.IsNullOrEmpty()) return false;

View file

@ -0,0 +1,36 @@
using BotSharp.Abstraction.Knowledges.Models;
namespace BotSharp.Plugin.TencentCos.Services;
public partial class TencentCosService
{
public bool SaveKnowledgeBaseFile(string collectionName, string vectorStoreProvider, string fileId, string fileName, Stream stream)
{
throw new NotImplementedException();
}
public bool DeleteKnowledgeFile(string collectionName, string vectorStoreProvider, string? fileId = null)
{
throw new NotImplementedException();
}
public bool SaveKnolwedgeBaseFileMeta(string collectionName, string vectorStoreProvider, string fileId, KnowledgeDocMetaData metaData)
{
throw new NotImplementedException();
}
public KnowledgeDocMetaData? GetKnowledgeBaseFileMeta(string collectionName, string vectorStoreProvider, string fileId)
{
throw new NotImplementedException();
}
public IEnumerable<KnowledgeFileModel> GetKnowledgeBaseFiles(string collectionName, string vectorStoreProvider)
{
throw new NotImplementedException();
}
public FileBinaryDataModel? GetKnowledgeBaseFileBinaryData(string collectionName, string vectorStoreProvider, string fileId)
{
throw new NotImplementedException();
}
}

View file

@ -16,7 +16,7 @@ public partial class TencentCosService
return found;
}
public bool SaveUserAvatar(InputFileModel file)
public bool SaveUserAvatar(FileDataModel file)
{
if (file == null || string.IsNullOrEmpty(file.FileData)) return false;

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Files;
using BotSharp.Abstraction.Knowledges.Models;
using BotSharp.Abstraction.Users;
using BotSharp.Plugin.TencentCos.Settings;
using System.Net.Mime;

View file

@ -141,7 +141,8 @@
"Enable": true,
"BatchSize": 50,
"MessageLimit": 2,
"BufferHours": 12
"BufferHours": 12,
"ExcludeAgentIds": []
},
"RateLimit": {
"MaxConversationPerDay": 100,