From ed575188b523aaf92f08cd1843b77a78b27b92e9 Mon Sep 17 00:00:00 2001 From: jacky Date: Mon, 3 Aug 2026 09:56:52 -0500 Subject: [PATCH] Fix Membase Refit client deserializing graph values as inferred types Refit 8's default serializer adds ObjectToInferredTypesConverter, so Dictionary response values arrive as string/double/Dictionary instead of JsonElement, silently emptying every consumer that does TryGetValue (GraphBuilder, query_flow_graph, similarity search result parsing). Register the client with plain Web JsonSerializerOptions. Also fail with a clear message in ProviderHelper when no LlmProviders entry exists for the requested model instead of a NullReferenceException. Co-Authored-By: Claude Fable 5 --- .../BotSharp.Plugin.Membase/Interfaces/IMembaseApi.cs | 2 +- src/Plugins/BotSharp.Plugin.Membase/MembasePlugin.cs | 6 +++++- .../BotSharp.Plugin.OpenAI/Providers/ProviderHelper.cs | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.Membase/Interfaces/IMembaseApi.cs b/src/Plugins/BotSharp.Plugin.Membase/Interfaces/IMembaseApi.cs index 8ab05160d..f8f5dcdc3 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/Interfaces/IMembaseApi.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/Interfaces/IMembaseApi.cs @@ -60,7 +60,7 @@ public interface IMembaseApi Task ValidatePgtDefinitionAsync(string graphId, string definitionId, [Body] PgtValidationRequest request); [Post("/graph/{graphId}/pgt-external/{correlationId}/complete")] - Task CompletePgtExternalAsync(string graphId, string correlationId, [FromBody] object emptyBody); + Task CompletePgtExternalAsync(string graphId, string correlationId, [FromBody] object body); #endregion #region Procedure diff --git a/src/Plugins/BotSharp.Plugin.Membase/MembasePlugin.cs b/src/Plugins/BotSharp.Plugin.Membase/MembasePlugin.cs index dcd0ff091..69f47ae30 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/MembasePlugin.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/MembasePlugin.cs @@ -26,7 +26,11 @@ public void RegisterDI(IServiceCollection services, IConfiguration config) services.AddHttpContextAccessor(); services.AddTransient(); - services.AddRefitClient(new RefitSettings + // Use plain Web options WITHOUT Refit's default ObjectToInferredTypesConverter: + // consumers (ObjectExtensions.TryGetValue, GraphBuilder, GetNodePropertiesOrDefault) + // rely on Dictionary values deserializing as JsonElement. + services.AddRefitClient(new RefitSettings( + new SystemTextJsonContentSerializer(new JsonSerializerOptions(JsonSerializerDefaults.Web))) { CollectionFormat = CollectionFormat.Multi }) diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/ProviderHelper.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/ProviderHelper.cs index df937390d..121b7a6d5 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/ProviderHelper.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/ProviderHelper.cs @@ -9,6 +9,10 @@ public static OpenAIClient GetClient(string provider, string model, string? apiK { var settingsService = services.GetRequiredService(); var settings = settingsService.GetSetting(provider, model); + if (settings == null && string.IsNullOrEmpty(apiKey)) + { + throw new InvalidOperationException($"No LLM model settings found for '{provider}.{model}'. Register the model under LlmProviders (appsettings/user secrets) or pass an api key."); + } var options = !string.IsNullOrEmpty(settings?.Endpoint) ? new OpenAIClientOptions { Endpoint = new Uri(settings.Endpoint) } : null; return new OpenAIClient(new ApiKeyCredential(apiKey ?? settings!.ApiKey), options);