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..5068b95b 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -16,7 +16,7 @@
-
+
@@ -26,30 +26,30 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
+
-
+
-
\ No newline at end of file
+
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.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..a3eda26f 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,12 +89,14 @@ 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,
options: responseOptions,
@@ -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
@@ -169,6 +182,7 @@ private async Task EnrichPromptWithContext(string prompt, bool enableCon
{
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 +190,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 +233,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 +256,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 +266,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 +287,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 +312,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,12 +322,14 @@ 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
var response = await _ResponseClient.CreateResponseAsync(
@@ -315,6 +343,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 +354,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.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.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);
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();
});