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 2b4bd77b..a17f224a 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -16,7 +16,7 @@ - + @@ -26,29 +26,29 @@ - - - - - - - - - - + + + + + + + + + + - - + + - - - + + + - + diff --git a/EssentialCSharp.Chat.Shared/EssentialCSharp.Chat.Common.csproj b/EssentialCSharp.Chat.Shared/EssentialCSharp.Chat.Common.csproj index e600d10f..19312f38 100644 --- a/EssentialCSharp.Chat.Shared/EssentialCSharp.Chat.Common.csproj +++ b/EssentialCSharp.Chat.Shared/EssentialCSharp.Chat.Common.csproj @@ -1,7 +1,9 @@  - net9.0 + net10.0 + + $(NoWarn);OPENAI001 diff --git a/EssentialCSharp.Chat.Shared/Services/AIChatService.cs b/EssentialCSharp.Chat.Shared/Services/AIChatService.cs index 8721d966..aaea5d2b 100644 --- a/EssentialCSharp.Chat.Shared/Services/AIChatService.cs +++ b/EssentialCSharp.Chat.Shared/Services/AIChatService.cs @@ -42,7 +42,7 @@ public AIChatService(IOptions options, AISearchService searchService, string prompt, string? systemPrompt = null, string? previousResponseId = null, - IMcpClient? mcpClient = null, + McpClient? mcpClient = null, IEnumerable? tools = null, ResponseReasoningEffortLevel? reasoningEffortLevel = null, bool enableContextualSearch = false, @@ -68,7 +68,7 @@ public AIChatService(IOptions options, AISearchService searchService, string prompt, string? systemPrompt = null, string? previousResponseId = null, - IMcpClient? mcpClient = null, + McpClient? mcpClient = null, IEnumerable? tools = null, ResponseReasoningEffortLevel? reasoningEffortLevel = null, bool enableContextualSearch = false, @@ -134,7 +134,7 @@ private async Task EnrichPromptWithContext(string prompt, bool enableCon private async IAsyncEnumerable<(string text, string? responseId)> ProcessStreamingUpdatesAsync( IAsyncEnumerable streamingUpdates, ResponseCreationOptions responseOptions, - IMcpClient? mcpClient, + McpClient? mcpClient, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default) { await foreach (var update in streamingUpdates.WithCancellation(cancellationToken)) @@ -178,7 +178,7 @@ private async Task EnrichPromptWithContext(string prompt, bool enableCon private async IAsyncEnumerable<(string text, string? responseId)> ExecuteFunctionCallAsync( FunctionCallResponseItem functionCallItem, ResponseCreationOptions responseOptions, - IMcpClient mcpClient, + McpClient mcpClient, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default) { // A dictionary of arguments to pass to the tool. Each key represents a parameter name, and its associated value represents the argument value. @@ -242,7 +242,7 @@ private static async Task CreateResponseOptionsAsync( string? previousResponseId = null, IEnumerable? tools = null, ResponseReasoningEffortLevel? reasoningEffortLevel = null, - IMcpClient? mcpClient = null, + McpClient? mcpClient = null, CancellationToken cancellationToken = default ) { @@ -265,9 +265,12 @@ private static async Task CreateResponseOptionsAsync( if (mcpClient is not null) { - await foreach (McpClientTool tool in mcpClient.EnumerateToolsAsync(cancellationToken: cancellationToken)) + var mcpTools = await mcpClient.ListToolsAsync(cancellationToken: cancellationToken); + foreach (McpClientTool tool in mcpTools) { - options.Tools.Add(ResponseTool.CreateFunctionTool(tool.Name, tool.Description, BinaryData.FromString(tool.JsonSchema.GetRawText()))); + // Convert McpClientTool to ResponseTool + // Note: The tool schema is managed internally by the MCP client + options.Tools.Add(ResponseTool.CreateFunctionTool(tool.Name, BinaryData.FromString(tool.Description), strictModeEnabled: false)); } } 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..01df7cf7 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..1fef6b8f 100644 --- a/EssentialCSharp.Web.Tests/WebApplicationFactory.cs +++ b/EssentialCSharp.Web.Tests/WebApplicationFactory.cs @@ -11,16 +11,28 @@ public sealed class WebApplicationFactory : WebApplicationFactory { private static string SqlConnectionString => $"DataSource=file:{Guid.NewGuid()}?mode=memory&cache=shared"; private SqliteConnection? _Connection; + private bool _databaseInitialized; protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureServices(services => { - ServiceDescriptor? descriptor = services.SingleOrDefault( - d => d.ServiceType == - typeof(DbContextOptions)); + // Remove the DatabaseMigrationService as it conflicts with test database setup + var migrationServiceDescriptor = services.FirstOrDefault( + d => d.ImplementationType == typeof(DatabaseMigrationService)); + if (migrationServiceDescriptor != null) + { + services.Remove(migrationServiceDescriptor); + } + + // Remove all existing DbContext-related registrations to avoid provider conflicts in EF Core 10 + var descriptorsToRemove = services + .Where(d => d.ServiceType == typeof(EssentialCSharpWebContext) || + d.ServiceType == typeof(DbContextOptions) || + d.ServiceType == typeof(DbContextOptions)) + .ToList(); - if (descriptor != null) + foreach (var descriptor in descriptorsToRemove) { services.Remove(descriptor); } @@ -28,18 +40,26 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) _Connection = new SqliteConnection(SqlConnectionString); _Connection.Open(); + // Add the test DbContext with SQLite services.AddDbContext(options => { options.UseSqlite(_Connection); }); + }); + } - using ServiceProvider serviceProvider = services.BuildServiceProvider(); - using IServiceScope scope = serviceProvider.CreateScope(); - IServiceProvider scopedServices = scope.ServiceProvider; - EssentialCSharpWebContext db = scopedServices.GetRequiredService(); + /// + /// Ensures the database is created. Called lazily on first access. + /// + private void EnsureDatabaseCreated() + { + if (_databaseInitialized) return; - db.Database.EnsureCreated(); - }); + var factory = Services.GetRequiredService(); + using var scope = factory.CreateScope(); + var db = scope.ServiceProvider.GetRequiredService(); + db.Database.EnsureCreated(); + _databaseInitialized = true; } /// @@ -50,6 +70,7 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) /// The result of the action public T InServiceScope(Func action) { + EnsureDatabaseCreated(); var factory = Services.GetRequiredService(); using var scope = factory.CreateScope(); return action(scope.ServiceProvider); @@ -61,6 +82,7 @@ public T InServiceScope(Func action) /// The action to execute with the scoped service provider public void InServiceScope(Action action) { + EnsureDatabaseCreated(); var factory = Services.GetRequiredService(); using var scope = factory.CreateScope(); action(scope.ServiceProvider); diff --git a/EssentialCSharp.Web/EssentialCSharp.Web.csproj b/EssentialCSharp.Web/EssentialCSharp.Web.csproj index cf53c6ff..db44a11a 100644 --- a/EssentialCSharp.Web/EssentialCSharp.Web.csproj +++ b/EssentialCSharp.Web/EssentialCSharp.Web.csproj @@ -1,6 +1,8 @@  - 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(); });