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..a66bbc65 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. } @@ -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 once the SDK exposes ResponseCreationOptions.User. + /// 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 once the SDK exposes ResponseCreationOptions.User. + /// 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( @@ -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,8 +349,9 @@ 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. + 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. @@ -423,7 +425,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 +438,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 +534,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,