From 43dfc9b64248e58505d0a4c86b0c735dffa03eb4 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Sat, 17 Jun 2023 21:56:22 -0500 Subject: [PATCH] Abstract Vector database. --- .../BotSharp.Plugin.Qdrant.csproj | 16 +++++ BotSharp.Plugin.Qdrant/QdrantDb.cs | 64 +++++++++++++++++++ BotSharp.Plugin.Qdrant/QdrantPlugin.cs | 18 ++++++ BotSharp.Plugin.Qdrant/QdrantSetting.cs | 7 ++ BotSharp.sln | 14 ++++ .../Knowledges/IKnowledgeBase.cs | 5 -- .../Knowledges/IVectorDb.cs | 9 +++ .../BotSharp.Core/BotSharp.Core.csproj | 1 - .../BotSharpServiceCollectionExtensions.cs | 12 ---- .../BotSharp.Core/Knowledges/KnowledgeBase.cs | 23 ++++++- .../Knowledges/Services/KnowledgeService.cs | 50 +++------------ .../BotSharp.Core/Users/UserController.cs | 1 - src/Infrastructure/BotSharp.Core/Using.cs | 14 +++- .../BotSharp.Plugin.MetaAI.csproj | 7 +- .../BotSharp.Plugin.MetaAI/MetaAiPlugin.cs | 2 + .../Providers/FaissDb.cs | 29 +++++++++ .../Providers/fastTextEmbeddingProvider.cs | 6 ++ src/WebStarter/WebStarter.csproj | 1 + src/WebStarter/appsettings.json | 8 ++- 19 files changed, 215 insertions(+), 72 deletions(-) create mode 100644 BotSharp.Plugin.Qdrant/BotSharp.Plugin.Qdrant.csproj create mode 100644 BotSharp.Plugin.Qdrant/QdrantDb.cs create mode 100644 BotSharp.Plugin.Qdrant/QdrantPlugin.cs create mode 100644 BotSharp.Plugin.Qdrant/QdrantSetting.cs delete mode 100644 src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeBase.cs create mode 100644 src/Infrastructure/BotSharp.Abstraction/Knowledges/IVectorDb.cs create mode 100644 src/Plugins/BotSharp.Plugin.MetaAI/Providers/FaissDb.cs diff --git a/BotSharp.Plugin.Qdrant/BotSharp.Plugin.Qdrant.csproj b/BotSharp.Plugin.Qdrant/BotSharp.Plugin.Qdrant.csproj new file mode 100644 index 00000000..921dcdcf --- /dev/null +++ b/BotSharp.Plugin.Qdrant/BotSharp.Plugin.Qdrant.csproj @@ -0,0 +1,16 @@ + + + + netstandard2.1 + enable + 10 + + + + + + + + + + diff --git a/BotSharp.Plugin.Qdrant/QdrantDb.cs b/BotSharp.Plugin.Qdrant/QdrantDb.cs new file mode 100644 index 00000000..2846cf85 --- /dev/null +++ b/BotSharp.Plugin.Qdrant/QdrantDb.cs @@ -0,0 +1,64 @@ +using BotSharp.Abstraction.Knowledges; +using QdrantCSharp; +using QdrantCSharp.Enums; +using QdrantCSharp.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace BotSharp.Plugin.Qdrant; + +public class QdrantDb : IVectorDb +{ + private readonly QdrantHttpClient _client; + private readonly QdrantSetting _setting; + public QdrantDb(QdrantSetting setting) + { + _setting = setting; + _client = new QdrantHttpClient + ( + url: _setting.Url, + apiKey: _setting.ApiKey + ); + } + + public async Task> GetCollections() + { + // List all the collections + var collections = await _client.GetCollections(); + return collections.Result.Collections.Select(x => x.Name).ToList(); + } + + public async Task CreateCollection(string collectionName) + { + var collections = await GetCollections(); + if (!collections.Contains(collectionName)) + { + // Create a new collection + await _client.CreateCollection(collectionName, new VectorParams(size: 300, distance: Distance.COSINE)); + } + + // Get collection info + var collectionInfo = await _client.GetCollection(collectionName); + if(collectionInfo == null) + { + throw new Exception($"Create {collectionName} failed."); + } + } + + public async Task Upsert(string collectionName, int id, float[] vector) + { + // Insert vectors + await _client.Upsert(collectionName, points: new List + { + new PointStruct(id: id, vector: vector) + }); + } + + public async Task> Search(string collectionName, float[] vector, int limit = 10) + { + var result = await _client.Search(collectionName, vector, limit); + return result.Result.Select(x => x.Id).ToList(); + } +} diff --git a/BotSharp.Plugin.Qdrant/QdrantPlugin.cs b/BotSharp.Plugin.Qdrant/QdrantPlugin.cs new file mode 100644 index 00000000..6d372215 --- /dev/null +++ b/BotSharp.Plugin.Qdrant/QdrantPlugin.cs @@ -0,0 +1,18 @@ +using BotSharp.Abstraction.Knowledges; +using BotSharp.Abstraction.Plugins; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace BotSharp.Plugin.Qdrant; + +public class QdrantPlugin : IBotSharpPlugin +{ + public void RegisterDI(IServiceCollection services, IConfiguration config) + { + var settings = new QdrantSetting(); + config.Bind("Qdrant", settings); + services.AddSingleton(x => settings); + + services.AddSingleton(); + } +} diff --git a/BotSharp.Plugin.Qdrant/QdrantSetting.cs b/BotSharp.Plugin.Qdrant/QdrantSetting.cs new file mode 100644 index 00000000..eed08792 --- /dev/null +++ b/BotSharp.Plugin.Qdrant/QdrantSetting.cs @@ -0,0 +1,7 @@ +namespace BotSharp.Plugin.Qdrant; + +public class QdrantSetting +{ + public string Url { get; set; } + public string ApiKey { get; set; } +} diff --git a/BotSharp.sln b/BotSharp.sln index 1f03d0eb..154f8b3c 100644 --- a/BotSharp.sln +++ b/BotSharp.sln @@ -35,6 +35,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTest", "tests\UnitTest\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.MetaAI", "src\Plugins\BotSharp.Plugin.MetaAI\BotSharp.Plugin.MetaAI.csproj", "{6D8D18A9-86D7-455E-81EC-9682C30AB7E7}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Databases", "Databases", "{53E7CD86-0D19-40D9-A0FA-AB4613837E89}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.Qdrant", "BotSharp.Plugin.Qdrant\BotSharp.Plugin.Qdrant.csproj", "{9686D771-CA9F-41B0-8508-C5B4E6E457A3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -123,6 +127,14 @@ Global {6D8D18A9-86D7-455E-81EC-9682C30AB7E7}.Release|Any CPU.Build.0 = Release|Any CPU {6D8D18A9-86D7-455E-81EC-9682C30AB7E7}.Release|x64.ActiveCfg = Release|Any CPU {6D8D18A9-86D7-455E-81EC-9682C30AB7E7}.Release|x64.Build.0 = Release|Any CPU + {9686D771-CA9F-41B0-8508-C5B4E6E457A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9686D771-CA9F-41B0-8508-C5B4E6E457A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9686D771-CA9F-41B0-8508-C5B4E6E457A3}.Debug|x64.ActiveCfg = Debug|Any CPU + {9686D771-CA9F-41B0-8508-C5B4E6E457A3}.Debug|x64.Build.0 = Debug|Any CPU + {9686D771-CA9F-41B0-8508-C5B4E6E457A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9686D771-CA9F-41B0-8508-C5B4E6E457A3}.Release|Any CPU.Build.0 = Release|Any CPU + {9686D771-CA9F-41B0-8508-C5B4E6E457A3}.Release|x64.ActiveCfg = Release|Any CPU + {9686D771-CA9F-41B0-8508-C5B4E6E457A3}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -140,6 +152,8 @@ Global {2323A7A3-E938-488D-A57E-638638054BC4} = {64264688-0F5C-4AB0-8F2B-B59B717CCE00} {0B6E1D7F-ABDE-47F6-8B2D-4483C2CFF2D6} = {32FAFFFE-A4CB-4FEE-BF7C-84518BBC6DCC} {6D8D18A9-86D7-455E-81EC-9682C30AB7E7} = {D5293208-2BEF-42FC-A64C-5954F61720BA} + {53E7CD86-0D19-40D9-A0FA-AB4613837E89} = {2635EC9B-2E5F-4313-AC21-0B847F31F36C} + {9686D771-CA9F-41B0-8508-C5B4E6E457A3} = {53E7CD86-0D19-40D9-A0FA-AB4613837E89} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19} diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeBase.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeBase.cs deleted file mode 100644 index 840e625e..00000000 --- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeBase.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace BotSharp.Abstraction.Knowledges; - -public interface IKnowledgeBase -{ -} diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IVectorDb.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IVectorDb.cs new file mode 100644 index 00000000..7fe53c75 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IVectorDb.cs @@ -0,0 +1,9 @@ +namespace BotSharp.Abstraction.Knowledges; + +public interface IVectorDb +{ + Task> GetCollections(); + Task CreateCollection(string collectionName); + Task Upsert(string collectionName, int id, float[] vector); + Task> Search(string collectionName, float[] vector, int limit = 10); +} diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index 4944dfce..cdbe9243 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -67,7 +67,6 @@ - diff --git a/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs b/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs index 8214df65..82c09c38 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs +++ b/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs @@ -1,15 +1,3 @@ -using BotSharp.Abstraction.Agents; -using BotSharp.Abstraction.Conversations; -using BotSharp.Abstraction.Infrastructures.ContentTransmitters; -using BotSharp.Abstraction.Knowledges; -using BotSharp.Abstraction.Users; -using BotSharp.Core.Agents.Services; -using BotSharp.Core.Conversations.Services; -using BotSharp.Core.Infrastructures; -using BotSharp.Core.Knowledges.Services; -using BotSharp.Core.Plugins; -using BotSharp.Core.Users.Services; -using BotSharp.Plugins.LLamaSharp; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; diff --git a/src/Infrastructure/BotSharp.Core/Knowledges/KnowledgeBase.cs b/src/Infrastructure/BotSharp.Core/Knowledges/KnowledgeBase.cs index 1b074d59..5b0335c2 100644 --- a/src/Infrastructure/BotSharp.Core/Knowledges/KnowledgeBase.cs +++ b/src/Infrastructure/BotSharp.Core/Knowledges/KnowledgeBase.cs @@ -1,7 +1,24 @@ -using BotSharp.Abstraction.Knowledges; - namespace BotSharp.Core.Knowledges; -public class KnowledgeBase : IKnowledgeBase +public class KnowledgeBase : IVectorDb { + public Task CreateCollection(string collectionName) + { + throw new NotImplementedException(); + } + + public Task> GetCollections() + { + throw new NotImplementedException(); + } + + public Task> Search(string collectionName, float[] vector, int limit = 10) + { + throw new NotImplementedException(); + } + + public Task Upsert(string collectionName, int id, float[] vector) + { + throw new NotImplementedException(); + } } diff --git a/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.cs b/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.cs index db2b7378..81ba2d26 100644 --- a/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.cs +++ b/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.cs @@ -1,8 +1,5 @@ using BotSharp.Abstraction.Knowledges; using BotSharp.Abstraction.Knowledges.Models; -using QdrantCSharp.Enums; -using QdrantCSharp.Models; -using QdrantCSharp; using System.IO; using System.Collections; using BotSharp.Abstraction.MLTasks; @@ -13,37 +10,19 @@ public class KnowledgeService : IKnowledgeService { private readonly ITextEmbedding _textEmbedding; private readonly ITextCompletion _textCompletion; + private readonly IVectorDb _db; string collectionName = "my_collection"; - public KnowledgeService(ITextEmbedding textEmbedding, ITextCompletion textCompletion) + public KnowledgeService(ITextEmbedding textEmbedding, + ITextCompletion textCompletion, + IVectorDb db) { _textEmbedding = textEmbedding; _textCompletion = textCompletion; - } - - public QdrantHttpClient GetClient() - { - var client = new QdrantHttpClient - ( - url: "", - apiKey: "" - ); - return client; + _db = db; } public async Task Feed(KnowledgeFeedModel knowledge) { - var client = GetClient(); - - // List all the collections - var collections = await client.GetCollections(); - if (!collections.Result.Collections.Select(x => x.Name).Contains(collectionName)) - { - // Create a new collection - await client.CreateCollection(collectionName, new VectorParams(size: 300, distance: Distance.COSINE)); - } - - // Get collection info - var collectionInfo = await client.GetCollection(collectionName); var idStart = 0; var lines = knowledge.Content.Split(". "); lines = lines.Select((x, i) => $"{i+1} {x}").ToArray(); @@ -52,33 +31,22 @@ public class KnowledgeService : IKnowledgeService foreach (var line in lines) { idStart++; - - // Insert vectors - /*await client.Upsert(collectionName, points: new List - { - new PointStruct(id: idStart, vector: _textEmbedding.GetVector(line)) - });*/ + await _db.Upsert(collectionName, idStart, _textEmbedding.GetVector(line)); } } public async Task GetAnswer(string question) { - var client = GetClient(); var vector = _textEmbedding.GetVector(question); // Vector search - var result = await client.Search - ( - collectionName, - vector, - limit: 10 - ); + var result = await _db.Search(collectionName, vector); var prompt = ""; var lines = File.ReadAllLines(collectionName + ".txt"); - foreach (var r in result.Result) + foreach (var r in result) { - prompt += lines[r.Id - 1] + "\n"; + prompt += lines[r - 1] + "\n"; } prompt += "###\r\n"; diff --git a/src/Infrastructure/BotSharp.Core/Users/UserController.cs b/src/Infrastructure/BotSharp.Core/Users/UserController.cs index 6be3be23..c03d82bc 100644 --- a/src/Infrastructure/BotSharp.Core/Users/UserController.cs +++ b/src/Infrastructure/BotSharp.Core/Users/UserController.cs @@ -1,5 +1,4 @@ using BotSharp.Abstraction.ApiAdapters; -using BotSharp.Abstraction.Users; using BotSharp.Abstraction.Users.Models; using BotSharp.Core.Users.ViewModels; using Microsoft.AspNetCore.Authorization; diff --git a/src/Infrastructure/BotSharp.Core/Using.cs b/src/Infrastructure/BotSharp.Core/Using.cs index 0e2fb894..4244f10d 100644 --- a/src/Infrastructure/BotSharp.Core/Using.cs +++ b/src/Infrastructure/BotSharp.Core/Using.cs @@ -6,7 +6,17 @@ global using System.Linq; global using Microsoft.Extensions.DependencyInjection; global using BotSharp.Abstraction.Plugins; global using EntityFrameworkCore.BootKit; -global using BotSharp.Abstraction; +global using BotSharp.Abstraction.Agents; +global using BotSharp.Abstraction.Conversations; +global using BotSharp.Abstraction.Infrastructures.ContentTransmitters; +global using BotSharp.Abstraction.Knowledges; +global using BotSharp.Abstraction.Users; global using BotSharp.Core.Repository; global using BotSharp.Core.Repository.Abstraction; -global using BotSharp.Core.Repository.DbTables; \ No newline at end of file +global using BotSharp.Core.Repository.DbTables; +global using BotSharp.Core.Agents.Services; +global using BotSharp.Core.Conversations.Services; +global using BotSharp.Core.Infrastructures; +global using BotSharp.Core.Knowledges.Services; +global using BotSharp.Core.Plugins; +global using BotSharp.Core.Users.Services; \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.MetaAI/BotSharp.Plugin.MetaAI.csproj b/src/Plugins/BotSharp.Plugin.MetaAI/BotSharp.Plugin.MetaAI.csproj index 4183f4bc..5ea98423 100644 --- a/src/Plugins/BotSharp.Plugin.MetaAI/BotSharp.Plugin.MetaAI.csproj +++ b/src/Plugins/BotSharp.Plugin.MetaAI/BotSharp.Plugin.MetaAI.csproj @@ -6,17 +6,12 @@ 10 - - - - - - + diff --git a/src/Plugins/BotSharp.Plugin.MetaAI/MetaAiPlugin.cs b/src/Plugins/BotSharp.Plugin.MetaAI/MetaAiPlugin.cs index 4d3ec9a5..e7437837 100644 --- a/src/Plugins/BotSharp.Plugin.MetaAI/MetaAiPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.MetaAI/MetaAiPlugin.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.Knowledges; using BotSharp.Abstraction.MLTasks; using BotSharp.Abstraction.Plugins; using BotSharp.Plugin.MetaAI.Providers; @@ -18,5 +19,6 @@ public class MetaAiPlugin : IBotSharpPlugin services.AddSingleton(x => settings.fastText); services.AddSingleton(); + services.AddSingleton(); } } diff --git a/src/Plugins/BotSharp.Plugin.MetaAI/Providers/FaissDb.cs b/src/Plugins/BotSharp.Plugin.MetaAI/Providers/FaissDb.cs new file mode 100644 index 00000000..6110fbe5 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.MetaAI/Providers/FaissDb.cs @@ -0,0 +1,29 @@ +using BotSharp.Abstraction.Knowledges; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace BotSharp.Plugin.MetaAI.Providers; + +public class FaissDb : IVectorDb +{ + public Task CreateCollection(string collectionName) + { + throw new NotImplementedException(); + } + + public Task> GetCollections() + { + throw new NotImplementedException(); + } + + public Task> Search(string collectionName, float[] vector, int limit = 10) + { + throw new NotImplementedException(); + } + + public Task Upsert(string collectionName, int id, float[] vector) + { + throw new NotImplementedException(); + } +} diff --git a/src/Plugins/BotSharp.Plugin.MetaAI/Providers/fastTextEmbeddingProvider.cs b/src/Plugins/BotSharp.Plugin.MetaAI/Providers/fastTextEmbeddingProvider.cs index 0e2a27a1..1d0c5b1e 100644 --- a/src/Plugins/BotSharp.Plugin.MetaAI/Providers/fastTextEmbeddingProvider.cs +++ b/src/Plugins/BotSharp.Plugin.MetaAI/Providers/fastTextEmbeddingProvider.cs @@ -1,6 +1,7 @@ using BotSharp.Abstraction.MLTasks; using BotSharp.Plugin.MetaAI.Settings; using FastText.NetWrapper; +using System.IO; namespace BotSharp.Plugin.MetaAI.Providers; @@ -14,6 +15,11 @@ public class fastTextEmbeddingProvider : ITextEmbedding _settings = settings; _fastText = new FastTextWrapper(); + if (!File.Exists(settings.ModelPath)) + { + throw new FileNotFoundException($"Can't load pre-trained word vectors from {settings.ModelPath}.\n Try to download from https://fasttext.cc/docs/en/english-vectors.html."); + } + if (!_fastText.IsModelReady()) { _fastText.LoadModel(settings.ModelPath); diff --git a/src/WebStarter/WebStarter.csproj b/src/WebStarter/WebStarter.csproj index 304e16d9..4d8be7ab 100644 --- a/src/WebStarter/WebStarter.csproj +++ b/src/WebStarter/WebStarter.csproj @@ -27,6 +27,7 @@ + diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index f3c049c0..f791ff88 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -48,11 +48,17 @@ "Assemblies": [ "BotSharp.Core" ] }, + "Qdrant": { + "Url": "", + "ApiKey": "" + }, + "PluginLoader": { "Assemblies": [ "BotSharp.Core", "BotSharp.Plugin.AzureOpenAI", - "BotSharp.Plugin.MetaAI" + "BotSharp.Plugin.MetaAI", + "BotSharp.Plugin.Qdrant" ] } }