Merge branch 'SciSharp:master' into master

This commit is contained in:
Haiping 2024-08-16 19:30:05 -05:00 committed by GitHub
commit f95dc6599d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 496 additions and 98 deletions

View file

@ -107,7 +107,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.TencentCos"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Interpreters", "Interpreters", "{C4C59872-3C8A-450D-83D5-2BE402D610D5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.PythonInterpreter", "src\Plugins\BotSharp.Plugin.PythonInterpreter\BotSharp.Plugin.PythonInterpreter.csproj", "{05E6E405-5021-406E-8A5E-0A7CEC881F6D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.PythonInterpreter", "src\Plugins\BotSharp.Plugin.PythonInterpreter\BotSharp.Plugin.PythonInterpreter.csproj", "{05E6E405-5021-406E-8A5E-0A7CEC881F6D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Graph", "Graph", "{97A0B191-64D7-4F8A-BFE8-1BFCC5E247E1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.Graph", "src\Plugins\BotSharp.Plugin.Graph\BotSharp.Plugin.Graph.csproj", "{EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -437,6 +441,14 @@ Global
{05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Release|Any CPU.Build.0 = Release|Any CPU
{05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Release|x64.ActiveCfg = Release|Any CPU
{05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Release|x64.Build.0 = Release|Any CPU
{EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Debug|x64.ActiveCfg = Debug|Any CPU
{EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Debug|x64.Build.0 = Debug|Any CPU
{EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Release|Any CPU.Build.0 = Release|Any CPU
{EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Release|x64.ActiveCfg = Release|Any CPU
{EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -489,6 +501,8 @@ Global
{BF029B0A-768B-43A1-8D91-E70B95505716} = {38B37C0D-1930-4D47-BCBF-E358EC1096B1}
{C4C59872-3C8A-450D-83D5-2BE402D610D5} = {2635EC9B-2E5F-4313-AC21-0B847F31F36C}
{05E6E405-5021-406E-8A5E-0A7CEC881F6D} = {C4C59872-3C8A-450D-83D5-2BE402D610D5}
{97A0B191-64D7-4F8A-BFE8-1BFCC5E247E1} = {2635EC9B-2E5F-4313-AC21-0B847F31F36C}
{EBFE97DA-D0BA-48BA-8B5D-083B60348D1D} = {97A0B191-64D7-4F8A-BFE8-1BFCC5E247E1}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19}

View file

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(TargetFramework)</TargetFramework>

View file

@ -0,0 +1,10 @@
using BotSharp.Abstraction.Graph.Models;
namespace BotSharp.Abstraction.Graph;
public interface IGraphDb
{
public string Name { get; }
Task<GraphSearchData> Search(string query, GraphSearchOptions options);
}

View file

@ -0,0 +1,6 @@
namespace BotSharp.Abstraction.Graph.Models;
public class GraphSearchData
{
public string Result { get; set; }
}

View file

@ -0,0 +1,6 @@
namespace BotSharp.Abstraction.Graph.Models;
public class GraphSearchOptions
{
public string Method { get; set; }
}

View file

@ -0,0 +1,6 @@
namespace BotSharp.Abstraction.Graph.Models;
public class GraphSearchResult
{
public string Result { get; set; }
}

View file

@ -1,10 +1,15 @@
using BotSharp.Abstraction.Graph.Models;
using BotSharp.Abstraction.VectorStorage.Models;
namespace BotSharp.Abstraction.Knowledges;
public interface IKnowledgeService
{
Task<IEnumerable<string>> GetKnowledgeCollections();
Task<IEnumerable<KnowledgeSearchResult>> SearchKnowledge(string collectionName, KnowledgeSearchOptions options);
Task FeedKnowledge(string collectionName, KnowledgeCreationModel model);
Task<StringIdPagedItems<KnowledgeSearchResult>> GetKnowledgeCollectionData(string collectionName, KnowledgeFilter filter);
Task<bool> DeleteKnowledgeCollectionData(string collectionName, string id);
Task<IEnumerable<string>> GetVectorCollections();
Task<IEnumerable<VectorSearchResult>> SearchVectorKnowledge(string query, string collectionName, VectorSearchOptions options);
Task FeedVectorKnowledge(string collectionName, KnowledgeCreationModel model);
Task<StringIdPagedItems<VectorSearchResult>> GetVectorCollectionData(string collectionName, VectorFilter filter);
Task<bool> DeleteVectorCollectionData(string collectionName, string id);
Task<GraphSearchResult> SearchGraphKnowledge(string query, GraphSearchOptions options);
Task<KnowledgeSearchResult> SearchKnowledge(string query, string collectionName, VectorSearchOptions vectorOptions, GraphSearchOptions graphOptions);
}

View file

@ -1,7 +0,0 @@
namespace BotSharp.Abstraction.Knowledges.Models;
public class KnowledgeFilter : StringIdPagination
{
[JsonPropertyName("with_vector")]
public bool WithVector { get; set; }
}

View file

@ -1,20 +1,10 @@
using BotSharp.Abstraction.Graph.Models;
using BotSharp.Abstraction.VectorStorage.Models;
namespace BotSharp.Abstraction.Knowledges.Models;
public class KnowledgeSearchResult : KnowledgeCollectionData
public class KnowledgeSearchResult
{
public KnowledgeSearchResult()
{
}
public static KnowledgeSearchResult CopyFrom(KnowledgeCollectionData data)
{
return new KnowledgeSearchResult
{
Id = data.Id,
Data = data.Data,
Score = data.Score,
Vector = data.Vector
};
}
}
public IEnumerable<VectorSearchResult> VectorResult { get; set; }
public GraphSearchResult GraphResult { get; set; }
}

View file

@ -4,8 +4,9 @@ namespace BotSharp.Abstraction.Knowledges.Settings;
public class KnowledgeBaseSettings
{
public string VectorDb { get; set; }
public string DefaultCollection { get; set; } = KnowledgeCollectionName.BotSharp;
public string VectorDb { get; set; }
public string GraphDb { get; set; }
public KnowledgeModelSetting TextEmbedding { get; set; }
}

View file

@ -1,3 +1,5 @@
using BotSharp.Abstraction.VectorStorage.Models;
namespace BotSharp.Abstraction.VectorStorage;
public interface IVectorDb
@ -5,9 +7,9 @@ public interface IVectorDb
string Name { get; }
Task<IEnumerable<string>> GetCollections();
Task<StringIdPagedItems<KnowledgeCollectionData>> GetCollectionData(string collectionName, KnowledgeFilter filter);
Task<StringIdPagedItems<VectorCollectionData>> GetCollectionData(string collectionName, VectorFilter filter);
Task CreateCollection(string collectionName, int dim);
Task<bool> Upsert(string collectionName, string id, float[] vector, string text, Dictionary<string, string>? payload = null);
Task<IEnumerable<KnowledgeCollectionData>> Search(string collectionName, float[] vector, IEnumerable<string>? fields, int limit = 5, float confidence = 0.5f, bool withVector = false);
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, string id);
}

View file

@ -1,6 +1,6 @@
namespace BotSharp.Abstraction.Knowledges.Models;
namespace BotSharp.Abstraction.VectorStorage.Models;
public class KnowledgeCollectionData
public class VectorCollectionData
{
public string Id { get; set; }
public Dictionary<string, string> Data { get; set; } = new();

View file

@ -0,0 +1,7 @@
namespace BotSharp.Abstraction.VectorStorage.Models;
public class VectorFilter : StringIdPagination
{
[JsonPropertyName("with_vector")]
public bool WithVector { get; set; }
}

View file

@ -1,10 +1,9 @@
using BotSharp.Abstraction.Knowledges.Enums;
namespace BotSharp.Abstraction.Knowledges.Models;
namespace BotSharp.Abstraction.VectorStorage.Models;
public class KnowledgeSearchOptions
public class VectorSearchOptions
{
public string Text { get; set; } = string.Empty;
public IEnumerable<string>? Fields { get; set; } = new List<string> { KnowledgePayloadName.Text, KnowledgePayloadName.Answer };
public int? Limit { get; set; } = 5;
public float? Confidence { get; set; } = 0.5f;

View file

@ -0,0 +1,20 @@
namespace BotSharp.Abstraction.VectorStorage.Models;
public class VectorSearchResult : VectorCollectionData
{
public VectorSearchResult()
{
}
public static VectorSearchResult CopyFrom(VectorCollectionData data)
{
return new VectorSearchResult
{
Id = data.Id,
Data = data.Data,
Score = data.Score,
Vector = data.Vector
};
}
}

View file

@ -1,4 +1,6 @@
using BotSharp.Abstraction.Graph.Models;
using BotSharp.Abstraction.Knowledges.Models;
using BotSharp.Abstraction.VectorStorage.Models;
using BotSharp.OpenAPI.ViewModels.Knowledges;
namespace BotSharp.OpenAPI.Controllers;
@ -16,36 +18,35 @@ public class KnowledgeBaseController : ControllerBase
_services = services;
}
[HttpGet("knowledge/collections")]
public async Task<IEnumerable<string>> GetKnowledgeCollections()
[HttpGet("knowledge/vector/collections")]
public async Task<IEnumerable<string>> GetVectorCollections()
{
return await _knowledgeService.GetKnowledgeCollections();
return await _knowledgeService.GetVectorCollections();
}
[HttpPost("/knowledge/{collection}/search")]
public async Task<IEnumerable<KnowledgeSearchResultViewModel>> SearchKnowledge([FromRoute] string collection, [FromBody] SearchKnowledgeRequest request)
[HttpPost("/knowledge/vector/{collection}/search")]
public async Task<IEnumerable<VectorKnowledgeViewModel>> SearchVectorKnowledge([FromRoute] string collection, [FromBody] SearchVectorKnowledgeRequest request)
{
var options = new KnowledgeSearchOptions
var options = new VectorSearchOptions
{
Text = request.Text,
Fields = request.Fields,
Limit = request.Limit ?? 5,
Confidence = request.Confidence ?? 0.5f,
WithVector = request.WithVector
};
var results = await _knowledgeService.SearchKnowledge(collection, options);
return results.Select(x => KnowledgeSearchResultViewModel.From(x)).ToList();
var results = await _knowledgeService.SearchVectorKnowledge(request.Text, collection, options);
return results.Select(x => VectorKnowledgeViewModel.From(x)).ToList();
}
[HttpPost("/knowledge/{collection}/data")]
public async Task<StringIdPagedItems<KnowledgeSearchResultViewModel>> GetKnowledgeCollectionData([FromRoute] string collection, [FromBody] KnowledgeFilter filter)
[HttpPost("/knowledge/vector/{collection}/data")]
public async Task<StringIdPagedItems<VectorKnowledgeViewModel>> GetVectorCollectionData([FromRoute] string collection, [FromBody] VectorFilter filter)
{
var data = await _knowledgeService.GetKnowledgeCollectionData(collection, filter);
var items = data.Items?.Select(x => KnowledgeSearchResultViewModel.From(x))?
.ToList() ?? new List<KnowledgeSearchResultViewModel>();
var data = await _knowledgeService.GetVectorCollectionData(collection, filter);
var items = data.Items?.Select(x => VectorKnowledgeViewModel.From(x))?
.ToList() ?? new List<VectorKnowledgeViewModel>();
return new StringIdPagedItems<KnowledgeSearchResultViewModel>
return new StringIdPagedItems<VectorKnowledgeViewModel>
{
Count = data.Count,
NextId = data.NextId,
@ -53,14 +54,14 @@ public class KnowledgeBaseController : ControllerBase
};
}
[HttpDelete("/knowledge/{collection}/data/{id}")]
public async Task<bool> DeleteKnowledgeCollectionData([FromRoute] string collection, [FromRoute] string id)
[HttpDelete("/knowledge/vector/{collection}/data/{id}")]
public async Task<bool> DeleteVectorCollectionData([FromRoute] string collection, [FromRoute] string id)
{
return await _knowledgeService.DeleteKnowledgeCollectionData(collection, id);
return await _knowledgeService.DeleteVectorCollectionData(collection, id);
}
[HttpPost("/knowledge/{collection}/upload")]
public async Task<IActionResult> UploadKnowledge([FromRoute] string collection, IFormFile file, [FromForm] int? startPageNum, [FromForm] int? endPageNum)
[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.Name == setttings.Pdf2TextConverter);
@ -73,7 +74,7 @@ public class KnowledgeBaseController : ControllerBase
}
var content = await textConverter.ConvertPdfToText(filePath, startPageNum, endPageNum);
await _knowledgeService.FeedKnowledge(collection, new KnowledgeCreationModel
await _knowledgeService.FeedVectorKnowledge(collection, new KnowledgeCreationModel
{
Content = content
});
@ -81,4 +82,43 @@ public class KnowledgeBaseController : ControllerBase
System.IO.File.Delete(filePath);
return Ok(new { count = 1, file.Length });
}
[HttpPost("/knowledge/graph/search")]
public async Task<GraphKnowledgeViewModel> SearchGraphKnowledge([FromBody] SearchGraphKnowledgeRequest request)
{
var options = new GraphSearchOptions
{
Method = request.Method
};
var result = await _knowledgeService.SearchGraphKnowledge(request.Query, options);
return new GraphKnowledgeViewModel
{
Result = result.Result
};
}
[HttpPost("/knowledge/search")]
public async Task<KnowledgeSearchViewModel> SearchKnowledge([FromBody] SearchKnowledgeRequest 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 graphOptions = new GraphSearchOptions
{
Method = request.GraphParams.Method
};
var result = await _knowledgeService.SearchKnowledge(request.Text, request.VectorParams.Collection, vectorOptions, graphOptions);
return new KnowledgeSearchViewModel
{
VectorResult = result?.VectorResult?.Select(x => VectorKnowledgeViewModel.From(x)),
GraphResult = result?.GraphResult != null ? new GraphKnowledgeViewModel { Result = result.GraphResult.Result } : null
};
}
}

View file

@ -0,0 +1,9 @@
using System.Text.Json.Serialization;
namespace BotSharp.OpenAPI.ViewModels.Knowledges;
public class GraphKnowledgeViewModel
{
[JsonPropertyName("result")]
public string Result { get; set; }
}

View file

@ -0,0 +1,12 @@
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

@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
namespace BotSharp.OpenAPI.ViewModels.Knowledges;
public class SearchGraphKnowledgeRequest
{
[JsonPropertyName("query")]
public string Query { get; set; } = string.Empty;
[JsonPropertyName("method")]
public string Method { get; set; } = string.Empty;
}

View file

@ -1,4 +1,3 @@
using BotSharp.Abstraction.Knowledges.Enums;
using System.Text.Json.Serialization;
namespace BotSharp.OpenAPI.ViewModels.Knowledges;
@ -8,6 +7,22 @@ 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; }
@ -19,4 +34,10 @@ public class SearchKnowledgeRequest
[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,21 @@
using System.Text.Json.Serialization;
namespace BotSharp.OpenAPI.ViewModels.Knowledges;
public class SearchVectorKnowledgeRequest
{
[JsonPropertyName("text")]
public string Text { get; set; } = string.Empty;
[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; }
}

View file

@ -1,9 +1,9 @@
using BotSharp.Abstraction.Knowledges.Models;
using BotSharp.Abstraction.VectorStorage.Models;
using System.Text.Json.Serialization;
namespace BotSharp.OpenAPI.ViewModels.Knowledges;
public class KnowledgeSearchResultViewModel
public class VectorKnowledgeViewModel
{
[JsonPropertyName("id")]
public string Id { get; set; }
@ -20,9 +20,9 @@ public class KnowledgeSearchResultViewModel
public float[]? Vector { get; set; }
public static KnowledgeSearchResultViewModel From(KnowledgeSearchResult result)
public static VectorKnowledgeViewModel From(VectorSearchResult result)
{
return new KnowledgeSearchResultViewModel
return new VectorKnowledgeViewModel
{
Id = result.Id,
Data = result.Data,

View file

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(TargetFramework)</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>$(LangVersion)</LangVersion>
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
<GenerateDocumentationFile>$(GenerateDocumentationFile)</GenerateDocumentationFile>
<OutputPath>$(SolutionDir)packages</OutputPath>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Infrastructure\BotSharp.Abstraction\BotSharp.Abstraction.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,95 @@
using BotSharp.Plugin.Graph.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using System.Net.Http;
using System.Net.Mime;
using System.Text;
using System.Text.Json;
namespace BotSharp.Plugin.Graph;
public class GraphDb : IGraphDb
{
private readonly IServiceProvider _services;
private readonly IHttpContextAccessor _context;
private readonly GraphDbSettings _settings;
private readonly ILogger<GraphDb> _logger;
private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true,
WriteIndented = true,
AllowTrailingCommas = true,
};
public GraphDb(
IServiceProvider services,
IHttpContextAccessor context,
ILogger<GraphDb> logger,
GraphDbSettings settings)
{
_services = services;
_context = context;
_logger = logger;
_settings = settings;
}
public string Name => "Default";
public async Task<GraphSearchData> Search(string query, GraphSearchOptions options)
{
if (string.IsNullOrWhiteSpace(_settings.BaseUrl))
{
return new GraphSearchData();
}
var url = $"{_settings.BaseUrl}/query";
var request = new GraphQueryRequest
{
Query = query,
Method = options.Method
};
return await SendRequest(url, request);
}
private async Task<GraphSearchData> SendRequest(string url, GraphQueryRequest request)
{
var result = new GraphSearchData();
var http = _services.GetRequiredService<IHttpClientFactory>();
using (var client = http.CreateClient())
{
var uri = new Uri(url);
try
{
var data = JsonSerializer.Serialize(request, _jsonOptions);
var message = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = uri,
Content = new StringContent(data, Encoding.UTF8, MediaTypeNames.Application.Json)
};
AddHeaders(client);
var rawResponse = await client.SendAsync(message);
rawResponse.EnsureSuccessStatusCode();
var responseStr = await rawResponse.Content.ReadAsStringAsync();
result = JsonSerializer.Deserialize<GraphSearchData>(responseStr, _jsonOptions);
return result;
}
catch (Exception ex)
{
_logger.LogError($"Error when fetching Lessen GLM response (Endpoint: {url}). {ex.Message}\r\n{ex.InnerException}");
return result;
}
}
}
private void AddHeaders(HttpClient client)
{
client.DefaultRequestHeaders.Add("Authorization", $"{_context.HttpContext.Request.Headers["Authorization"]}");
client.DefaultRequestHeaders.Add("Origin", $"{_context.HttpContext.Request.Headers["Origin"]}");
}
}

View file

@ -0,0 +1,6 @@
namespace BotSharp.Plugin.Graph;
public class GraphDbSettings
{
public string BaseUrl { get; set; }
}

View file

@ -0,0 +1,24 @@
using BotSharp.Abstraction.Plugins;
using BotSharp.Abstraction.Settings;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace BotSharp.Plugin.Graph;
public class GraphPlugin : IBotSharpPlugin
{
public string Id => "74497c25-5e8d-4ee9-b6a8-ce8fe4dabea9";
public string Name => "Graph";
public string Description => "Graph Database";
public string IconUrl => "https://www.microsoft.com/en-us/research/uploads/prodnew/2024/06/GraphRag2024-BlogHeroFeature-1400x788-1.png";
public void RegisterDI(IServiceCollection services, IConfiguration config)
{
services.AddScoped(provider =>
{
var settingService = provider.GetRequiredService<ISettingService>();
return settingService.Bind<GraphDbSettings>("Graph");
});
services.AddScoped<IGraphDb, GraphDb>();
}
}

View file

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace BotSharp.Plugin.Graph.Models;
public class GraphQueryRequest
{
[JsonPropertyName("query")]
public string Query { get; set; }
[JsonPropertyName("method")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Method { get; set; }
}

View file

@ -0,0 +1,7 @@
global using System;
global using System.Collections.Generic;
global using System.Linq;
global using System.Threading.Tasks;
global using Microsoft.Extensions.Logging;
global using BotSharp.Abstraction.Graph;
global using BotSharp.Abstraction.Graph.Models;

View file

@ -1,3 +1,4 @@
using BotSharp.Abstraction.VectorStorage.Models;
using BotSharp.Plugin.KnowledgeBase.Utilities;
using Tensorflow.NumPy;
@ -22,17 +23,17 @@ public class MemoryVectorDb : IVectorDb
return _collections.Select(x => x.Key).ToList();
}
public Task<StringIdPagedItems<KnowledgeCollectionData>> GetCollectionData(string collectionName, KnowledgeFilter filter)
public Task<StringIdPagedItems<VectorCollectionData>> GetCollectionData(string collectionName, VectorFilter filter)
{
throw new NotImplementedException();
}
public async Task<IEnumerable<KnowledgeCollectionData>> Search(string collectionName, float[] vector,
public async Task<IEnumerable<VectorCollectionData>> Search(string collectionName, float[] vector,
IEnumerable<string>? fields, int limit = 5, float confidence = 0.5f, bool withVector = false)
{
if (!_vectors.ContainsKey(collectionName))
{
return new List<KnowledgeCollectionData>();
return new List<VectorCollectionData>();
}
var similarities = VectorUtility.CalCosineSimilarity(vector, _vectors[collectionName]);
@ -41,7 +42,7 @@ public class MemoryVectorDb : IVectorDb
var results = np.argsort(similarities).ToArray<int>()
.Reverse()
.Take(limit)
.Select(i => new KnowledgeCollectionData
.Select(i => new VectorCollectionData
{
Data = new Dictionary<string, string> { { "text", _vectors[collectionName][i].Text } },
Score = similarities[i],

View file

@ -2,7 +2,7 @@ namespace BotSharp.Plugin.KnowledgeBase.Services;
public partial class KnowledgeService
{
public async Task FeedKnowledge(string collectionName, KnowledgeCreationModel knowledge)
public async Task FeedVectorKnowledge(string collectionName, KnowledgeCreationModel knowledge)
{
var index = 0;
var lines = _textChopper.Chop(knowledge.Content, new ChunkOption

View file

@ -2,7 +2,7 @@ namespace BotSharp.Plugin.KnowledgeBase.Services;
public partial class KnowledgeService
{
public async Task<bool> DeleteKnowledgeCollectionData(string collectionName, string id)
public async Task<bool> DeleteVectorCollectionData(string collectionName, string id)
{
try
{

View file

@ -1,8 +1,11 @@
using BotSharp.Abstraction.Graph.Models;
using BotSharp.Abstraction.VectorStorage.Models;
namespace BotSharp.Plugin.KnowledgeBase.Services;
public partial class KnowledgeService
{
public async Task<IEnumerable<string>> GetKnowledgeCollections()
public async Task<IEnumerable<string>> GetVectorCollections()
{
try
{
@ -16,44 +19,88 @@ public partial class KnowledgeService
}
}
public async Task<StringIdPagedItems<KnowledgeSearchResult>> GetKnowledgeCollectionData(string collectionName, KnowledgeFilter filter)
public async Task<StringIdPagedItems<VectorSearchResult>> GetVectorCollectionData(string collectionName, VectorFilter filter)
{
try
{
var db = GetVectorDb();
var pagedResult = await db.GetCollectionData(collectionName, filter);
return new StringIdPagedItems<KnowledgeSearchResult>
return new StringIdPagedItems<VectorSearchResult>
{
Count = pagedResult.Count,
Items = pagedResult.Items.Select(x => KnowledgeSearchResult.CopyFrom(x)),
Items = pagedResult.Items.Select(x => VectorSearchResult.CopyFrom(x)),
NextId = pagedResult.NextId,
};
}
catch (Exception ex)
{
_logger.LogWarning($"Error when getting knowledge collection data ({collectionName}). {ex.Message}\r\n{ex.InnerException}");
return new StringIdPagedItems<KnowledgeSearchResult>();
return new StringIdPagedItems<VectorSearchResult>();
}
}
public async Task<IEnumerable<KnowledgeSearchResult>> SearchKnowledge(string collectionName, KnowledgeSearchOptions options)
public async Task<IEnumerable<VectorSearchResult>> SearchVectorKnowledge(string query, string collectionName, VectorSearchOptions options)
{
try
{
var textEmbedding = GetTextEmbedding();
var vector = await textEmbedding.GetVectorAsync(options.Text);
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 => KnowledgeSearchResult.CopyFrom(x)).ToList();
var results = found.Select(x => VectorSearchResult.CopyFrom(x)).ToList();
return results;
}
catch (Exception ex)
{
_logger.LogWarning($"Error when searching knowledge ({collectionName}). {ex.Message}\r\n{ex.InnerException}");
return new List<KnowledgeSearchResult>();
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 {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();
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}. {ex.Message}\r\n{ex.InnerException}");
return new KnowledgeSearchResult();
}
}
}

View file

@ -25,6 +25,12 @@ public partial class KnowledgeService : IKnowledgeService
return db;
}
private IGraphDb GetGraphDb()
{
var db = _services.GetServices<IGraphDb>().FirstOrDefault(x => x.Name == _settings.GraphDb);
return db;
}
private ITextEmbedding GetTextEmbedding()
{
var embedding = _services.GetServices<ITextEmbedding>().FirstOrDefault(x => x.Provider == _settings.TextEmbedding.Provider);

View file

@ -15,7 +15,7 @@ global using BotSharp.Abstraction.Users;
global using BotSharp.Abstraction.Utilities;
global using BotSharp.Abstraction.Conversations.Models;
global using BotSharp.Abstraction.Agents.Settings;
global using BotSharp.Abstraction.Conversations.Settings;
global using BotSharp.Abstraction.Graph;
global using BotSharp.Abstraction.Knowledges.Settings;
global using BotSharp.Abstraction.Knowledges.Enums;
global using BotSharp.Abstraction.VectorStorage;

View file

@ -1,6 +1,6 @@
using BotSharp.Abstraction.Knowledges.Models;
using BotSharp.Abstraction.Utilities;
using BotSharp.Abstraction.VectorStorage;
using BotSharp.Abstraction.VectorStorage.Models;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
@ -16,7 +16,7 @@ public class FaissDb : IVectorDb
throw new NotImplementedException();
}
public Task<StringIdPagedItems<KnowledgeCollectionData>> GetCollectionData(string collectionName, KnowledgeFilter filter)
public Task<StringIdPagedItems<VectorCollectionData>> GetCollectionData(string collectionName, VectorFilter filter)
{
throw new NotImplementedException();
}
@ -26,7 +26,7 @@ public class FaissDb : IVectorDb
throw new NotImplementedException();
}
public Task<IEnumerable<KnowledgeCollectionData>> Search(string collectionName, float[] vector,
public Task<IEnumerable<VectorCollectionData>> Search(string collectionName, float[] vector,
IEnumerable<string>? fields, int limit = 10, float confidence = 0.5f, bool withVector = false)
{
throw new NotImplementedException();

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Utilities;
using BotSharp.Abstraction.VectorStorage.Models;
using Qdrant.Client;
using Qdrant.Client.Grpc;
@ -41,27 +42,27 @@ public class QdrantDb : IVectorDb
return collections.ToList();
}
public async Task<StringIdPagedItems<KnowledgeCollectionData>> GetCollectionData(string collectionName, KnowledgeFilter filter)
public async Task<StringIdPagedItems<VectorCollectionData>> GetCollectionData(string collectionName, VectorFilter filter)
{
var client = GetClient();
var exist = await DoesCollectionExist(client, collectionName);
if (!exist)
{
return new StringIdPagedItems<KnowledgeCollectionData>();
return new StringIdPagedItems<VectorCollectionData>();
}
var totalPointCount = await client.CountAsync(collectionName);
var response = await client.ScrollAsync(collectionName, limit: (uint)filter.Size,
offset: !string.IsNullOrWhiteSpace(filter.StartId) ? new PointId { Uuid = filter.StartId } : 0,
vectorsSelector: filter.WithVector);
var points = response?.Result?.Select(x => new KnowledgeCollectionData
var points = response?.Result?.Select(x => new VectorCollectionData
{
Id = x.Id?.Uuid ?? string.Empty,
Data = x.Payload.ToDictionary(x => x.Key, x => x.Value.StringValue),
Vector = filter.WithVector ? x.Vectors?.Vector?.Data?.ToArray() : null
})?.ToList() ?? new List<KnowledgeCollectionData>();
})?.ToList() ?? new List<VectorCollectionData>();
return new StringIdPagedItems<KnowledgeCollectionData>
return new StringIdPagedItems<VectorCollectionData>
{
Count = totalPointCount,
NextId = response?.NextPageOffset?.Uuid,
@ -124,10 +125,10 @@ public class QdrantDb : IVectorDb
return result.Status == UpdateStatus.Completed;
}
public async Task<IEnumerable<KnowledgeCollectionData>> Search(string collectionName, float[] vector,
public async Task<IEnumerable<VectorCollectionData>> Search(string collectionName, float[] vector,
IEnumerable<string>? fields, int limit = 5, float confidence = 0.5f, bool withVector = false)
{
var results = new List<KnowledgeCollectionData>();
var results = new List<VectorCollectionData>();
var client = GetClient();
var exist = await DoesCollectionExist(client, collectionName);
@ -161,7 +162,7 @@ public class QdrantDb : IVectorDb
data = point.Payload.ToDictionary(k => k.Key, v => v.Value.StringValue);
}
results.Add(new KnowledgeCollectionData
results.Add(new VectorCollectionData
{
Id = point.Id.Uuid,
Data = data,

View file

@ -1,6 +1,6 @@
using BotSharp.Abstraction.Knowledges.Models;
using BotSharp.Abstraction.Utilities;
using BotSharp.Abstraction.VectorStorage;
using BotSharp.Abstraction.VectorStorage.Models;
using Microsoft.SemanticKernel.Memory;
using System.Collections.Generic;
using System.Threading.Tasks;
@ -28,7 +28,7 @@ namespace BotSharp.Plugin.SemanticKernel
await _memoryStore.CreateCollectionAsync(collectionName);
}
public Task<StringIdPagedItems<KnowledgeCollectionData>> GetCollectionData(string collectionName, KnowledgeFilter filter)
public Task<StringIdPagedItems<VectorCollectionData>> GetCollectionData(string collectionName, VectorFilter filter)
{
throw new System.NotImplementedException();
}
@ -43,15 +43,15 @@ namespace BotSharp.Plugin.SemanticKernel
return result;
}
public async Task<IEnumerable<KnowledgeCollectionData>> Search(string collectionName, float[] vector,
public async Task<IEnumerable<VectorCollectionData>> Search(string collectionName, float[] vector,
IEnumerable<string>? fields, int limit = 5, float confidence = 0.5f, bool withVector = false)
{
var results = _memoryStore.GetNearestMatchesAsync(collectionName, vector, limit);
var resultTexts = new List<KnowledgeCollectionData>();
var resultTexts = new List<VectorCollectionData>();
await foreach (var (record, score) in results)
{
resultTexts.Add(new KnowledgeCollectionData
resultTexts.Add(new VectorCollectionData
{
Data = new Dictionary<string, string> { { "text", record.Metadata.Text } },
Score = score,

View file

@ -48,6 +48,7 @@
<ProjectReference Include="..\Plugins\BotSharp.Plugin.ChatbotUI\BotSharp.Plugin.ChatbotUI.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.HuggingFace\BotSharp.Plugin.HuggingFace.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.KnowledgeBase\BotSharp.Plugin.KnowledgeBase.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.Graph\BotSharp.Plugin.Graph.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.MetaAI\BotSharp.Plugin.MetaAI.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.MetaMessenger\BotSharp.Plugin.MetaMessenger.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.Qdrant\BotSharp.Plugin.Qdrant.csproj" />

View file

@ -249,6 +249,10 @@
"ApiKey": ""
},
"Graph": {
"BaseUrl": ""
},
"WeChat": {
"AgentId": "437bed34-1169-4833-95ce-c24b8b56154a",
"Token": "#{Token}#",
@ -259,6 +263,7 @@
"KnowledgeBase": {
"VectorDb": "Qdrant",
"GraphDb": "Default",
"DefaultCollection": "BotSharp",
"TextEmbedding": {
"Provider": "openai",
@ -309,6 +314,7 @@
"BotSharp.Plugin.HuggingFace",
"BotSharp.Plugin.KnowledgeBase",
"BotSharp.Plugin.Planner",
"BotSharp.Plugin.Graph",
"BotSharp.Plugin.Qdrant",
"BotSharp.Plugin.ChatHub",
"BotSharp.Plugin.WeChat",