diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs index 5bf7aced..20825791 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs @@ -8,8 +8,10 @@ public interface IKnowledgeService Task> GetVectorCollections(); Task> SearchVectorKnowledge(string query, string collectionName, VectorSearchOptions options); Task FeedVectorKnowledge(string collectionName, KnowledgeCreationModel model); - Task> GetVectorCollectionData(string collectionName, VectorFilter filter); + Task> GetPagedVectorCollectionData(string collectionName, VectorFilter filter); Task DeleteVectorCollectionData(string collectionName, string id); + Task CreateVectorCollectionData(string collectionName, VectorCreateModel create); + Task UpdateVectorCollectionData(string collectionName, VectorUpdateModel update); Task SearchGraphKnowledge(string query, GraphSearchOptions options); Task SearchKnowledge(string query, string collectionName, VectorSearchOptions vectorOptions, GraphSearchOptions graphOptions); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/Settings/KnowledgeBaseSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Settings/KnowledgeBaseSettings.cs index 65bf7637..963a2b3b 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/Settings/KnowledgeBaseSettings.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Settings/KnowledgeBaseSettings.cs @@ -4,14 +4,28 @@ namespace BotSharp.Abstraction.Knowledges.Settings; public class KnowledgeBaseSettings { - public string DefaultCollection { get; set; } = KnowledgeCollectionName.BotSharp; public string VectorDb { get; set; } public string GraphDb { get; set; } - public KnowledgeModelSetting TextEmbedding { get; set; } + + public DefaultKnowledgeBaseSetting Default { get; set; } + public List Collections { get; set; } = new(); } -public class KnowledgeModelSetting +public class DefaultKnowledgeBaseSetting +{ + public string CollectionName { get; set; } = KnowledgeCollectionName.BotSharp; + public KnowledgeTextEmbeddingSetting TextEmbedding { get; set; } +} + +public class VectorCollectionSetting +{ + public string Name { get; set; } + public KnowledgeTextEmbeddingSetting TextEmbedding { get; set; } +} + +public class KnowledgeTextEmbeddingSetting { public string Provider { get; set; } public string Model { get; set; } + public int Dimension { get; set; } } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/MLTasks/ITextEmbedding.cs b/src/Infrastructure/BotSharp.Abstraction/MLTasks/ITextEmbedding.cs index 8ad6363e..79b3aff8 100644 --- a/src/Infrastructure/BotSharp.Abstraction/MLTasks/ITextEmbedding.cs +++ b/src/Infrastructure/BotSharp.Abstraction/MLTasks/ITextEmbedding.cs @@ -6,8 +6,9 @@ public interface ITextEmbedding /// The Embedding provider like Microsoft Azure, OpenAI, ClaudAI /// string Provider { get; } - int Dimension { get; set; } Task GetVectorAsync(string text); Task> GetVectorsAsync(List texts); void SetModelName(string model); + void SetDimension(int dimension); + int GetDimension(); } diff --git a/src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs b/src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs index 16e19a6f..cc7cfba0 100644 --- a/src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs +++ b/src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs @@ -52,6 +52,11 @@ public class LlmModelSetting /// public float CompletionCost { get; set; } + /// + /// Embedding dimension + /// + public int Dimension { get; set; } + public override string ToString() { return $"[{Type}] {Name} {Endpoint}"; diff --git a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs index 5dac3766..53b921a1 100644 --- a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs +++ b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs @@ -7,7 +7,8 @@ public interface IVectorDb string Name { get; } Task> GetCollections(); - Task> GetCollectionData(string collectionName, VectorFilter filter); + 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 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); diff --git a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorCreateModel.cs b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorCreateModel.cs new file mode 100644 index 00000000..3d9cb88c --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorCreateModel.cs @@ -0,0 +1,7 @@ +namespace BotSharp.Abstraction.VectorStorage.Models; + +public class VectorCreateModel +{ + public string Text { get; set; } + public Dictionary? Payload { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorUpdateModel.cs b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorUpdateModel.cs new file mode 100644 index 00000000..45600219 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorUpdateModel.cs @@ -0,0 +1,6 @@ +namespace BotSharp.Abstraction.VectorStorage.Models; + +public class VectorUpdateModel : VectorCreateModel +{ + public string Id { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Core/Infrastructures/CompletionProvider.cs b/src/Infrastructure/BotSharp.Core/Infrastructures/CompletionProvider.cs index bb10ccf3..874a063e 100644 --- a/src/Infrastructure/BotSharp.Core/Infrastructures/CompletionProvider.cs +++ b/src/Infrastructure/BotSharp.Core/Infrastructures/CompletionProvider.cs @@ -111,7 +111,12 @@ public class CompletionProvider logger.LogError($"Can't resolve completion provider by {provider}"); } + + var llmProviderService = services.GetRequiredService(); + var found = llmProviderService.GetSetting(provider, model); + completer.SetModelName(model); + completer.SetDimension(found.Dimension); return completer; } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs index 90773dfd..deb21378 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs @@ -39,10 +39,10 @@ public class KnowledgeBaseController : ControllerBase return results.Select(x => VectorKnowledgeViewModel.From(x)).ToList(); } - [HttpPost("/knowledge/vector/{collection}/data")] - public async Task> GetVectorCollectionData([FromRoute] string collection, [FromBody] VectorFilter filter) + [HttpPost("/knowledge/vector/{collection}/page")] + public async Task> GetPagedVectorCollectionData([FromRoute] string collection, [FromBody] VectorFilter filter) { - var data = await _knowledgeService.GetVectorCollectionData(collection, filter); + var data = await _knowledgeService.GetPagedVectorCollectionData(collection, filter); var items = data.Items?.Select(x => VectorKnowledgeViewModel.From(x))? .ToList() ?? new List(); @@ -54,6 +54,33 @@ public class KnowledgeBaseController : ControllerBase }; } + [HttpPost("/knowledge/vector/{collection}/create")] + public async Task CreateVectorKnowledge([FromRoute] string collection, [FromBody] VectorKnowledgeCreateRequest request) + { + var create = new VectorCreateModel + { + Text = request.Text, + Payload = request.Payload + }; + + var created = await _knowledgeService.CreateVectorCollectionData(collection, create); + return created; + } + + [HttpPut("/knowledge/vector/{collection}/update")] + public async Task UpdateVectorKnowledge([FromRoute] string collection, [FromBody] VectorKnowledgeUpdateRequest request) + { + var update = new VectorUpdateModel + { + Id = request.Id, + Text = request.Text, + Payload = request.Payload + }; + + var updated = await _knowledgeService.UpdateVectorCollectionData(collection, update); + return updated; + } + [HttpDelete("/knowledge/vector/{collection}/data/{id}")] public async Task DeleteVectorCollectionData([FromRoute] string collection, [FromRoute] string id) { diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/TextEmbeddingController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/TextEmbeddingController.cs index 73bfbfcb..1eee6f2d 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/TextEmbeddingController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/TextEmbeddingController.cs @@ -18,7 +18,7 @@ public class TextEmbeddingController : ControllerBase _logger = logger; } - [HttpPost("/text-embedding/generation")] + [HttpPost("/text-embedding/generate")] public async Task> GenerateTextEmbeddings(EmbeddingInputModel input) { var state = _services.GetRequiredService(); @@ -27,7 +27,10 @@ public class TextEmbeddingController : ControllerBase try { var completion = CompletionProvider.GetTextEmbedding(_services, provider: input.Provider ?? "openai", model: input.Model ?? "text-embedding-3-large"); - completion.Dimension = input.Dimension; + if (input.Dimension.HasValue && input.Dimension.Value > 0) + { + completion.SetDimension(input.Dimension.Value); + } var embeddings = await completion.GetVectorsAsync(input.Texts?.ToList() ?? []); return embeddings; diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Embeddings/EmbeddingInputModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Embeddings/EmbeddingInputModel.cs index b01840f0..50559580 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Embeddings/EmbeddingInputModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Embeddings/EmbeddingInputModel.cs @@ -8,6 +8,6 @@ public class EmbeddingInputModel : MessageConfig public IEnumerable Texts { get; set; } = new List(); [JsonPropertyName("dimension")] - public int Dimension { get; set; } = 3072; + public int? Dimension { get; set; } = 3072; } diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/VectorKnowledgeCreateRequest.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/VectorKnowledgeCreateRequest.cs new file mode 100644 index 00000000..37948322 --- /dev/null +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/VectorKnowledgeCreateRequest.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace BotSharp.OpenAPI.ViewModels.Knowledges; + +public class VectorKnowledgeCreateRequest +{ + [JsonPropertyName("text")] + public string Text { get; set; } + + [JsonPropertyName("payload")] + public Dictionary? Payload { get; set; } +} diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/VectorKnowledgeUpdateRequest.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/VectorKnowledgeUpdateRequest.cs new file mode 100644 index 00000000..355ae776 --- /dev/null +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/VectorKnowledgeUpdateRequest.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace BotSharp.OpenAPI.ViewModels.Knowledges; + +public class VectorKnowledgeUpdateRequest : VectorKnowledgeCreateRequest +{ + [JsonPropertyName("id")] + public string Id { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Embedding/TextEmbeddingProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Embedding/TextEmbeddingProvider.cs index 092d62ec..39119050 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Embedding/TextEmbeddingProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Embedding/TextEmbeddingProvider.cs @@ -10,11 +10,10 @@ public class TextEmbeddingProvider : ITextEmbedding private const int DEFAULT_DIMENSION = 3072; protected string _model; + protected int _dimension; public virtual string Provider => "azure-openai"; - public int Dimension { get; set; } - public TextEmbeddingProvider( AzureOpenAiSettings settings, ILogger logger, @@ -50,24 +49,26 @@ public class TextEmbeddingProvider : ITextEmbedding _model = model; } + public void SetDimension(int dimension) + { + _dimension = dimension > 0 ? dimension : DEFAULT_DIMENSION; + } + + public int GetDimension() + { + return _dimension; + } + private EmbeddingGenerationOptions PrepareOptions() { return new EmbeddingGenerationOptions { - Dimensions = GetDimension() + Dimensions = GetDimensionOption() }; } - private int GetDimension() + private int GetDimensionOption() { - var state = _services.GetRequiredService(); - var stateDimension = state.GetState("embedding_dimension"); - var defaultDimension = Dimension > 0 ? Dimension : DEFAULT_DIMENSION; - - if (int.TryParse(stateDimension, out var dimension)) - { - return dimension > 0 ? dimension : defaultDimension; - } - return defaultDimension; + return _dimension > 0 ? _dimension : DEFAULT_DIMENSION; } } diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/KnowledgeRetrievalFn.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/KnowledgeRetrievalFn.cs index dcb4650e..060e2150 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/KnowledgeRetrievalFn.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/KnowledgeRetrievalFn.cs @@ -17,12 +17,11 @@ public class KnowledgeRetrievalFn : IFunctionCallback { var args = JsonSerializer.Deserialize(message.FunctionArgs ?? "{}"); - var embedding = _services.GetServices().FirstOrDefault(x => x.Provider == _settings.TextEmbedding.Provider); - embedding.SetModelName(_settings.TextEmbedding.Model); + var collectionName = _settings.Default.CollectionName ?? KnowledgeCollectionName.BotSharp; + var embedding = KnowledgeSettingUtility.GetTextEmbeddingSetting(_services, collectionName); var vector = await embedding.GetVectorAsync(args.Question); var vectorDb = _services.GetServices().FirstOrDefault(x => x.Name == _settings.VectorDb); - var collectionName = !string.IsNullOrWhiteSpace(_settings.DefaultCollection) ? _settings.DefaultCollection : KnowledgeCollectionName.BotSharp; var knowledges = await vectorDb.Search(collectionName, vector, new List { KnowledgePayloadName.Text, KnowledgePayloadName.Answer }); if (!knowledges.IsNullOrEmpty()) diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/MemorizeKnowledgeFn.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/MemorizeKnowledgeFn.cs index 728a1be9..f709e3b8 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/MemorizeKnowledgeFn.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/MemorizeKnowledgeFn.cs @@ -17,8 +17,8 @@ public class MemorizeKnowledgeFn : IFunctionCallback { var args = JsonSerializer.Deserialize(message.FunctionArgs ?? "{}"); - var embedding = _services.GetServices().FirstOrDefault(x => x.Provider == _settings.TextEmbedding.Provider); - embedding.SetModelName(_settings.TextEmbedding.Model); + var collectionName = _settings.Default.CollectionName ?? KnowledgeCollectionName.BotSharp; + var embedding = KnowledgeSettingUtility.GetTextEmbeddingSetting(_services, collectionName); var vector = await embedding.GetVectorsAsync(new List { @@ -26,7 +26,6 @@ public class MemorizeKnowledgeFn : IFunctionCallback }); var vectorDb = _services.GetServices().FirstOrDefault(x => x.Name == _settings.VectorDb); - var collectionName = !string.IsNullOrWhiteSpace(_settings.DefaultCollection) ? _settings.DefaultCollection : KnowledgeCollectionName.BotSharp; await vectorDb.CreateCollection(collectionName, vector[0].Length); var result = await vectorDb.Upsert(collectionName, Guid.NewGuid(), vector[0], diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs index 27baea0f..213825b5 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs @@ -34,8 +34,8 @@ public class KnowledgeBasePlugin : IBotSharpPlugin { SubMenu = new List { - new PluginMenuDef("Q & A", link: "page/knowledge-base/vector"), - new PluginMenuDef("Relations", link: "page/knowledge-base/graph") + new PluginMenuDef("Q & A", link: "page/knowledge-base/question-answer"), + new PluginMenuDef("Relations", link: "page/knowledge-base/relations") } }); return true; diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/MemVecDb/MemoryVectorDb.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/MemVecDb/MemoryVectorDb.cs index 05677c2f..0d4be819 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/MemVecDb/MemoryVectorDb.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/MemVecDb/MemoryVectorDb.cs @@ -1,5 +1,3 @@ -using BotSharp.Abstraction.VectorStorage.Models; -using BotSharp.Plugin.KnowledgeBase.Utilities; using Tensorflow.NumPy; namespace BotSharp.Plugin.KnowledgeBase.MemVecDb; @@ -23,7 +21,13 @@ public class MemoryVectorDb : IVectorDb return _collections.Select(x => x.Key).ToList(); } - public Task> GetCollectionData(string collectionName, VectorFilter filter) + public Task> GetPagedCollectionData(string collectionName, VectorFilter filter) + { + throw new NotImplementedException(); + } + + public Task> GetCollectionData(string collectionName, IEnumerable ids, + bool withPayload = false, bool withVector = false) { throw new NotImplementedException(); } diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Create.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Create.cs index 391fb5fa..9b185867 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Create.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Create.cs @@ -13,9 +13,9 @@ public partial class KnowledgeService }); var db = GetVectorDb(); - var textEmbedding = GetTextEmbedding(); + var textEmbedding = GetTextEmbedding(collectionName); - await db.CreateCollection(collectionName, textEmbedding.Dimension); + await db.CreateCollection(collectionName, textEmbedding.GetDimension()); foreach (var line in lines) { var vec = await textEmbedding.GetVectorAsync(line); @@ -24,4 +24,27 @@ public partial class KnowledgeService Console.WriteLine($"Saved vector {index}/{lines.Count}: {line}\n"); } } + + public async Task 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; + } + } } diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Delete.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Delete.cs index 3cc72ae2..e78990a7 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Delete.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Delete.cs @@ -16,7 +16,7 @@ public partial class KnowledgeService } catch (Exception ex) { - _logger.LogWarning($"Error when deleting knowledge collection data ({collectionName}-{id}). {ex.Message}\r\n{ex.InnerException}"); + _logger.LogWarning($"Error when deleting vector collection data ({collectionName}-{id}). {ex.Message}\r\n{ex.InnerException}"); return false; } } diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs index 4e001954..ee36780e 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs @@ -1,5 +1,4 @@ using BotSharp.Abstraction.Graph.Models; -using BotSharp.Abstraction.VectorStorage.Models; namespace BotSharp.Plugin.KnowledgeBase.Services; @@ -14,17 +13,17 @@ public partial class KnowledgeService } catch (Exception ex) { - _logger.LogWarning($"Error when getting knowledge collections. {ex.Message}\r\n{ex.InnerException}"); + _logger.LogWarning($"Error when getting vector db collections. {ex.Message}\r\n{ex.InnerException}"); return Enumerable.Empty(); } } - public async Task> GetVectorCollectionData(string collectionName, VectorFilter filter) + public async Task> GetPagedVectorCollectionData(string collectionName, VectorFilter filter) { try { var db = GetVectorDb(); - var pagedResult = await db.GetCollectionData(collectionName, filter); + var pagedResult = await db.GetPagedCollectionData(collectionName, filter); return new StringIdPagedItems { Count = pagedResult.Count, @@ -34,7 +33,7 @@ public partial class KnowledgeService } catch (Exception ex) { - _logger.LogWarning($"Error when getting knowledge collection data ({collectionName}). {ex.Message}\r\n{ex.InnerException}"); + _logger.LogWarning($"Error when getting vector knowledge collection data ({collectionName}). {ex.Message}\r\n{ex.InnerException}"); return new StringIdPagedItems(); } } @@ -43,7 +42,7 @@ public partial class KnowledgeService { try { - var textEmbedding = GetTextEmbedding(); + var textEmbedding = GetTextEmbedding(collectionName); var vector = await textEmbedding.GetVectorAsync(query); // Vector search @@ -55,7 +54,7 @@ public partial class KnowledgeService } catch (Exception ex) { - _logger.LogWarning($"Error when searching knowledge ({collectionName}). {ex.Message}\r\n{ex.InnerException}"); + _logger.LogWarning($"Error when searching vector knowledge ({collectionName}). {ex.Message}\r\n{ex.InnerException}"); return new List(); } } @@ -73,7 +72,7 @@ public partial class KnowledgeService } catch (Exception ex) { - _logger.LogWarning($"Error when searching graph {query}. {ex.Message}\r\n{ex.InnerException}"); + _logger.LogWarning($"Error when searching graph knowledge (Query: {query}). {ex.Message}\r\n{ex.InnerException}"); return new GraphSearchResult(); } } @@ -82,7 +81,7 @@ public partial class KnowledgeService { try { - var textEmbedding = GetTextEmbedding(); + var textEmbedding = GetTextEmbedding(collectionName); var vector = await textEmbedding.GetVectorAsync(query); var vectorDb = GetVectorDb(); @@ -99,7 +98,7 @@ public partial class KnowledgeService } catch (Exception ex) { - _logger.LogWarning($"Error when searching knowledge (vector collection: {collectionName}) {query}. {ex.Message}\r\n{ex.InnerException}"); + _logger.LogWarning($"Error when searching knowledge (Vector collection: {collectionName}) (Query: {query}). {ex.Message}\r\n{ex.InnerException}"); return new KnowledgeSearchResult(); } } diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Update.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Update.cs new file mode 100644 index 00000000..0fa1f5cd --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Update.cs @@ -0,0 +1,31 @@ +namespace BotSharp.Plugin.KnowledgeBase.Services; + +public partial class KnowledgeService +{ + public async Task 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 }); + 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; + } + } +} diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.cs index 1f96042b..eec49891 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.cs @@ -31,13 +31,8 @@ public partial class KnowledgeService : IKnowledgeService return db; } - private ITextEmbedding GetTextEmbedding() + private ITextEmbedding GetTextEmbedding(string collection) { - var embedding = _services.GetServices().FirstOrDefault(x => x.Provider == _settings.TextEmbedding.Provider); - if (embedding != null) - { - embedding.SetModelName(_settings.TextEmbedding.Model); - } - return embedding; + return KnowledgeSettingUtility.GetTextEmbeddingSetting(_services, collection); } } diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Using.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Using.cs index 296d56b5..7eeea4f0 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Using.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Using.cs @@ -19,6 +19,7 @@ global using BotSharp.Abstraction.Graph; global using BotSharp.Abstraction.Knowledges.Settings; global using BotSharp.Abstraction.Knowledges.Enums; global using BotSharp.Abstraction.VectorStorage; +global using BotSharp.Abstraction.VectorStorage.Models; global using BotSharp.Abstraction.Knowledges.Models; global using BotSharp.Abstraction.MLTasks; global using BotSharp.Abstraction.Functions; @@ -31,4 +32,5 @@ global using BotSharp.Abstraction.Agents.Models; global using BotSharp.Abstraction.Functions.Models; global using BotSharp.Abstraction.Repositories; global using BotSharp.Plugin.KnowledgeBase.Services; -global using BotSharp.Plugin.KnowledgeBase.Enum; \ No newline at end of file +global using BotSharp.Plugin.KnowledgeBase.Enum; +global using BotSharp.Plugin.KnowledgeBase.Utilities; \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Utilities/KnowledgeSettingUtility.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Utilities/KnowledgeSettingUtility.cs new file mode 100644 index 00000000..770e1efa --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Utilities/KnowledgeSettingUtility.cs @@ -0,0 +1,19 @@ +namespace BotSharp.Plugin.KnowledgeBase.Utilities; + +public static class KnowledgeSettingUtility +{ + 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); + embedding.SetModelName(found.Model); + embedding.SetDimension(found.Dimension); + return embedding; + } +} diff --git a/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/TextEmbeddingProvider.cs b/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/TextEmbeddingProvider.cs index a2c39d2c..a9288064 100644 --- a/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/TextEmbeddingProvider.cs +++ b/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/TextEmbeddingProvider.cs @@ -7,7 +7,9 @@ public class TextEmbeddingProvider : ITextEmbedding private LLamaEmbedder _embedder; private readonly LlamaSharpSettings _settings; private readonly IServiceProvider _services; - public int Dimension { get; set; } = 4096; + private const int DEFAULT_DIMENSION = 4096; + + protected int _dimension = DEFAULT_DIMENSION; public string Provider => "llama-sharp"; @@ -36,4 +38,14 @@ public class TextEmbeddingProvider : ITextEmbedding } public void SetModelName(string model) { } + + public void SetDimension(int dimension) + { + _dimension = dimension > 0 ? dimension : DEFAULT_DIMENSION; + } + + public int GetDimension() + { + return _dimension; + } } diff --git a/src/Plugins/BotSharp.Plugin.MetaAI/Providers/FaissDb.cs b/src/Plugins/BotSharp.Plugin.MetaAI/Providers/FaissDb.cs index 034f5329..902087d7 100644 --- a/src/Plugins/BotSharp.Plugin.MetaAI/Providers/FaissDb.cs +++ b/src/Plugins/BotSharp.Plugin.MetaAI/Providers/FaissDb.cs @@ -16,7 +16,13 @@ public class FaissDb : IVectorDb throw new NotImplementedException(); } - public Task> GetCollectionData(string collectionName, VectorFilter filter) + public Task> GetPagedCollectionData(string collectionName, VectorFilter filter) + { + throw new NotImplementedException(); + } + + public Task> GetCollectionData(string collectionName, IEnumerable ids, + bool withPayload = false, bool withVector = false) { throw new NotImplementedException(); } diff --git a/src/Plugins/BotSharp.Plugin.MetaAI/Providers/fastTextEmbeddingProvider.cs b/src/Plugins/BotSharp.Plugin.MetaAI/Providers/fastTextEmbeddingProvider.cs index 4de0c5aa..7d9c6141 100644 --- a/src/Plugins/BotSharp.Plugin.MetaAI/Providers/fastTextEmbeddingProvider.cs +++ b/src/Plugins/BotSharp.Plugin.MetaAI/Providers/fastTextEmbeddingProvider.cs @@ -14,19 +14,7 @@ public class fastTextEmbeddingProvider : ITextEmbedding private FastTextWrapper _fastText; private readonly IServiceProvider _services; - private int dimension; - public int Dimension - { - get - { - LoadModel(); - return _fastText.GetModelDimension(); - } - set - { - dimension = value; - } - } + private int _dimension; public string Provider => "meta-ai"; @@ -73,4 +61,15 @@ public class fastTextEmbeddingProvider : ITextEmbedding } public void SetModelName(string model) { } + + public void SetDimension(int dimension) + { + LoadModel(); + _dimension = _fastText.GetModelDimension(); + } + + public int GetDimension() + { + return _dimension; + } } diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Embedding/TextEmbeddingProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Embedding/TextEmbeddingProvider.cs index b86014a4..bebf6853 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Embedding/TextEmbeddingProvider.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Embedding/TextEmbeddingProvider.cs @@ -10,11 +10,10 @@ public class TextEmbeddingProvider : ITextEmbedding private const int DEFAULT_DIMENSION = 3072; protected string _model = "text-embedding-3-large"; + protected int _dimension = DEFAULT_DIMENSION; public virtual string Provider => "openai"; - public int Dimension { get; set; } - public TextEmbeddingProvider( OpenAiSettings settings, ILogger logger, @@ -50,24 +49,26 @@ public class TextEmbeddingProvider : ITextEmbedding _model = model; } + public void SetDimension(int dimension) + { + _dimension = dimension > 0 ? dimension : DEFAULT_DIMENSION; + } + + public int GetDimension() + { + return _dimension; + } + private EmbeddingGenerationOptions PrepareOptions() { return new EmbeddingGenerationOptions { - Dimensions = GetDimension() + Dimensions = GetDimensionOption() }; } - private int GetDimension() + private int GetDimensionOption() { - var state = _services.GetRequiredService(); - var stateDimension = state.GetState("embedding_dimension"); - var defaultDimension = Dimension > 0 ? Dimension : DEFAULT_DIMENSION; - - if (int.TryParse(stateDimension, out var dimension)) - { - return dimension > 0 ? dimension : defaultDimension; - } - return defaultDimension; + return _dimension > 0 ? _dimension : DEFAULT_DIMENSION; } } \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs b/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs index f9071ba2..99d917d4 100644 --- a/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs +++ b/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs @@ -42,7 +42,7 @@ public class QdrantDb : IVectorDb return collections.ToList(); } - public async Task> GetCollectionData(string collectionName, VectorFilter filter) + public async Task> GetPagedCollectionData(string collectionName, VectorFilter filter) { var client = GetClient(); var exist = await DoesCollectionExist(client, collectionName); @@ -70,6 +70,29 @@ public class QdrantDb : IVectorDb }; } + + public async Task> GetCollectionData(string collectionName, IEnumerable ids, + bool withPayload = false, bool withVector = false) + { + if (ids.IsNullOrEmpty()) return Enumerable.Empty(); + + var client = GetClient(); + var exist = await DoesCollectionExist(client, collectionName); + if (!exist) + { + return Enumerable.Empty(); + } + + var pointIds = ids.Select(x => new PointId { Uuid = x.ToString() }).Distinct().ToList(); + var points = await client.RetrieveAsync(collectionName, pointIds, withPayload, withVector); + return points.Select(x => new VectorCollectionData + { + Id = x.Id?.Uuid ?? string.Empty, + Data = x.Payload?.ToDictionary(x => x.Key, x => x.Value.StringValue) ?? new(), + Vector = x.Vectors?.Vector?.Data?.ToArray() + }); + } + public async Task CreateCollection(string collectionName, int dim) { var client = GetClient(); diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/IntentClassifier.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/IntentClassifier.cs index c52baf59..e28eb8b6 100644 --- a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/IntentClassifier.cs +++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/IntentClassifier.cs @@ -57,12 +57,13 @@ public class IntentClassifier return; } - var vector = _services.GetServices().FirstOrDefault(x => x.Provider == _knowledgeBaseSettings.TextEmbedding.Provider); - vector.SetModelName(_knowledgeBaseSettings.TextEmbedding.Model); + var embedding = _services.GetServices().FirstOrDefault(x => x.Provider == _knowledgeBaseSettings.Default.TextEmbedding.Provider); + embedding.SetModelName(_knowledgeBaseSettings.Default.TextEmbedding.Model); + embedding.SetDimension(_knowledgeBaseSettings.Default.TextEmbedding.Dimension); var layers = new List { - keras.layers.InputLayer((vector.Dimension), name: "Input"), + keras.layers.InputLayer((embedding.GetDimension()), name: "Input"), keras.layers.Dense(256, activation:"relu"), keras.layers.Dense(256, activation:"relu"), keras.layers.Dense(GetLabels().Length, activation: keras.activations.Softmax) @@ -136,10 +137,11 @@ public class IntentClassifier public NDArray GetTextEmbedding(string text) { var knowledgeSettings = _services.GetRequiredService(); - var embedding = _services.GetServices() .FirstOrDefault(x => x.Provider == knowledgeSettings.TextEmbedding.Provider); - embedding.SetModelName(knowledgeSettings.TextEmbedding.Model); + var embedding = _services.GetServices().FirstOrDefault(x => x.Provider == knowledgeSettings.Default.TextEmbedding.Provider); + embedding.SetModelName(knowledgeSettings.Default.TextEmbedding.Model); + embedding.SetDimension(_knowledgeBaseSettings.Default.TextEmbedding.Dimension); - var x = np.zeros((1, embedding.Dimension), dtype: np.float32); + var x = np.zeros((1, embedding.GetDimension()), dtype: np.float32); x[0] = embedding.GetVectorAsync(text).GetAwaiter().GetResult(); return x; } @@ -186,7 +188,7 @@ public class IntentClassifier // Sort label to keep the same order var uniqueLabelList = labelList.Distinct().OrderBy(x => x).ToArray(); - var x = np.zeros((vectorList.Count, vector.Dimension), dtype: np.float32); + var x = np.zeros((vectorList.Count, vector.GetDimension()), dtype: np.float32); var y = np.zeros((vectorList.Count, 1), dtype: np.float32); for (int i = 0; i < vectorList.Count; i++) diff --git a/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelMemoryStoreProvider.cs b/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelMemoryStoreProvider.cs index 2cab2835..a797b47f 100644 --- a/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelMemoryStoreProvider.cs +++ b/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelMemoryStoreProvider.cs @@ -29,11 +29,17 @@ namespace BotSharp.Plugin.SemanticKernel await _memoryStore.CreateCollectionAsync(collectionName); } - public Task> GetCollectionData(string collectionName, VectorFilter filter) + public Task> GetPagedCollectionData(string collectionName, VectorFilter filter) { throw new System.NotImplementedException(); } + public Task> GetCollectionData(string collectionName, IEnumerable ids, + bool withPayload = false, bool withVector = false) + { + throw new NotImplementedException(); + } + public async Task> GetCollections() { var result = new List(); diff --git a/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelTextEmbeddingProvider.cs b/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelTextEmbeddingProvider.cs index 79c83f37..5486d972 100644 --- a/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelTextEmbeddingProvider.cs +++ b/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelTextEmbeddingProvider.cs @@ -24,13 +24,13 @@ namespace BotSharp.Plugin.SemanticKernel public SemanticKernelTextEmbeddingProvider(ITextEmbeddingGenerationService embedding, IConfiguration configuration) #pragma warning restore SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. { - this._embedding = embedding; - this._configuration = configuration; - this.Dimension = configuration.GetValue("SemanticKernel:Dimension"); + _embedding = embedding; + _configuration = configuration; + _dimension = configuration.GetValue("SemanticKernel:Dimension"); } /// - public int Dimension { get; set; } + protected int _dimension; public string Provider => "semantic-kernel"; @@ -51,5 +51,15 @@ namespace BotSharp.Plugin.SemanticKernel } public void SetModelName(string model) { } + + public void SetDimension(int dimension) + { + _dimension = dimension > 0 ? dimension : _configuration.GetValue("SemanticKernel:Dimension"); + } + + public int GetDimension() + { + return _dimension; + } } } diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index 2c634959..c34fc791 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -264,11 +264,24 @@ "KnowledgeBase": { "VectorDb": "Qdrant", "GraphDb": "Default", - "DefaultCollection": "BotSharp", - "TextEmbedding": { - "Provider": "openai", - "Model": "text-embedding-3-small" - } + "Default": { + "CollectionName": "BotSharp", + "TextEmbedding": { + "Provider": "openai", + "Model": "text-embedding-3-small", + "Dimension": 1536 + } + }, + "Collections": [ + { + "Name": "BotSharp", + "TextEmbedding": { + "Provider": "openai", + "Model": "text-embedding-3-small", + "Dimension": 1536 + } + } + ] }, "SparkDesk": {