From 2fc121ec2ba9480b4ba099b6026278a5c2e04d52 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 15 Jan 2026 15:05:57 -0600 Subject: [PATCH 1/4] refine json repair & graph db --- .../BotSharp.Abstraction/Graph/IGraphDb.cs | 2 +- .../Graph/Models/GraphSearchData.cs | 6 - .../Graph/Models/GraphSearchResult.cs | 4 +- .../Graph/Options/GraphSearchOptions.cs | 5 +- .../Knowledges/IGraphKnowledgeService.cs | 9 ++ .../Knowledges/IKnowledgeService.cs | 4 - .../IJsonRepairService.cs | 10 +- .../Shared/Options/JsonRepairOptions.cs | 25 ++++ .../Utilities/StringExtensions.cs | 12 +- .../Agents/Services/AgentService.Rendering.cs | 5 +- .../BotSharp.Core/BotSharp.Core.csproj | 6 +- .../Instructs/InsturctionPlugin.cs | 3 + .../Services/InstructService.Execute.cs | 7 +- .../JsonRepair/JsonRepairPlugin.cs | 19 --- .../JsonRepair/JsonRepairService.cs | 113 --------------- .../BotSharp.Core/Shared/JsonRepairService.cs | 130 ++++++++++++++++++ .../Instruct/InstructModeController.cs | 3 +- .../KnowledgeBase/KnowledgeBaseController.cs | 8 +- .../Request/SearchGraphKnowledgeRequest.cs | 15 +- src/Plugins/BotSharp.Plugin.Graph/GraphDb.cs | 15 +- .../Graph/GraphKnowledgeService.cs | 45 ++++++ .../Hooks/KnowledgeHook.cs | 10 +- .../KnowledgeBasePlugin.cs | 2 + .../Services/KnowledgeService.Graph.cs | 24 ---- .../Services/KnowledgeService.cs | 6 - .../Services/MembaseGraphDb.cs | 55 ++++++++ 26 files changed, 339 insertions(+), 204 deletions(-) delete mode 100644 src/Infrastructure/BotSharp.Abstraction/Graph/Models/GraphSearchData.cs create mode 100644 src/Infrastructure/BotSharp.Abstraction/Knowledges/IGraphKnowledgeService.cs rename src/Infrastructure/BotSharp.Abstraction/{Utilities => Shared}/IJsonRepairService.cs (58%) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Shared/Options/JsonRepairOptions.cs delete mode 100644 src/Infrastructure/BotSharp.Core/JsonRepair/JsonRepairPlugin.cs delete mode 100644 src/Infrastructure/BotSharp.Core/JsonRepair/JsonRepairService.cs create mode 100644 src/Infrastructure/BotSharp.Core/Shared/JsonRepairService.cs create mode 100644 src/Plugins/BotSharp.Plugin.KnowledgeBase/Graph/GraphKnowledgeService.cs delete mode 100644 src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Graph.cs create mode 100644 src/Plugins/BotSharp.Plugin.Membase/Services/MembaseGraphDb.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Graph/IGraphDb.cs b/src/Infrastructure/BotSharp.Abstraction/Graph/IGraphDb.cs index 5f30d7500..e2aa3e76f 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Graph/IGraphDb.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Graph/IGraphDb.cs @@ -7,5 +7,5 @@ public interface IGraphDb { public string Provider { get; } - Task Search(string query, GraphSearchOptions options); + Task SearchAsync(string query, GraphSearchOptions? options = null); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Graph/Models/GraphSearchData.cs b/src/Infrastructure/BotSharp.Abstraction/Graph/Models/GraphSearchData.cs deleted file mode 100644 index b6a9b714d..000000000 --- a/src/Infrastructure/BotSharp.Abstraction/Graph/Models/GraphSearchData.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace BotSharp.Abstraction.Graph.Models; - -public class GraphSearchData -{ - public string Result { get; set; } -} diff --git a/src/Infrastructure/BotSharp.Abstraction/Graph/Models/GraphSearchResult.cs b/src/Infrastructure/BotSharp.Abstraction/Graph/Models/GraphSearchResult.cs index e99854479..cb42d6f61 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Graph/Models/GraphSearchResult.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Graph/Models/GraphSearchResult.cs @@ -2,5 +2,7 @@ namespace BotSharp.Abstraction.Graph.Models; public class GraphSearchResult { - public string Result { get; set; } + public string Result { get; set; } = string.Empty; + public string[] Keys { get; set; } = []; + public Dictionary[] Values { get; set; } = []; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Graph/Options/GraphSearchOptions.cs b/src/Infrastructure/BotSharp.Abstraction/Graph/Options/GraphSearchOptions.cs index 9b84a5e08..3e00f3387 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Graph/Options/GraphSearchOptions.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Graph/Options/GraphSearchOptions.cs @@ -2,5 +2,8 @@ namespace BotSharp.Abstraction.Graph.Options; public class GraphSearchOptions { - public string Method { get; set; } + public string? Provider { get; set; } + public string? GraphId { get; set; } + public Dictionary? Arguments { get; set; } + public string? Method { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IGraphKnowledgeService.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IGraphKnowledgeService.cs new file mode 100644 index 000000000..fcc5f08cf --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IGraphKnowledgeService.cs @@ -0,0 +1,9 @@ +using BotSharp.Abstraction.Graph.Models; +using BotSharp.Abstraction.Graph.Options; + +namespace BotSharp.Abstraction.Knowledges; + +public interface IGraphKnowledgeService +{ + Task SearchAsync(string query, GraphSearchOptions? options = null); +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs index 393898b5f..4af6d7e6c 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs @@ -26,10 +26,6 @@ public interface IKnowledgeService Task UpsertVectorCollectionData(string collectionName, VectorUpdateModel update); #endregion - #region Graph - Task SearchGraphKnowledge(string query, GraphSearchOptions options); - #endregion - #region Document /// /// Save documents and their contents to knowledgebase diff --git a/src/Infrastructure/BotSharp.Abstraction/Utilities/IJsonRepairService.cs b/src/Infrastructure/BotSharp.Abstraction/Shared/IJsonRepairService.cs similarity index 58% rename from src/Infrastructure/BotSharp.Abstraction/Utilities/IJsonRepairService.cs rename to src/Infrastructure/BotSharp.Abstraction/Shared/IJsonRepairService.cs index 833cd611b..81f0583ef 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Utilities/IJsonRepairService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Shared/IJsonRepairService.cs @@ -1,4 +1,6 @@ -namespace BotSharp.Abstraction.Utilities; +using BotSharp.Abstraction.Shared.Options; + +namespace BotSharp.Abstraction.Shared; /// /// Service for repairing malformed JSON using LLM. @@ -10,14 +12,16 @@ public interface IJsonRepairService /// /// Target type /// The malformed JSON string + /// The options to fix malformed JSON string /// Deserialized object or default if repair fails - Task RepairAndDeserialize(string malformedJson); + Task RepairAndDeserializeAsync(string malformedJson, JsonRepairOptions? options = null); /// /// Repair malformed JSON string. /// /// The malformed JSON string + /// The options to fix malformed JSON string /// Repaired JSON string - Task Repair(string malformedJson); + Task RepairAsync(string malformedJson, JsonRepairOptions? options = null); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Shared/Options/JsonRepairOptions.cs b/src/Infrastructure/BotSharp.Abstraction/Shared/Options/JsonRepairOptions.cs new file mode 100644 index 000000000..a9676b020 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Shared/Options/JsonRepairOptions.cs @@ -0,0 +1,25 @@ +namespace BotSharp.Abstraction.Shared.Options; + +public class JsonRepairOptions : LlmConfigBase +{ + /// + /// Agent id to get instruction + /// + [JsonPropertyName("agent_id")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? AgentId { get; set; } + + /// + /// Template (prompt) name + /// + [JsonPropertyName("template_name")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? TemplateName { get; set; } + + /// + /// Data that can be used to fill in the prompt + /// + [JsonPropertyName("data")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Dictionary? Data { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Utilities/StringExtensions.cs b/src/Infrastructure/BotSharp.Abstraction/Utilities/StringExtensions.cs index 6088ca46a..20ff3dc51 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Utilities/StringExtensions.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Utilities/StringExtensions.cs @@ -64,16 +64,14 @@ public static string CleanStr(this string? str) return str.Replace(" ", "").Replace("\t", "").Replace("\n", "").Replace("\r", ""); } - [GeneratedRegex(@"[^\u0000-\u007F]")] - private static partial Regex NonAsciiCharactersRegex(); - public static string CleanJsonStr(this string? str) { - if (string.IsNullOrWhiteSpace(str)) return string.Empty; - - str = str.Replace("```json", string.Empty).Replace("```", string.Empty).Trim(); + if (string.IsNullOrWhiteSpace(str)) + { + return string.Empty; + } - return NonAsciiCharactersRegex().Replace(str, ""); + return str.Replace("```json", string.Empty).Replace("```", string.Empty).Trim(); } public static T? Json(this string text) diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs index a897c552b..7282a12b2 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs @@ -152,7 +152,10 @@ public string RenderTemplate(Agent agent, string templateName, IDictionary x.Name == templateName)?.Content ?? string.Empty; return template.Contains(JsonFormat) ? ResponseFormatType.Json : ResponseFormatType.Text; diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index 7f1d2ca93..9869580dc 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -1,4 +1,4 @@ - + $(TargetFramework) @@ -297,4 +297,8 @@ + + + + diff --git a/src/Infrastructure/BotSharp.Core/Instructs/InsturctionPlugin.cs b/src/Infrastructure/BotSharp.Core/Instructs/InsturctionPlugin.cs index 7bde20e84..538c9f44c 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/InsturctionPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/InsturctionPlugin.cs @@ -1,7 +1,9 @@ using BotSharp.Abstraction.Instructs.Settings; using BotSharp.Abstraction.Plugins.Models; using BotSharp.Abstraction.Settings; +using BotSharp.Abstraction.Shared; using BotSharp.Core.Instructs.Hooks; +using BotSharp.Core.Shared; using Microsoft.Extensions.Configuration; namespace BotSharp.Core.Instructs; @@ -20,6 +22,7 @@ public void RegisterDI(IServiceCollection services, IConfiguration config) return settingService.Bind("Instruction"); }); + services.AddScoped(); services.AddScoped(); } diff --git a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs index 13112c2ad..f0ec11c23 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs @@ -10,6 +10,7 @@ using BotSharp.Abstraction.Instructs.Options; using BotSharp.Abstraction.MLTasks; using BotSharp.Abstraction.Models; +using BotSharp.Abstraction.Shared; namespace BotSharp.Core.Instructs; @@ -55,7 +56,6 @@ public async Task Execute( } response = await RunLlm(agent, message, instruction, templateName, files, fileOptions, responseFormat); - return response; } @@ -294,12 +294,13 @@ private async Task RunLlm( { result = await GetChatCompletion(chatCompleter, agent, instruction, prompt, message.MessageId, files); } + // Repair JSON format if needed - responseFormat = responseFormat ?? agentService.GetTemplateResponseFormat(agent, templateName); + responseFormat ??= agentService.GetTemplateResponseFormat(agent, templateName); if (responseFormat == ResponseFormatType.Json) { var jsonRepairService = _services.GetRequiredService(); - result = await jsonRepairService.Repair(result); + result = await jsonRepairService.RepairAsync(result); } response.Text = result; } diff --git a/src/Infrastructure/BotSharp.Core/JsonRepair/JsonRepairPlugin.cs b/src/Infrastructure/BotSharp.Core/JsonRepair/JsonRepairPlugin.cs deleted file mode 100644 index 73157984e..000000000 --- a/src/Infrastructure/BotSharp.Core/JsonRepair/JsonRepairPlugin.cs +++ /dev/null @@ -1,19 +0,0 @@ -using BotSharp.Abstraction.Plugins; -using BotSharp.Abstraction.Utilities; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace BotSharp.Core.JsonRepair; - -public class JsonRepairPlugin : IBotSharpPlugin -{ - public string Id => "b2e8f9c4-6d5a-4f28-cbe1-cf8b92e344cb"; - public string Name => "JSON Repair"; - public string Description => "Repair malformed JSON using LLM"; - - public void RegisterDI(IServiceCollection services, IConfiguration config) - { - services.AddScoped(); - } -} - diff --git a/src/Infrastructure/BotSharp.Core/JsonRepair/JsonRepairService.cs b/src/Infrastructure/BotSharp.Core/JsonRepair/JsonRepairService.cs deleted file mode 100644 index 257267119..000000000 --- a/src/Infrastructure/BotSharp.Core/JsonRepair/JsonRepairService.cs +++ /dev/null @@ -1,113 +0,0 @@ -using BotSharp.Abstraction.Templating; - -namespace BotSharp.Core.JsonRepair; - -/// -/// Service for repairing malformed JSON using LLM. -/// -public class JsonRepairService : IJsonRepairService -{ - private readonly IServiceProvider _services; - private readonly ILogger _logger; - - private const string ROUTER_AGENT_ID = "01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a"; - private const string TEMPLATE_NAME = "json_repair"; - - public JsonRepairService( - IServiceProvider services, - ILogger logger) - { - _services = services; - _logger = logger; - } - - public async Task Repair(string malformedJson) - { - var json = malformedJson.CleanJsonStr(); - if (IsValidJson(json)) return json; - - var repairedJson = await RepairByLLM(json); - if(IsValidJson(repairedJson)) return repairedJson; - - // Try repairing again if still invalid - repairedJson = await RepairByLLM(json); - - return IsValidJson(repairedJson) ? repairedJson : json; - } - - public async Task RepairAndDeserialize(string malformedJson) - { - var json = await Repair(malformedJson); - - return json.Json(); - } - - - private static bool IsValidJson(string malformedJson) - { - if (string.IsNullOrWhiteSpace(malformedJson)) - return false; - - try - { - JsonDocument.Parse(malformedJson); - return true; - } - catch (JsonException) - { - return false; - } - } - - private async Task RepairByLLM(string malformedJson) - { - var agentService = _services.GetRequiredService(); - var router = await agentService.GetAgent(ROUTER_AGENT_ID); - - var template = router.Templates?.FirstOrDefault(x => x.Name == TEMPLATE_NAME)?.Content; - if (string.IsNullOrEmpty(template)) - { - _logger.LogWarning($"Template '{TEMPLATE_NAME}' not found in agent '{ROUTER_AGENT_ID}'"); - return malformedJson; - } - - var render = _services.GetRequiredService(); - var prompt = render.Render(template, new Dictionary - { - { "input", malformedJson } - }); - - try - { - var completion = CompletionProvider.GetChatCompletion(_services, - provider: router?.LlmConfig?.Provider, - model: router?.LlmConfig?.Model); - - var agent = new Agent - { - Id = Guid.Empty.ToString(), - Name = "JsonRepair", - Instruction = "You are a JSON repair expert." - }; - - var dialogs = new List - { - new RoleDialogModel(AgentRole.User, prompt) - { - FunctionName = TEMPLATE_NAME - } - }; - - var response = await completion.GetChatCompletions(agent, dialogs); - - _logger.LogInformation($"JSON repair result: {response.Content}"); - return response.Content.CleanJsonStr(); - } - catch (Exception ex) - { - _logger.LogError(ex, "Failed to repair and deserialize JSON"); - return malformedJson; - } - } -} - diff --git a/src/Infrastructure/BotSharp.Core/Shared/JsonRepairService.cs b/src/Infrastructure/BotSharp.Core/Shared/JsonRepairService.cs new file mode 100644 index 000000000..32a9efbe9 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Shared/JsonRepairService.cs @@ -0,0 +1,130 @@ +using BotSharp.Abstraction.Shared; +using BotSharp.Abstraction.Shared.Options; +using BotSharp.Abstraction.Templating; + +namespace BotSharp.Core.Shared; + +/// +/// Service for repairing malformed JSON using LLM. +/// +public class JsonRepairService : IJsonRepairService +{ + private readonly IServiceProvider _services; + private readonly ILogger _logger; + + private const string DEFAULT_TEMPLATE_NAME = "json_repair"; + + public JsonRepairService( + IServiceProvider services, + ILogger logger) + { + _services = services; + _logger = logger; + } + + public async Task RepairAsync(string malformedJson, JsonRepairOptions? options = null) + { + if (string.IsNullOrWhiteSpace(malformedJson)) + { + return string.Empty; + } + + var json = malformedJson.CleanJsonStr(); + if (IsValidJson(json)) + { + return json; + } + + var repairedJson = await RepairByLLMAsync(json, options); + if (IsValidJson(repairedJson)) + { + return repairedJson; + } + + // Try repairing again if still invalid + repairedJson = await RepairByLLMAsync(json, options); + + return IsValidJson(repairedJson) ? repairedJson : json; + } + + public async Task RepairAndDeserializeAsync(string malformedJson, JsonRepairOptions? options = null) + { + var json = await RepairAsync(malformedJson, options); + return json.Json(); + } + + + #region Private methods + private bool IsValidJson(string malformedJson) + { + if (string.IsNullOrWhiteSpace(malformedJson)) + { + return false; + } + + try + { + JsonDocument.Parse(malformedJson); + return true; + } + catch (JsonException ex) + { + _logger.LogError(ex, $"Error when parse json {malformedJson}"); + return false; + } + } + + private async Task RepairByLLMAsync(string malformedJson, JsonRepairOptions? options) + { + var agentId = options?.AgentId ?? BuiltInAgentId.AIAssistant; + var templateName = options?.TemplateName ?? DEFAULT_TEMPLATE_NAME; + + var agentService = _services.GetRequiredService(); + var agent = await agentService.GetAgent(agentId); + + var template = agent?.Templates?.FirstOrDefault(x => x.Name == templateName)?.Content; + if (string.IsNullOrEmpty(template)) + { + _logger.LogWarning($"Template '{templateName}' cannot be found in agent '{agent?.Name ?? agentId}'"); + return malformedJson; + } + + var render = _services.GetRequiredService(); + var data = options?.Data ?? []; + data["input"] = malformedJson; + var prompt = render.Render(template, data); + + try + { + var completion = CompletionProvider.GetChatCompletion(_services, + provider: options?.Provider ?? agent?.LlmConfig?.Provider ?? "openai", + model: options?.Model ?? agent?.LlmConfig?.Model ?? "gpt-4o-mini"); + + var innerAgent = new Agent + { + Id = Guid.Empty.ToString(), + Name = "JsonRepair", + Instruction = "You are a JSON repair expert." + }; + + var dialogs = new List + { + new RoleDialogModel(AgentRole.User, prompt) + { + FunctionName = templateName + } + }; + + var response = await completion.GetChatCompletions(innerAgent, dialogs); + + _logger.LogInformation($"JSON repair result: {response.Content}"); + return response.Content.CleanJsonStr(); + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to repair and deserialize JSON."); + return malformedJson; + } + } + #endregion +} \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/Instruct/InstructModeController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/Instruct/InstructModeController.cs index 344f0758b..9f5cb69b7 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/Instruct/InstructModeController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/Instruct/InstructModeController.cs @@ -65,7 +65,8 @@ public async Task InstructCompletionSse([FromRoute] string agentId, [FromBody] I templateName: input.Template, files: input.Files, codeOptions: input.CodeOptions, - fileOptions: input.FileOptions); + fileOptions: input.FileOptions, + responseFormat: input.ResponseFormat); result.States = state.GetStates(); diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBase/KnowledgeBaseController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBase/KnowledgeBaseController.cs index 2618f79af..01b8ff2e2 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBase/KnowledgeBaseController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBase/KnowledgeBaseController.cs @@ -11,13 +11,16 @@ namespace BotSharp.OpenAPI.Controllers; public partial class KnowledgeBaseController : ControllerBase { private readonly IKnowledgeService _knowledgeService; + private readonly IGraphKnowledgeService _graphKnowledgeService; private readonly IServiceProvider _services; public KnowledgeBaseController( IKnowledgeService knowledgeService, + IGraphKnowledgeService graphKnowledgeService, IServiceProvider services) { _knowledgeService = knowledgeService; + _graphKnowledgeService = graphKnowledgeService; _services = services; } @@ -206,10 +209,13 @@ public async Task SearchGraphKnowledge([FromBody] Searc { var options = new GraphSearchOptions { + Provider = request.Provider, + GraphId = request.GraphId, + Arguments = request.Arguments, Method = request.Method }; - var result = await _knowledgeService.SearchGraphKnowledge(request.Query, options); + var result = await _graphKnowledgeService.SearchAsync(request.Query, options); return new GraphKnowledgeViewModel { Result = result.Result diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/SearchGraphKnowledgeRequest.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/SearchGraphKnowledgeRequest.cs index 119e729e4..de3aa2b68 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/SearchGraphKnowledgeRequest.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/SearchGraphKnowledgeRequest.cs @@ -7,6 +7,19 @@ public class SearchGraphKnowledgeRequest [JsonPropertyName("query")] public string Query { get; set; } = string.Empty; + [JsonPropertyName("provider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Provider { get; set; } + + [JsonPropertyName("graph_id")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? GraphId { get; set; } + + [JsonPropertyName("arguments")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Dictionary? Arguments { get; set; } + [JsonPropertyName("method")] - public string Method { get; set; } = string.Empty; + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Method { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.Graph/GraphDb.cs b/src/Plugins/BotSharp.Plugin.Graph/GraphDb.cs index 93ae8fbc8..219a4a529 100644 --- a/src/Plugins/BotSharp.Plugin.Graph/GraphDb.cs +++ b/src/Plugins/BotSharp.Plugin.Graph/GraphDb.cs @@ -38,25 +38,27 @@ public GraphDb( public string Provider => "Remote"; - public async Task Search(string query, GraphSearchOptions options) + public async Task SearchAsync(string query, GraphSearchOptions? options = null) { if (string.IsNullOrWhiteSpace(_settings.BaseUrl)) { - return new GraphSearchData(); + return new GraphSearchResult(); } var url = $"{_settings.BaseUrl}{_settings.SearchPath}"; var request = new GraphQueryRequest { Query = query, - Method = options.Method + Method = options?.Method }; return await SendRequest(url, request); } - private async Task SendRequest(string url, GraphQueryRequest request) + + #region Private methods + private async Task SendRequest(string url, GraphQueryRequest request) { - var result = new GraphSearchData(); + var result = new GraphSearchResult(); var http = _services.GetRequiredService(); using (var client = http.CreateClient()) @@ -77,7 +79,7 @@ private async Task SendRequest(string url, GraphQueryRequest re rawResponse.EnsureSuccessStatusCode(); var responseStr = await rawResponse.Content.ReadAsStringAsync(); - result = JsonSerializer.Deserialize(responseStr, _jsonOptions); + result = JsonSerializer.Deserialize(responseStr, _jsonOptions); return result; } catch (Exception ex) @@ -93,4 +95,5 @@ private void AddHeaders(HttpClient client) client.DefaultRequestHeaders.Add("Authorization", $"{_context.HttpContext.Request.Headers["Authorization"]}"); client.DefaultRequestHeaders.Add("Origin", $"{_context.HttpContext.Request.Headers["Origin"]}"); } + #endregion } diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Graph/GraphKnowledgeService.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Graph/GraphKnowledgeService.cs new file mode 100644 index 000000000..022a5b429 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Graph/GraphKnowledgeService.cs @@ -0,0 +1,45 @@ +using BotSharp.Abstraction.Graph.Options; + +namespace BotSharp.Plugin.KnowledgeBase.Graph; + +public class GraphKnowledgeService : IGraphKnowledgeService +{ + private readonly IServiceProvider _services; + private readonly ILogger _logger; + private readonly KnowledgeBaseSettings _settings; + + public GraphKnowledgeService( + IServiceProvider services, + ILogger logger, + KnowledgeBaseSettings settings) + { + _services = services; + _logger = logger; + _settings = settings; + } + + public async Task SearchAsync(string query, GraphSearchOptions? options = null) + { + try + { + var db = GetGraphDb(options?.Provider); + var result = await db.SearchAsync(query, options); + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error when searching graph knowledge (Query: {query})."); + return new GraphSearchResult(); + } + } + + + #region Private methods + private IGraphDb GetGraphDb(string? provider = null) + { + var graphProvider = provider ?? _settings.GraphDb.Provider; + var db = _services.GetServices().FirstOrDefault(x => x.Provider == graphProvider); + return db; + } + #endregion +} diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Hooks/KnowledgeHook.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Hooks/KnowledgeHook.cs index adb441395..6b4b8b99a 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Hooks/KnowledgeHook.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Hooks/KnowledgeHook.cs @@ -6,16 +6,16 @@ namespace BotSharp.Plugin.KnowledgeBase.Hooks; public class KnowledgeHook : IKnowledgeHook { private readonly IKnowledgeService _knowledgeService; - private readonly ITextEmbedding _textEmbedding; + private readonly IGraphKnowledgeService _graphKnowledgeService; private readonly IServiceProvider _services; public KnowledgeHook( IKnowledgeService knowledgeService, - ITextEmbedding textEmbedding, + IGraphKnowledgeService graphKnowledgeService, IServiceProvider services) { _knowledgeService = knowledgeService; - _textEmbedding = textEmbedding; + _graphKnowledgeService = graphKnowledgeService; _services = services; } @@ -40,7 +40,7 @@ public async Task> GetDomainKnowledges(RoleDialogModel message, str { Method = "local" }; - var result = await _knowledgeService.SearchGraphKnowledge(text, options); + var result = await _graphKnowledgeService.SearchAsync(text, options); results.Add(result.Result); } else if (knowledgeBase.Type == "document") @@ -93,7 +93,7 @@ public async Task> GetGlobalKnowledges(RoleDialogModel message) { Method = "local" }; - var result = await _knowledgeService.SearchGraphKnowledge(text, options); + var result = await _graphKnowledgeService.SearchAsync(text, options); results.Add(result.Result); } else diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs index c50764594..755d1ebcc 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs @@ -1,6 +1,7 @@ using BotSharp.Abstraction.Knowledges.Processors; using BotSharp.Abstraction.Plugins.Models; using BotSharp.Abstraction.Settings; +using BotSharp.Plugin.KnowledgeBase.Graph; using Microsoft.Extensions.Configuration; namespace BotSharp.Plugin.KnowledgeBase; @@ -23,6 +24,7 @@ public void RegisterDI(IServiceCollection services, IConfiguration config) services.AddSingleton(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); } diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Graph.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Graph.cs deleted file mode 100644 index f6bcac183..000000000 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Graph.cs +++ /dev/null @@ -1,24 +0,0 @@ -using BotSharp.Abstraction.Graph.Options; - -namespace BotSharp.Plugin.KnowledgeBase.Services; - -public partial class KnowledgeService -{ - public async Task 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.LogError(ex, $"Error when searching graph knowledge (Query: {query})."); - return new GraphSearchResult(); - } - } -} diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.cs index 9555960aa..4a288e651 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.cs @@ -25,12 +25,6 @@ private IVectorDb GetVectorDb() return db; } - private IGraphDb GetGraphDb() - { - var db = _services.GetServices().FirstOrDefault(x => x.Provider == _settings.GraphDb.Provider); - return db; - } - private async Task GetTextEmbedding(string collectionName) { return await KnowledgeSettingHelper.GetTextEmbeddingSetting(_services, collectionName); diff --git a/src/Plugins/BotSharp.Plugin.Membase/Services/MembaseGraphDb.cs b/src/Plugins/BotSharp.Plugin.Membase/Services/MembaseGraphDb.cs new file mode 100644 index 000000000..26a5aeeee --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.Membase/Services/MembaseGraphDb.cs @@ -0,0 +1,55 @@ +using BotSharp.Abstraction.Graph; +using BotSharp.Abstraction.Graph.Models; +using BotSharp.Abstraction.Graph.Options; +using Microsoft.Extensions.Logging; +using System.Text.Json; + +namespace BotSharp.Plugin.Membase.Services; + +public class MembaseGraphDb : IGraphDb +{ + private readonly IServiceProvider _services; + private readonly ILogger _logger; + private readonly IMembaseApi _membaseApi; + + public MembaseGraphDb( + IServiceProvider services, + ILogger logger, + IMembaseApi membaseApi) + { + _services = services; + _logger = logger; + _membaseApi = membaseApi; + } + + public string Provider => "membase"; + + public async Task SearchAsync(string query, GraphSearchOptions? options = null) + { + if (string.IsNullOrEmpty(options?.GraphId)) + { + throw new ArgumentException($"Please provide a valid {Provider} graph id."); + } + + try + { + var response = await _membaseApi.CypherQueryAsync(options.GraphId, new CypherQueryRequest + { + Query = query, + Parameters = options.Arguments ?? [] + }); + + return new GraphSearchResult + { + Keys = response.Columns, + Values = response.Data, + Result = JsonSerializer.Serialize(response.Data) + }; + } + catch (Exception ex) + { + _logger.LogError($"Error when querying {Provider} graph db."); + return new(); + } + } +} From 03933395aba50f21ddaf0f6939cb8167a8e5707d Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 15 Jan 2026 15:12:42 -0600 Subject: [PATCH 2/4] clean code --- .../Knowledges/ICypherGraphService.cs | 14 -------------- .../BotSharp.Core/BotSharp.Core.csproj | 4 ---- .../BotSharp.Core/Shared/JsonRepairService.cs | 2 +- .../BotSharp.Plugin.Membase/MembasePlugin.cs | 2 -- .../Services/MembaseGraphDb.cs | 2 +- .../Services/MembaseService.cs | 2 +- 6 files changed, 3 insertions(+), 23 deletions(-) delete mode 100644 src/Infrastructure/BotSharp.Abstraction/Knowledges/ICypherGraphService.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/ICypherGraphService.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/ICypherGraphService.cs deleted file mode 100644 index 7cb9bf7ec..000000000 --- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/ICypherGraphService.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace BotSharp.Abstraction.Knowledges; - -/// -/// Graph-based semantic knowledge service that supports complex relationships and connections between entities. -/// This service allows for executing Cypher queries to traverse and analyze graph data structures. -/// -public interface ICypherGraphService -{ - Task Execute(string graphId, string query, Dictionary? args = null); - - Task MergeNode(string graphId, GraphNode node); - - Task DeleteNode(string graphId, string nodeId); -} diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index 9869580dc..7aa3a42dd 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -297,8 +297,4 @@ - - - - diff --git a/src/Infrastructure/BotSharp.Core/Shared/JsonRepairService.cs b/src/Infrastructure/BotSharp.Core/Shared/JsonRepairService.cs index 32a9efbe9..07de2e31d 100644 --- a/src/Infrastructure/BotSharp.Core/Shared/JsonRepairService.cs +++ b/src/Infrastructure/BotSharp.Core/Shared/JsonRepairService.cs @@ -90,7 +90,7 @@ private async Task RepairByLLMAsync(string malformedJson, JsonRepairOpti } var render = _services.GetRequiredService(); - var data = options?.Data ?? []; + var data = options?.Data ?? new Dictionary(); data["input"] = malformedJson; var prompt = render.Render(template, data); diff --git a/src/Plugins/BotSharp.Plugin.Membase/MembasePlugin.cs b/src/Plugins/BotSharp.Plugin.Membase/MembasePlugin.cs index be7f19ca8..4b6c62592 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/MembasePlugin.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/MembasePlugin.cs @@ -22,7 +22,5 @@ public void RegisterDI(IServiceCollection services, IConfiguration config) }) .AddHttpMessageHandler() .ConfigureHttpClient(c => c.BaseAddress = new Uri(settings.Host)); - - services.AddScoped(); } } diff --git a/src/Plugins/BotSharp.Plugin.Membase/Services/MembaseGraphDb.cs b/src/Plugins/BotSharp.Plugin.Membase/Services/MembaseGraphDb.cs index 26a5aeeee..d5c601fff 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/Services/MembaseGraphDb.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/Services/MembaseGraphDb.cs @@ -36,7 +36,7 @@ public async Task SearchAsync(string query, GraphSearchOption var response = await _membaseApi.CypherQueryAsync(options.GraphId, new CypherQueryRequest { Query = query, - Parameters = options.Arguments ?? [] + Parameters = options.Arguments ?? new Dictionary() }); return new GraphSearchResult diff --git a/src/Plugins/BotSharp.Plugin.Membase/Services/MembaseService.cs b/src/Plugins/BotSharp.Plugin.Membase/Services/MembaseService.cs index abd3c0700..f0d445908 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/Services/MembaseService.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/Services/MembaseService.cs @@ -2,7 +2,7 @@ namespace BotSharp.Plugin.Membase.Services; -public class MembaseService : ICypherGraphService +public class MembaseService { private readonly IServiceProvider _services; private readonly IMembaseApi _membase; From ff17a6c1652f947f727fbe14f33c2fe4d195011f Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 15 Jan 2026 17:20:11 -0600 Subject: [PATCH 3/4] clean code --- .../BotSharp.Abstraction/Graph/IGraphDb.cs | 18 +++++- .../Graph/IGraphKnowledgeService.cs | 19 ++++++ .../Graph/Models/GraphEdge.cs | 5 ++ .../Graph/Models/GraphNode.cs} | 7 +-- .../Graph/Options/GraphNodeOptions.cs | 6 ++ .../Graph/Requests/GraphNodeCreationModel.cs | 16 +++++ .../Graph/Requests/GraphNodeUpdateModel.cs | 10 +++ .../Responses/GraphNodeDeleteResponse.cs | 6 ++ .../Knowledges/IGraphKnowledgeService.cs | 9 --- .../Knowledges/Models/CyperGraphModels.cs | 21 ------- .../KnowledgeBase/KnowledgeBaseController.cs | 1 + .../Graph/GraphKnowledgeService.Node.cs | 43 +++++++++++++ .../Graph/GraphKnowledgeService.cs | 2 +- .../KnowledgeBasePlugin.cs | 1 - .../Controllers/MembaseController.cs | 24 ++++--- .../GraphDb/MembaseGraphDb.Node.cs | 62 +++++++++++++++++++ .../{Services => GraphDb}/MembaseGraphDb.cs | 4 +- .../Models/{Responses => Graph}/Edge.cs | 2 +- .../Models/{Responses => Graph}/GraphInfo.cs | 2 +- .../Models/Graph/Node.cs | 29 +++++++++ .../Models/Requests/NodeCreationModel.cs | 24 ++++--- .../Models/Requests/NodeUpdateModel.cs | 24 ++++--- .../Services/IMembaseApi.cs | 1 + .../Services/MembaseService.cs | 11 ++-- 24 files changed, 273 insertions(+), 74 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Graph/IGraphKnowledgeService.cs create mode 100644 src/Infrastructure/BotSharp.Abstraction/Graph/Models/GraphEdge.cs rename src/{Plugins/BotSharp.Plugin.Membase/Models/Responses/Node.cs => Infrastructure/BotSharp.Abstraction/Graph/Models/GraphNode.cs} (67%) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Graph/Options/GraphNodeOptions.cs create mode 100644 src/Infrastructure/BotSharp.Abstraction/Graph/Requests/GraphNodeCreationModel.cs create mode 100644 src/Infrastructure/BotSharp.Abstraction/Graph/Requests/GraphNodeUpdateModel.cs create mode 100644 src/Infrastructure/BotSharp.Abstraction/Graph/Responses/GraphNodeDeleteResponse.cs delete mode 100644 src/Infrastructure/BotSharp.Abstraction/Knowledges/IGraphKnowledgeService.cs delete mode 100644 src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/CyperGraphModels.cs create mode 100644 src/Plugins/BotSharp.Plugin.KnowledgeBase/Graph/GraphKnowledgeService.Node.cs create mode 100644 src/Plugins/BotSharp.Plugin.Membase/GraphDb/MembaseGraphDb.Node.cs rename src/Plugins/BotSharp.Plugin.Membase/{Services => GraphDb}/MembaseGraphDb.cs (94%) rename src/Plugins/BotSharp.Plugin.Membase/Models/{Responses => Graph}/Edge.cs (91%) rename src/Plugins/BotSharp.Plugin.Membase/Models/{Responses => Graph}/GraphInfo.cs (92%) create mode 100644 src/Plugins/BotSharp.Plugin.Membase/Models/Graph/Node.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Graph/IGraphDb.cs b/src/Infrastructure/BotSharp.Abstraction/Graph/IGraphDb.cs index e2aa3e76f..c627bb570 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Graph/IGraphDb.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Graph/IGraphDb.cs @@ -1,5 +1,7 @@ using BotSharp.Abstraction.Graph.Models; using BotSharp.Abstraction.Graph.Options; +using BotSharp.Abstraction.Graph.Requests; +using BotSharp.Abstraction.Graph.Responses; namespace BotSharp.Abstraction.Graph; @@ -7,5 +9,19 @@ public interface IGraphDb { public string Provider { get; } - Task SearchAsync(string query, GraphSearchOptions? options = null); + Task SearchAsync(string query, GraphSearchOptions? options = null) + => throw new NotImplementedException(); + + #region Node + Task GetNodeAsync(string graphId, string nodeId) + => throw new NotImplementedException(); + Task CreateNodeAsync(string graphId, GraphNodeCreationModel node) + => throw new NotImplementedException(); + Task UpdateNodeAsync(string graphId, string nodeId, GraphNodeUpdateModel node) + => throw new NotImplementedException(); + Task UpsertNodeAsync(string graphId, string nodeId, GraphNodeUpdateModel node) + => throw new NotImplementedException(); + Task DeleteNodeAsync(string graphId, string nodeId) + => throw new NotImplementedException(); + #endregion } diff --git a/src/Infrastructure/BotSharp.Abstraction/Graph/IGraphKnowledgeService.cs b/src/Infrastructure/BotSharp.Abstraction/Graph/IGraphKnowledgeService.cs new file mode 100644 index 000000000..03a743e4b --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Graph/IGraphKnowledgeService.cs @@ -0,0 +1,19 @@ +using BotSharp.Abstraction.Graph.Models; +using BotSharp.Abstraction.Graph.Options; +using BotSharp.Abstraction.Graph.Requests; +using BotSharp.Abstraction.Graph.Responses; + +namespace BotSharp.Abstraction.Graph; + +public interface IGraphKnowledgeService +{ + Task SearchAsync(string query, GraphSearchOptions? options = null); + + #region Node + Task GetNodeAsync(string graphId, string nodeId, GraphNodeOptions? options = null); + Task CreateNodeAsync(string graphId, GraphNodeCreationModel node, GraphNodeOptions? options = null); + Task UpdateNodeAsync(string graphId, string nodeId, GraphNodeUpdateModel node, GraphNodeOptions? options = null); + Task UpsertNodeAsync(string graphId, string nodeId, GraphNodeUpdateModel node, GraphNodeOptions? options = null); + Task DeleteNodeAsync(string graphId, string nodeId, GraphNodeOptions? options = null); + #endregion +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Graph/Models/GraphEdge.cs b/src/Infrastructure/BotSharp.Abstraction/Graph/Models/GraphEdge.cs new file mode 100644 index 000000000..b73c6744a --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Graph/Models/GraphEdge.cs @@ -0,0 +1,5 @@ +namespace BotSharp.Abstraction.Graph.Models; + +public class GraphEdge +{ +} diff --git a/src/Plugins/BotSharp.Plugin.Membase/Models/Responses/Node.cs b/src/Infrastructure/BotSharp.Abstraction/Graph/Models/GraphNode.cs similarity index 67% rename from src/Plugins/BotSharp.Plugin.Membase/Models/Responses/Node.cs rename to src/Infrastructure/BotSharp.Abstraction/Graph/Models/GraphNode.cs index 2a08cc6b7..e427070cf 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/Models/Responses/Node.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Graph/Models/GraphNode.cs @@ -1,12 +1,11 @@ -namespace BotSharp.Plugin.Membase.Models; +namespace BotSharp.Abstraction.Graph.Models; -public class Node +public class GraphNode { public string Id { get; set; } = string.Empty; public List Labels { get; set; } = new(); public object Properties { get; set; } = new(); - public EmbeddingInfo? Embedding { get; set; } - public DateTime Time { get; set; } = DateTime.UtcNow; + public DateTime? Time { get; set; } = DateTime.UtcNow; public override string ToString() { diff --git a/src/Infrastructure/BotSharp.Abstraction/Graph/Options/GraphNodeOptions.cs b/src/Infrastructure/BotSharp.Abstraction/Graph/Options/GraphNodeOptions.cs new file mode 100644 index 000000000..fb0ab5015 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Graph/Options/GraphNodeOptions.cs @@ -0,0 +1,6 @@ +namespace BotSharp.Abstraction.Graph.Options; + +public class GraphNodeOptions +{ + public string? Provider { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Graph/Requests/GraphNodeCreationModel.cs b/src/Infrastructure/BotSharp.Abstraction/Graph/Requests/GraphNodeCreationModel.cs new file mode 100644 index 000000000..308acbedc --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Graph/Requests/GraphNodeCreationModel.cs @@ -0,0 +1,16 @@ +namespace BotSharp.Abstraction.Graph.Requests; + +public class GraphNodeCreationModel +{ + public string? Id { get; set; } + public string[]? Labels { get; set; } + public object? Properties { get; set; } + public GraphNodeEmbedding? Embedding { get; set; } + public DateTime? Time { get; set; } +} + +public class GraphNodeEmbedding +{ + public string Model { get; set; } + public float[] Vector { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/Graph/Requests/GraphNodeUpdateModel.cs b/src/Infrastructure/BotSharp.Abstraction/Graph/Requests/GraphNodeUpdateModel.cs new file mode 100644 index 000000000..6b27fb93f --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Graph/Requests/GraphNodeUpdateModel.cs @@ -0,0 +1,10 @@ +namespace BotSharp.Abstraction.Graph.Requests; + +public class GraphNodeUpdateModel +{ + public string Id { get; set; } = null!; + public string[]? Labels { get; set; } + public object? Properties { get; set; } + public GraphNodeEmbedding? Embedding { get; set; } + public DateTime? Time { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Graph/Responses/GraphNodeDeleteResponse.cs b/src/Infrastructure/BotSharp.Abstraction/Graph/Responses/GraphNodeDeleteResponse.cs new file mode 100644 index 000000000..fb568587b --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Graph/Responses/GraphNodeDeleteResponse.cs @@ -0,0 +1,6 @@ +namespace BotSharp.Abstraction.Graph.Responses; + +public class GraphNodeDeleteResponse : ResponseBase +{ + public string? Message { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IGraphKnowledgeService.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IGraphKnowledgeService.cs deleted file mode 100644 index fcc5f08cf..000000000 --- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IGraphKnowledgeService.cs +++ /dev/null @@ -1,9 +0,0 @@ -using BotSharp.Abstraction.Graph.Models; -using BotSharp.Abstraction.Graph.Options; - -namespace BotSharp.Abstraction.Knowledges; - -public interface IGraphKnowledgeService -{ - Task SearchAsync(string query, GraphSearchOptions? options = null); -} diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/CyperGraphModels.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/CyperGraphModels.cs deleted file mode 100644 index afedc2723..000000000 --- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/CyperGraphModels.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace BotSharp.Abstraction.Knowledges.Models; - -public class GraphQueryResult -{ - public string[] Columns { get; set; } = []; - public Dictionary[] Items { get; set; } = []; -} - -public class GraphNode -{ - public string Id { get; set; } = string.Empty; - public List Labels { get; set; } = new(); - public object Properties { get; set; } = new(); - public DateTime Time { get; set; } = DateTime.UtcNow; - - public override string ToString() - { - var labelsString = Labels.Count > 0 ? string.Join(", ", Labels) : "No Labels"; - return $"Node ({labelsString}: {Id})"; - } -} diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBase/KnowledgeBaseController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBase/KnowledgeBaseController.cs index 01b8ff2e2..c775cbf84 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBase/KnowledgeBaseController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBase/KnowledgeBaseController.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Files.Utilities; +using BotSharp.Abstraction.Graph; using BotSharp.Abstraction.Graph.Options; using BotSharp.Abstraction.VectorStorage.Models; using BotSharp.Abstraction.VectorStorage.Options; diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Graph/GraphKnowledgeService.Node.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Graph/GraphKnowledgeService.Node.cs new file mode 100644 index 000000000..529229ff4 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Graph/GraphKnowledgeService.Node.cs @@ -0,0 +1,43 @@ +using BotSharp.Abstraction.Graph.Options; +using BotSharp.Abstraction.Graph.Requests; +using BotSharp.Abstraction.Graph.Responses; + +namespace BotSharp.Plugin.KnowledgeBase.Graph; + +public partial class GraphKnowledgeService +{ + public async Task GetNodeAsync(string graphId, string nodeId, GraphNodeOptions? options = null) + { + var db = GetGraphDb(options?.Provider); + var result = await db.GetNodeAsync(graphId, nodeId); + return result; + } + + public async Task CreateNodeAsync(string graphId, GraphNodeCreationModel node, GraphNodeOptions? options = null) + { + var db = GetGraphDb(options?.Provider); + var result = await db.CreateNodeAsync(graphId, node); + return result; + } + + public async Task UpdateNodeAsync(string graphId, string nodeId, GraphNodeUpdateModel node, GraphNodeOptions? options = null) + { + var db = GetGraphDb(options?.Provider); + var result = await db.UpdateNodeAsync(graphId, nodeId, node); + return result; + } + + public async Task UpsertNodeAsync(string graphId, string nodeId, GraphNodeUpdateModel node, GraphNodeOptions? options = null) + { + var db = GetGraphDb(options?.Provider); + var result = await db.UpsertNodeAsync(graphId, nodeId, node); + return result; + } + + public async Task DeleteNodeAsync(string graphId, string nodeId, GraphNodeOptions? options = null) + { + var db = GetGraphDb(options?.Provider); + var result = await db.DeleteNodeAsync(graphId, nodeId); + return result; + } +} diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Graph/GraphKnowledgeService.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Graph/GraphKnowledgeService.cs index 022a5b429..18d03d986 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Graph/GraphKnowledgeService.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Graph/GraphKnowledgeService.cs @@ -2,7 +2,7 @@ namespace BotSharp.Plugin.KnowledgeBase.Graph; -public class GraphKnowledgeService : IGraphKnowledgeService +public partial class GraphKnowledgeService : IGraphKnowledgeService { private readonly IServiceProvider _services; private readonly ILogger _logger; diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs index 755d1ebcc..a1d41307e 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Knowledges.Processors; using BotSharp.Abstraction.Plugins.Models; using BotSharp.Abstraction.Settings; using BotSharp.Plugin.KnowledgeBase.Graph; diff --git a/src/Plugins/BotSharp.Plugin.Membase/Controllers/MembaseController.cs b/src/Plugins/BotSharp.Plugin.Membase/Controllers/MembaseController.cs index 7e1292346..64e1deef8 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/Controllers/MembaseController.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/Controllers/MembaseController.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.Graph; using Microsoft.AspNetCore.Http; namespace BotSharp.Plugin.Membase.Controllers; @@ -6,14 +7,11 @@ namespace BotSharp.Plugin.Membase.Controllers; [ApiController] public class MembaseController : ControllerBase { - private readonly IUserIdentity _user; private readonly IServiceProvider _services; public MembaseController( - IUserIdentity user, IServiceProvider services) { - _user = user; _services = services; } @@ -27,9 +25,7 @@ public MembaseController( [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task ExecuteGraphQuery( - string graphId, - [FromBody] CypherQueryRequest request) + public async Task ExecuteGraphQuery(string graphId, [FromBody] CypherQueryRequest request) { if (string.IsNullOrWhiteSpace(graphId)) { @@ -43,10 +39,18 @@ public async Task ExecuteGraphQuery( try { - var cypherGraphService = _services.GetRequiredService(); - var result = await cypherGraphService.Execute(graphId, request.Query, request.Parameters); - - return Ok(result); + var graph = _services.GetRequiredService(); + var result = await graph.SearchAsync(query: request.Query, options: new() + { + Provider = "membase", + GraphId = graphId, + Arguments = request.Parameters + }); + return Ok(new + { + Columns = result.Keys, + Items = result.Values + }); } catch (Exception ex) { diff --git a/src/Plugins/BotSharp.Plugin.Membase/GraphDb/MembaseGraphDb.Node.cs b/src/Plugins/BotSharp.Plugin.Membase/GraphDb/MembaseGraphDb.Node.cs new file mode 100644 index 000000000..3259e0f88 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.Membase/GraphDb/MembaseGraphDb.Node.cs @@ -0,0 +1,62 @@ +using BotSharp.Abstraction.Graph.Models; +using BotSharp.Abstraction.Graph.Requests; +using BotSharp.Abstraction.Graph.Responses; + +namespace BotSharp.Plugin.Membase.GraphDb; + +public partial class MembaseGraphDb +{ + public async Task GetNodeAsync(string graphId, string nodeId) + { + var found = await _membaseApi.GetNodeAsync(graphId, nodeId); + if (found == null) + { + return null; + } + + return new GraphNode + { + Id = found.Id, + Labels = found.Labels, + Properties = found.Properties, + Time = found.Time + }; + } + + public async Task CreateNodeAsync(string graphId, GraphNodeCreationModel node) + { + var model = NodeCreationModel.From(node); + var createdNode = await _membaseApi.CreateNodeAsync(graphId, model); + return createdNode.ToGraphNode(); + } + + public async Task UpdateNodeAsync(string graphId, string nodeId, GraphNodeUpdateModel node) + { + var model = NodeUpdateModel.From(node); + var updatedNode = await _membaseApi.UpdateNodeAsync(graphId, nodeId, model); + return updatedNode.ToGraphNode(); + } + + public async Task UpsertNodeAsync(string graphId, string nodeId, GraphNodeUpdateModel node) + { + var model = NodeUpdateModel.From(node); + var updatedNode = await _membaseApi.MergeNodeAsync(graphId, nodeId, model); + return updatedNode.ToGraphNode(); + } + + public async Task DeleteNodeAsync(string graphId, string nodeId) + { + try + { + var response = await _membaseApi.DeleteNodeAsync(graphId, nodeId); + return new GraphNodeDeleteResponse { Success = response != null, Message = response?.Message }; + } + catch (Exception ex) + { + return new GraphNodeDeleteResponse + { + ErrorMsg = ex.Message + }; + } + } +} diff --git a/src/Plugins/BotSharp.Plugin.Membase/Services/MembaseGraphDb.cs b/src/Plugins/BotSharp.Plugin.Membase/GraphDb/MembaseGraphDb.cs similarity index 94% rename from src/Plugins/BotSharp.Plugin.Membase/Services/MembaseGraphDb.cs rename to src/Plugins/BotSharp.Plugin.Membase/GraphDb/MembaseGraphDb.cs index d5c601fff..3c0f4d695 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/Services/MembaseGraphDb.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/GraphDb/MembaseGraphDb.cs @@ -4,9 +4,9 @@ using Microsoft.Extensions.Logging; using System.Text.Json; -namespace BotSharp.Plugin.Membase.Services; +namespace BotSharp.Plugin.Membase.GraphDb; -public class MembaseGraphDb : IGraphDb +public partial class MembaseGraphDb : IGraphDb { private readonly IServiceProvider _services; private readonly ILogger _logger; diff --git a/src/Plugins/BotSharp.Plugin.Membase/Models/Responses/Edge.cs b/src/Plugins/BotSharp.Plugin.Membase/Models/Graph/Edge.cs similarity index 91% rename from src/Plugins/BotSharp.Plugin.Membase/Models/Responses/Edge.cs rename to src/Plugins/BotSharp.Plugin.Membase/Models/Graph/Edge.cs index 456e68542..697e11b13 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/Models/Responses/Edge.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/Models/Graph/Edge.cs @@ -1,4 +1,4 @@ -namespace BotSharp.Plugin.Membase.Models; +namespace BotSharp.Plugin.Membase.Models.Graph; public class Edge { diff --git a/src/Plugins/BotSharp.Plugin.Membase/Models/Responses/GraphInfo.cs b/src/Plugins/BotSharp.Plugin.Membase/Models/Graph/GraphInfo.cs similarity index 92% rename from src/Plugins/BotSharp.Plugin.Membase/Models/Responses/GraphInfo.cs rename to src/Plugins/BotSharp.Plugin.Membase/Models/Graph/GraphInfo.cs index 128e7b6ff..4c9f91635 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/Models/Responses/GraphInfo.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/Models/Graph/GraphInfo.cs @@ -1,4 +1,4 @@ -namespace BotSharp.Plugin.Membase.Models; +namespace BotSharp.Plugin.Membase.Models.Graph; public class GraphInfo { diff --git a/src/Plugins/BotSharp.Plugin.Membase/Models/Graph/Node.cs b/src/Plugins/BotSharp.Plugin.Membase/Models/Graph/Node.cs new file mode 100644 index 000000000..25bab3f79 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.Membase/Models/Graph/Node.cs @@ -0,0 +1,29 @@ +using BotSharp.Abstraction.Graph.Models; + +namespace BotSharp.Plugin.Membase.Models.Graph; + +public class Node +{ + public string Id { get; set; } = string.Empty; + public List Labels { get; set; } = new(); + public object Properties { get; set; } = new(); + public NodeEmbedding? Embedding { get; set; } + public DateTime? Time { get; set; } = DateTime.UtcNow; + + public GraphNode ToGraphNode() + { + return new GraphNode + { + Id = Id, + Labels = Labels ?? [], + Properties = Properties ?? new(), + Time = Time + }; + } + + public override string ToString() + { + var labelsString = Labels.Count > 0 ? string.Join(", ", Labels) : "No Labels"; + return $"Node ({labelsString}: {Id})"; + } +} diff --git a/src/Plugins/BotSharp.Plugin.Membase/Models/Requests/NodeCreationModel.cs b/src/Plugins/BotSharp.Plugin.Membase/Models/Requests/NodeCreationModel.cs index 4cb102c5d..abd038279 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/Models/Requests/NodeCreationModel.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/Models/Requests/NodeCreationModel.cs @@ -1,3 +1,5 @@ +using BotSharp.Abstraction.Graph.Requests; + namespace BotSharp.Plugin.Membase.Models; public class NodeCreationModel @@ -5,23 +7,27 @@ public class NodeCreationModel public string? Id { get; set; } public string[]? Labels { get; set; } public object? Properties { get; set; } - public EmbeddingInfo? Embedding { get; set; } + public NodeEmbedding? Embedding { get; set; } public DateTime? Time { get; set; } - public Node ToNode() + public static NodeCreationModel From(GraphNodeCreationModel request) { - return new Node + return new NodeCreationModel { - Id = Id, - Labels = Labels?.ToList() ?? new List(), - Properties = Properties ?? new(), - Embedding = Embedding, - Time = Time ?? DateTime.UtcNow + Id = request.Id, + Labels = request.Labels, + Properties = request.Properties, + Time = request.Time, + Embedding = request?.Embedding != null ? new NodeEmbedding + { + Model = request.Embedding.Model, + Vector = request.Embedding.Vector + } : null }; } } -public class EmbeddingInfo +public class NodeEmbedding { public string Model { get; set; } public float[] Vector { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.Membase/Models/Requests/NodeUpdateModel.cs b/src/Plugins/BotSharp.Plugin.Membase/Models/Requests/NodeUpdateModel.cs index a97586894..2de701664 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/Models/Requests/NodeUpdateModel.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/Models/Requests/NodeUpdateModel.cs @@ -1,3 +1,5 @@ +using BotSharp.Abstraction.Graph.Requests; + namespace BotSharp.Plugin.Membase.Models; public class NodeUpdateModel @@ -5,18 +7,22 @@ public class NodeUpdateModel public string Id { get; set; } = null!; public string[]? Labels { get; set; } public object? Properties { get; set; } - public EmbeddingInfo? Embedding { get; set; } + public NodeEmbedding? Embedding { get; set; } public DateTime? Time { get; set; } - public Node ToNode() + public static NodeUpdateModel From(GraphNodeUpdateModel request) { - return new Node + return new NodeUpdateModel { - Id = Id, - Labels = Labels?.ToList() ?? [], - Properties = Properties ?? new(), - Embedding = Embedding, - Time = Time ?? DateTime.UtcNow + Id = request.Id, + Labels = request.Labels, + Properties = request.Properties, + Time = request.Time, + Embedding = request?.Embedding != null ? new NodeEmbedding + { + Model = request.Embedding.Model, + Vector = request.Embedding.Vector + } : null }; } -} +} \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.Membase/Services/IMembaseApi.cs b/src/Plugins/BotSharp.Plugin.Membase/Services/IMembaseApi.cs index 5f69f387f..85c0b3b17 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/Services/IMembaseApi.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/Services/IMembaseApi.cs @@ -1,3 +1,4 @@ +using BotSharp.Plugin.Membase.Models.Graph; using Refit; namespace BotSharp.Plugin.Membase.Services; diff --git a/src/Plugins/BotSharp.Plugin.Membase/Services/MembaseService.cs b/src/Plugins/BotSharp.Plugin.Membase/Services/MembaseService.cs index f0d445908..040ef8e0f 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/Services/MembaseService.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/Services/MembaseService.cs @@ -1,7 +1,8 @@ -using BotSharp.Abstraction.Knowledges.Models; +using BotSharp.Abstraction.Graph.Models; namespace BotSharp.Plugin.Membase.Services; +[Obsolete] public class MembaseService { private readonly IServiceProvider _services; @@ -13,7 +14,7 @@ public MembaseService(IServiceProvider services, IMembaseApi membase) _membase = membase; } - public async Task Execute(string graphId, string query, Dictionary? args = null) + public async Task Execute(string graphId, string query, Dictionary? args = null) { var response = await _membase.CypherQueryAsync(graphId, new CypherQueryRequest { @@ -21,10 +22,10 @@ public async Task Execute(string graphId, string query, Dictio Parameters = args ?? [] }); - return new GraphQueryResult + return new GraphSearchResult { - Columns = response.Columns, - Items = response.Data + Keys = response.Columns, + Values = response.Data }; } From 2a7ac735159f38cfba134d1fe21a37c064797b9f Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 15 Jan 2026 17:28:40 -0600 Subject: [PATCH 4/4] add di --- .../BotSharp.Plugin.Membase/Controllers/MembaseController.cs | 3 +-- src/Plugins/BotSharp.Plugin.Membase/MembasePlugin.cs | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.Membase/Controllers/MembaseController.cs b/src/Plugins/BotSharp.Plugin.Membase/Controllers/MembaseController.cs index 64e1deef8..056409a83 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/Controllers/MembaseController.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/Controllers/MembaseController.cs @@ -39,10 +39,9 @@ public async Task ExecuteGraphQuery(string graphId, [FromBody] Cy try { - var graph = _services.GetRequiredService(); + var graph = _services.GetServices().First(x => x.Provider == "membase"); var result = await graph.SearchAsync(query: request.Query, options: new() { - Provider = "membase", GraphId = graphId, Arguments = request.Parameters }); diff --git a/src/Plugins/BotSharp.Plugin.Membase/MembasePlugin.cs b/src/Plugins/BotSharp.Plugin.Membase/MembasePlugin.cs index 4b6c62592..315e432d9 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/MembasePlugin.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/MembasePlugin.cs @@ -1,3 +1,5 @@ +using BotSharp.Abstraction.Graph; +using BotSharp.Plugin.Membase.GraphDb; using Refit; namespace BotSharp.Plugin.Membase; @@ -22,5 +24,7 @@ public void RegisterDI(IServiceCollection services, IConfiguration config) }) .AddHttpMessageHandler() .ConfigureHttpClient(c => c.BaseAddress = new Uri(settings.Host)); + + services.AddScoped(); } }