From cbb6c58dbccc35b57d138291a134821a944c46d5 Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Wed, 11 Feb 2026 20:47:16 -0800 Subject: [PATCH 1/7] Updates --- Directory.Build.props | 2 +- Directory.Packages.props | 26 +++++++++---------- .../EssentialCSharp.Chat.Common.csproj | 4 +-- .../EssentialCSharp.Chat.Tests.csproj | 2 +- .../EssentialCSharp.Chat.csproj | 2 +- .../EssentialCSharp.Web.Tests.csproj | 4 +-- .../EssentialCSharp.Web.csproj | 7 ++++- EssentialCSharp.Web/Program.cs | 2 +- 8 files changed, 27 insertions(+), 22 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 10592d1d..1a87d274 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ enable - 12 + 14 Recommended enable true diff --git a/Directory.Packages.props b/Directory.Packages.props index 65a63e13..76d7fc31 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -26,22 +26,22 @@ - - - - - - - - - - + + + + + + + + + + - - + + - + diff --git a/EssentialCSharp.Chat.Shared/EssentialCSharp.Chat.Common.csproj b/EssentialCSharp.Chat.Shared/EssentialCSharp.Chat.Common.csproj index e600d10f..3baa5d24 100644 --- a/EssentialCSharp.Chat.Shared/EssentialCSharp.Chat.Common.csproj +++ b/EssentialCSharp.Chat.Shared/EssentialCSharp.Chat.Common.csproj @@ -1,7 +1,7 @@ - + - net9.0 + net10.0 diff --git a/EssentialCSharp.Chat.Tests/EssentialCSharp.Chat.Tests.csproj b/EssentialCSharp.Chat.Tests/EssentialCSharp.Chat.Tests.csproj index f1432132..62dc7206 100644 --- a/EssentialCSharp.Chat.Tests/EssentialCSharp.Chat.Tests.csproj +++ b/EssentialCSharp.Chat.Tests/EssentialCSharp.Chat.Tests.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 false diff --git a/EssentialCSharp.Chat/EssentialCSharp.Chat.csproj b/EssentialCSharp.Chat/EssentialCSharp.Chat.csproj index bf161a97..01ea6026 100644 --- a/EssentialCSharp.Chat/EssentialCSharp.Chat.csproj +++ b/EssentialCSharp.Chat/EssentialCSharp.Chat.csproj @@ -2,7 +2,7 @@ Exe - net9.0 + net10.0 0.0.1 diff --git a/EssentialCSharp.Web.Tests/EssentialCSharp.Web.Tests.csproj b/EssentialCSharp.Web.Tests/EssentialCSharp.Web.Tests.csproj index cde07e16..c88a3867 100644 --- a/EssentialCSharp.Web.Tests/EssentialCSharp.Web.Tests.csproj +++ b/EssentialCSharp.Web.Tests/EssentialCSharp.Web.Tests.csproj @@ -1,7 +1,7 @@ - + - net9.0 + net10.0 false false diff --git a/EssentialCSharp.Web/EssentialCSharp.Web.csproj b/EssentialCSharp.Web/EssentialCSharp.Web.csproj index cf53c6ff..c560a13b 100644 --- a/EssentialCSharp.Web/EssentialCSharp.Web.csproj +++ b/EssentialCSharp.Web/EssentialCSharp.Web.csproj @@ -1,6 +1,11 @@  - net9.0 + net10.0 + + $(NoWarn);CA1873 diff --git a/EssentialCSharp.Web/Program.cs b/EssentialCSharp.Web/Program.cs index 1b83f672..faa7cef2 100644 --- a/EssentialCSharp.Web/Program.cs +++ b/EssentialCSharp.Web/Program.cs @@ -32,7 +32,7 @@ private static void Main(string[] args) // Only loopback proxies are allowed by default. // Clear that restriction because forwarders are enabled by explicit // configuration. - options.KnownNetworks.Clear(); + options.KnownIPNetworks.Clear(); options.KnownProxies.Clear(); }); From 210d56e74d61f13c1f1bb80087b258bab576256e Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Wed, 11 Feb 2026 21:20:31 -0800 Subject: [PATCH 2/7] .net 10 finish --- Directory.Packages.props | 20 +++++------ .../Extensions/ServiceCollectionExtensions.cs | 4 +-- .../Services/AIChatService.cs | 33 ++++++++++++++++++- .../WebApplicationFactory.cs | 22 ++++++++++--- 4 files changed, 61 insertions(+), 18 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 76d7fc31..429ace8c 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -16,30 +16,30 @@ - + - + - - - - + + + + - - + + - + @@ -48,7 +48,7 @@ - + diff --git a/EssentialCSharp.Chat.Shared/Extensions/ServiceCollectionExtensions.cs b/EssentialCSharp.Chat.Shared/Extensions/ServiceCollectionExtensions.cs index 059a6d13..c6498e3e 100644 --- a/EssentialCSharp.Chat.Shared/Extensions/ServiceCollectionExtensions.cs +++ b/EssentialCSharp.Chat.Shared/Extensions/ServiceCollectionExtensions.cs @@ -11,7 +11,7 @@ namespace EssentialCSharp.Chat.Common.Extensions; public static class ServiceCollectionExtensions { - private static readonly string[] PostgresScopes = ["https://ossrdbms-aad.database.windows.net/.default"]; + private static readonly string[] _PostgresScopes = ["https://ossrdbms-aad.database.windows.net/.default"]; /// /// Adds Azure OpenAI and related AI services to the service collection using Managed Identity @@ -124,7 +124,7 @@ private static IServiceCollection AddPostgresVectorStoreWithManagedIdentity( if (isAzurePostgres && string.IsNullOrEmpty(builder.Password)) { // Get access token for Azure PostgreSQL using managed identity - var tokenRequestContext = new TokenRequestContext(PostgresScopes); + var tokenRequestContext = new TokenRequestContext(_PostgresScopes); var accessToken = credential.GetToken(tokenRequestContext, default); // Set the password to the access token diff --git a/EssentialCSharp.Chat.Shared/Services/AIChatService.cs b/EssentialCSharp.Chat.Shared/Services/AIChatService.cs index 8721d966..a3e01f9e 100644 --- a/EssentialCSharp.Chat.Shared/Services/AIChatService.cs +++ b/EssentialCSharp.Chat.Shared/Services/AIChatService.cs @@ -13,7 +13,9 @@ public class AIChatService { private readonly AIOptions _Options; private readonly AzureOpenAIClient _AzureClient; +#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. private readonly OpenAIResponseClient _ResponseClient; +#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. private readonly AISearchService _SearchService; public AIChatService(IOptions options, AISearchService searchService, AzureOpenAIClient azureClient) @@ -24,7 +26,9 @@ public AIChatService(IOptions options, AISearchService searchService, // Initialize Azure OpenAI client and get the Response Client from it _AzureClient = azureClient; +#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. _ResponseClient = _AzureClient.GetOpenAIResponseClient(_Options.ChatDeploymentName); +#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. } /// @@ -43,8 +47,10 @@ public AIChatService(IOptions options, AISearchService searchService, string? systemPrompt = null, string? previousResponseId = null, IMcpClient? mcpClient = null, +#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. IEnumerable? tools = null, ResponseReasoningEffortLevel? reasoningEffortLevel = null, +#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. bool enableContextualSearch = false, CancellationToken cancellationToken = default) { @@ -69,8 +75,10 @@ public AIChatService(IOptions options, AISearchService searchService, string? systemPrompt = null, string? previousResponseId = null, IMcpClient? mcpClient = null, +#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. IEnumerable? tools = null, ResponseReasoningEffortLevel? reasoningEffortLevel = null, +#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. bool enableContextualSearch = false, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -81,11 +89,13 @@ public AIChatService(IOptions options, AISearchService searchService, var systemContext = systemPrompt ?? _Options.SystemPrompt; // Create the streaming response using the Responses API +#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. List responseItems = [ResponseItem.CreateUserMessageItem(enrichedPrompt)]; if (systemContext is not null) { responseItems.Add( ResponseItem.CreateSystemMessageItem(systemContext)); +#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. } var streamingUpdates = _ResponseClient.CreateResponseStreamingAsync( responseItems, @@ -132,14 +142,17 @@ private async Task EnrichPromptWithContext(string prompt, bool enableCon /// Processes streaming updates from the OpenAI Responses API, handling both regular responses and function calls /// private async IAsyncEnumerable<(string text, string? responseId)> ProcessStreamingUpdatesAsync( +#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. IAsyncEnumerable streamingUpdates, ResponseCreationOptions responseOptions, +#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. IMcpClient? mcpClient, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default) { await foreach (var update in streamingUpdates.WithCancellation(cancellationToken)) { string? responseId; +#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. if (update is StreamingResponseCreatedUpdate created) { // Remember the response ID for later function calls @@ -167,8 +180,10 @@ private async Task EnrichPromptWithContext(string prompt, bool enableCon } else if (update is StreamingResponseCompletedUpdate completedUpdate) { +#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. yield return (string.Empty, responseId: completedUpdate.Response.Id); // Signal completion with response ID } +#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. } } @@ -176,8 +191,10 @@ private async Task EnrichPromptWithContext(string prompt, bool enableCon /// Executes a function call and streams the response /// private async IAsyncEnumerable<(string text, string? responseId)> ExecuteFunctionCallAsync( +#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. FunctionCallResponseItem functionCallItem, ResponseCreationOptions responseOptions, +#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. IMcpClient mcpClient, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -217,11 +234,13 @@ private async Task EnrichPromptWithContext(string prompt, bool enableCon // Create input items with both the function call and the result // This matches the Python pattern: append both tool_call and result +#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. var inputItems = new List { functionCallItem, // The original function call new FunctionCallOutputResponseItem(functionCallItem.CallId, string.Join("", toolResult.Content.Where(x => x.Type == "text").OfType().Select(x => x.Text))) }; +#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. // Stream the function call response using the same processing logic var functionResponseStream = _ResponseClient.CreateResponseStreamingAsync( @@ -238,6 +257,7 @@ private async Task EnrichPromptWithContext(string prompt, bool enableCon /// /// Creates response options with optional features /// +#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. private static async Task CreateResponseOptionsAsync( string? previousResponseId = null, IEnumerable? tools = null, @@ -247,6 +267,7 @@ private static async Task CreateResponseOptionsAsync( ) { var options = new ResponseCreationOptions(); +#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. // Add conversation context if available if (!string.IsNullOrEmpty(previousResponseId)) @@ -267,17 +288,21 @@ private static async Task CreateResponseOptionsAsync( { await foreach (McpClientTool tool in mcpClient.EnumerateToolsAsync(cancellationToken: cancellationToken)) { - options.Tools.Add(ResponseTool.CreateFunctionTool(tool.Name, tool.Description, BinaryData.FromString(tool.JsonSchema.GetRawText()))); +#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + options.Tools.Add(ResponseTool.CreateFunctionTool(tool.Name, functionDescription: tool.Description, strictModeEnabled: true, functionParameters: BinaryData.FromString(tool.JsonSchema.GetRawText()))); +#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. } } // Add reasoning options if specified if (reasoningEffortLevel.HasValue) { +#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. options.ReasoningOptions = new ResponseReasoningOptions() { ReasoningEffortLevel = reasoningEffortLevel.Value }; +#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. } return options; @@ -288,7 +313,9 @@ private static async Task CreateResponseOptionsAsync( /// private async Task<(string response, string responseId)> GetChatCompletionCore( string prompt, +#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. ResponseCreationOptions responseOptions, +#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. string? systemPrompt = null, CancellationToken cancellationToken = default) { @@ -296,11 +323,13 @@ private static async Task CreateResponseOptionsAsync( var systemContext = systemPrompt ?? _Options.SystemPrompt; // Create the streaming response using the Responses API +#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. List responseItems = [ResponseItem.CreateUserMessageItem(prompt)]; if (systemContext is not null) { responseItems.Add( ResponseItem.CreateSystemMessageItem(systemContext)); +#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. } // Create the response using the Responses API @@ -315,6 +344,7 @@ private static async Task CreateResponseOptionsAsync( foreach (var outputItem in response.Value.OutputItems) { +#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. if (outputItem is MessageResponseItem messageItem && messageItem.Role == MessageRole.Assistant) { @@ -325,6 +355,7 @@ private static async Task CreateResponseOptionsAsync( break; } } +#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. } return (responseText, responseId); diff --git a/EssentialCSharp.Web.Tests/WebApplicationFactory.cs b/EssentialCSharp.Web.Tests/WebApplicationFactory.cs index 8c84b992..b5b8a8f0 100644 --- a/EssentialCSharp.Web.Tests/WebApplicationFactory.cs +++ b/EssentialCSharp.Web.Tests/WebApplicationFactory.cs @@ -1,8 +1,10 @@ -using EssentialCSharp.Web.Data; +using System.Data.Common; +using EssentialCSharp.Web.Data; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.Extensions.DependencyInjection; namespace EssentialCSharp.Web.Tests; @@ -16,13 +18,23 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureServices(services => { - ServiceDescriptor? descriptor = services.SingleOrDefault( + ServiceDescriptor? dbContextDescriptor = services.SingleOrDefault( d => d.ServiceType == - typeof(DbContextOptions)); + typeof(IDbContextOptionsConfiguration)); - if (descriptor != null) + if (dbContextDescriptor != null) { - services.Remove(descriptor); + services.Remove(dbContextDescriptor); + } + + ServiceDescriptor? dbConnectionDescriptor = + services.SingleOrDefault( + d => d.ServiceType == + typeof(DbConnection)); + + if (dbConnectionDescriptor != null) + { + services.Remove(dbConnectionDescriptor); } _Connection = new SqliteConnection(SqlConnectionString); From 2d73f4cd113821d96a4c69052459e8d1970c7acb Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Wed, 11 Feb 2026 21:33:25 -0800 Subject: [PATCH 3/7] Update Directory.Packages.props Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 429ace8c..4de959ea 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -33,7 +33,7 @@ - + From 836fc6ba0da390589688ff83d3e0800066230d24 Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Wed, 11 Feb 2026 21:33:35 -0800 Subject: [PATCH 4/7] Update EssentialCSharp.Chat.Shared/Services/AIChatService.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- EssentialCSharp.Chat.Shared/Services/AIChatService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EssentialCSharp.Chat.Shared/Services/AIChatService.cs b/EssentialCSharp.Chat.Shared/Services/AIChatService.cs index a3e01f9e..d4f56c35 100644 --- a/EssentialCSharp.Chat.Shared/Services/AIChatService.cs +++ b/EssentialCSharp.Chat.Shared/Services/AIChatService.cs @@ -95,8 +95,8 @@ public AIChatService(IOptions options, AISearchService searchService, { responseItems.Add( ResponseItem.CreateSystemMessageItem(systemContext)); -#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. } +#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. var streamingUpdates = _ResponseClient.CreateResponseStreamingAsync( responseItems, options: responseOptions, From 42e8d3592620de65363fb58e22ad02d0dd7a65fd Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Wed, 11 Feb 2026 21:33:42 -0800 Subject: [PATCH 5/7] Update EssentialCSharp.Chat.Shared/Services/AIChatService.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- EssentialCSharp.Chat.Shared/Services/AIChatService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EssentialCSharp.Chat.Shared/Services/AIChatService.cs b/EssentialCSharp.Chat.Shared/Services/AIChatService.cs index d4f56c35..70634831 100644 --- a/EssentialCSharp.Chat.Shared/Services/AIChatService.cs +++ b/EssentialCSharp.Chat.Shared/Services/AIChatService.cs @@ -329,8 +329,8 @@ private static async Task CreateResponseOptionsAsync( { responseItems.Add( ResponseItem.CreateSystemMessageItem(systemContext)); -#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. } +#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. // Create the response using the Responses API var response = await _ResponseClient.CreateResponseAsync( From cc6279613adf5c5b281f15d9c36ef9fedb1087a1 Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Wed, 11 Feb 2026 21:33:51 -0800 Subject: [PATCH 6/7] Update EssentialCSharp.Chat.Shared/Services/AIChatService.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- EssentialCSharp.Chat.Shared/Services/AIChatService.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/EssentialCSharp.Chat.Shared/Services/AIChatService.cs b/EssentialCSharp.Chat.Shared/Services/AIChatService.cs index 70634831..a3eda26f 100644 --- a/EssentialCSharp.Chat.Shared/Services/AIChatService.cs +++ b/EssentialCSharp.Chat.Shared/Services/AIChatService.cs @@ -180,7 +180,6 @@ private async Task EnrichPromptWithContext(string prompt, bool enableCon } else if (update is StreamingResponseCompletedUpdate completedUpdate) { -#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. yield return (string.Empty, responseId: completedUpdate.Response.Id); // Signal completion with response ID } #pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. From f70523b31b996458acdf1e9d4f951fcb92f551fa Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Wed, 11 Feb 2026 21:41:01 -0800 Subject: [PATCH 7/7] Update EssentialCSharp.Shared.Models version reference --- Directory.Packages.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 4de959ea..5068b95b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -22,7 +22,7 @@ - + @@ -52,4 +52,4 @@ - \ No newline at end of file +