From d98acf4b70ec601e2217ca2b7d64a45fe7f4d367 Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Sat, 16 May 2026 21:35:15 -0700 Subject: [PATCH 1/2] Upgrade NuGet dependencies to latest compatible versions Bumps the following packages to their latest stable/preview releases: - Microsoft.SemanticKernel: 1.72.0 -> 1.76.0 - Microsoft.Extensions.Http.Resilience: 10.5.0 -> 10.6.0 - NuGet.Packaging/NuGet.Protocol: 7.3.1 -> 7.6.0 - ModelContextProtocol*: 1.2.0 -> 1.3.0 - Microsoft.SemanticKernel.Connectors.PgVector: 1.72.0-preview -> 1.74.0-preview - System.CommandLine: 2.0.7 -> 2.0.8 - Microsoft.SourceLink.GitHub: 10.0.203 -> 10.0.300 - Azure.Monitor.OpenTelemetry.Profiler: 1.0.0-beta9 -> 1.0.1-beta.1 **Breaking Changes in OpenAI SDK:** The upgrade pulls OpenAI 2.10.0 (via Semantic Kernel 1.76.0) which deprecates the old Responses API. Migrated AIChatService from: - OpenAIResponseClient -> ResponsesClient - ResponseCreationOptions -> CreateResponseOptions Updated method invocations to use new signature where InputItems is populated directly on the options object rather than passed as separate arguments. All chat service tests pass; build succeeds. --- Directory.Packages.props | 25 +++++----- .../Services/AIChatService.cs | 47 ++++++++++--------- 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 07296dc7..97eb8f8d 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -9,9 +9,6 @@ escalated to an error by TreatWarningsAsErrors. --> false - - 1.72.0 - @@ -23,7 +20,7 @@ - + @@ -39,22 +36,22 @@ - - - - + + + + - - + + - - - + + + - + diff --git a/EssentialCSharp.Chat.Shared/Services/AIChatService.cs b/EssentialCSharp.Chat.Shared/Services/AIChatService.cs index fe1bcb4d..feec05d9 100644 --- a/EssentialCSharp.Chat.Shared/Services/AIChatService.cs +++ b/EssentialCSharp.Chat.Shared/Services/AIChatService.cs @@ -17,7 +17,7 @@ public partial 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; + private readonly ResponsesClient _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; private readonly ILogger _Logger; @@ -34,7 +34,7 @@ public AIChatService(IOptions options, AISearchService searchService, _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); + _ResponseClient = _AzureClient.GetResponsesClient(); #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. } @@ -48,7 +48,7 @@ public AIChatService(IOptions options, AISearchService searchService, /// Optional reasoning effort level for reasoning models /// Enable vector search for contextual information /// Authenticated end-user identifier. Currently reserved for forwarding - /// to Azure OpenAI for abuse monitoring once the SDK exposes ResponseCreationOptions.User. + /// to Azure OpenAI for abuse monitoring via CreateResponseOptions.EndUserId. /// Cancellation token /// The AI response text and response ID for conversation continuity public async Task<(string response, string responseId)> GetChatCompletion( @@ -79,7 +79,7 @@ public AIChatService(IOptions options, AISearchService searchService, /// Optional reasoning effort level for reasoning models /// Enable vector search for contextual information /// Authenticated end-user identifier. Currently reserved for forwarding - /// to Azure OpenAI for abuse monitoring once the SDK exposes ResponseCreationOptions.User. + /// to Azure OpenAI for abuse monitoring via CreateResponseOptions.EndUserId. /// Cancellation token /// An async enumerable of response text chunks and final response ID public async IAsyncEnumerable<(string text, string? responseId)> GetChatCompletionStream( @@ -100,12 +100,10 @@ public AIChatService(IOptions options, AISearchService searchService, // 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)]; + responseOptions.InputItems.Clear(); + responseOptions.InputItems.Add(ResponseItem.CreateUserMessageItem(enrichedPrompt)); + var streamingUpdates = _ResponseClient.CreateResponseStreamingAsync(responseOptions, cancellationToken); #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, - cancellationToken: cancellationToken); await foreach (var result in ProcessStreamingUpdatesAsync(streamingUpdates, responseOptions, mcpClient, toolCallDepth: 0, endUserId: endUserId, cancellationToken: cancellationToken)) { @@ -181,7 +179,7 @@ private static string SanitizeForXmlContext(string? input) => 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, + CreateResponseOptions 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. McpClient? mcpClient, int toolCallDepth = 0, @@ -247,7 +245,10 @@ private static string SanitizeForXmlContext(string? input) => outputItems.Add(await ExecuteSingleToolCallAsync(functionCallItem, toolCallDepth, endUserId, mcpClient, cancellationToken)); } - var continuationStream = _ResponseClient.CreateResponseStreamingAsync(outputItems, continuationOptions, cancellationToken); + continuationOptions.InputItems.Clear(); + foreach (var outputItem in outputItems) + continuationOptions.InputItems.Add(outputItem); + var continuationStream = _ResponseClient.CreateResponseStreamingAsync(continuationOptions, cancellationToken); #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. await foreach (var result in ProcessStreamingUpdatesAsync(continuationStream, continuationOptions, mcpClient, toolCallDepth + 1, endUserId, cancellationToken)) @@ -338,7 +339,7 @@ private async Task ExecuteSingleToolCallAsync( /// 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 async Task CreateResponseOptionsAsync( + private async Task CreateResponseOptionsAsync( string? systemPrompt = null, string? previousResponseId = null, IEnumerable? tools = null, @@ -348,7 +349,7 @@ private async Task CreateResponseOptionsAsync( CancellationToken cancellationToken = default ) { - var options = new ResponseCreationOptions(); + var options = new CreateResponseOptions(); #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. // Set the system prompt via Instructions — this is stateless across turns when using previous_response_id, @@ -423,7 +424,7 @@ private 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, + CreateResponseOptions 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. McpClient? mcpClient = null, string? endUserId = null, @@ -436,13 +437,13 @@ private async Task CreateResponseOptionsAsync( const int MaxToolCallIterations = 10; for (int iteration = 0; iteration < MaxToolCallIterations; iteration++) { - ClientResult response; + ClientResult response; try { - response = await _ResponseClient.CreateResponseAsync( - responseItems, - options: responseOptions, - cancellationToken: cancellationToken); + responseOptions.InputItems.Clear(); + foreach (var responseItem in responseItems) + responseOptions.InputItems.Add(responseItem); + response = await _ResponseClient.CreateResponseAsync(responseOptions, cancellationToken); } catch (ClientResultException ex) when (IsContextLengthError(ex)) { @@ -532,16 +533,16 @@ private bool IsMcpToolAllowed(string toolName) /// /// Returns a clone of with - /// replaced. + /// replaced. /// All behavior-affecting properties are copied so that tool-call continuation legs /// produce identical generation behavior to the initial leg. /// #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 ResponseCreationOptions CloneOptionsWithPreviousResponseId( - ResponseCreationOptions source, + private static CreateResponseOptions CloneOptionsWithPreviousResponseId( + CreateResponseOptions source, string? previousResponseId) { - var clone = new ResponseCreationOptions + var clone = new CreateResponseOptions { Instructions = source.Instructions, PreviousResponseId = previousResponseId, From 1ccd92fce3fe9c6885cafb49ca7841025a027e93 Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Sat, 16 May 2026 22:00:24 -0700 Subject: [PATCH 2/2] Fix missing model/deployment and update endUserId XML docs - Set options.Model = _Options.ChatDeploymentName in CreateResponseOptionsAsync so the configured deployment is actually forwarded on every request. GetResponsesClient() has no string overload in the installed SDK, so the model must be set on CreateResponseOptions instead. - Update endUserId XML doc in GetChatCompletion and GetChatCompletionStream from 'Currently reserved for forwarding' to accurately state the value IS forwarded via CreateResponseOptions.EndUserId. --- EssentialCSharp.Chat.Shared/Services/AIChatService.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/EssentialCSharp.Chat.Shared/Services/AIChatService.cs b/EssentialCSharp.Chat.Shared/Services/AIChatService.cs index feec05d9..a66bbc65 100644 --- a/EssentialCSharp.Chat.Shared/Services/AIChatService.cs +++ b/EssentialCSharp.Chat.Shared/Services/AIChatService.cs @@ -47,8 +47,8 @@ public AIChatService(IOptions options, AISearchService searchService, /// Optional tools for the AI to use /// Optional reasoning effort level for reasoning models /// Enable vector search for contextual information - /// Authenticated end-user identifier. Currently reserved for forwarding - /// to Azure OpenAI for abuse monitoring via CreateResponseOptions.EndUserId. + /// Forwarded to Azure OpenAI for abuse monitoring and Microsoft Defender + /// prompt-shield correlation via CreateResponseOptions.EndUserId. /// Cancellation token /// The AI response text and response ID for conversation continuity public async Task<(string response, string responseId)> GetChatCompletion( @@ -78,8 +78,8 @@ public AIChatService(IOptions options, AISearchService searchService, /// Optional tools for the AI to use /// Optional reasoning effort level for reasoning models /// Enable vector search for contextual information - /// Authenticated end-user identifier. Currently reserved for forwarding - /// to Azure OpenAI for abuse monitoring via CreateResponseOptions.EndUserId. + /// Forwarded to Azure OpenAI for abuse monitoring and Microsoft Defender + /// prompt-shield correlation via CreateResponseOptions.EndUserId. /// Cancellation token /// An async enumerable of response text chunks and final response ID public async IAsyncEnumerable<(string text, string? responseId)> GetChatCompletionStream( @@ -351,6 +351,7 @@ private async Task CreateResponseOptionsAsync( { var options = new CreateResponseOptions(); #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. + options.Model = _Options.ChatDeploymentName; // Set the system prompt via Instructions — this is stateless across turns when using previous_response_id, // preventing accumulation of system messages in the conversation context.