Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 11 additions & 14 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@
escalated to an error by TreatWarningsAsErrors. -->
<NuGetAudit Condition="'$(AccessToNugetFeed)' != 'true'">false</NuGetAudit>
</PropertyGroup>
<PropertyGroup>
<SemanticKernelVersion>1.72.0</SemanticKernelVersion>
</PropertyGroup>
<ItemGroup Condition="$(AccessToNugetFeed)">
<PackageVersion Include="ContentFeedNuget" Version="$(ToolingPackagesVersion)" />
</ItemGroup>
Expand All @@ -23,7 +20,7 @@
<PackageVersion Include="Azure.Identity" Version="1.21.0" />
<PackageVersion Include="Azure.Monitor.OpenTelemetry.AspNetCore" Version="1.5.0" />
<!-- TODO: update to stable release when Azure.Monitor.OpenTelemetry.Profiler reaches GA -->
<PackageVersion Include="Azure.Monitor.OpenTelemetry.Profiler" Version="1.0.0-beta9" />
<PackageVersion Include="Azure.Monitor.OpenTelemetry.Profiler" Version="1.0.1-beta.1" />
<PackageVersion Include="TUnit" Version="1.40.5" />
<PackageVersion Include="EssentialCSharp.Shared.Models" Version="$(ToolingPackagesVersion)" />
<PackageVersion Include="HtmlAgilityPack" Version="1.12.4" />
Expand All @@ -39,22 +36,22 @@
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.8" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.8" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="10.5.0" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="$(SemanticKernelVersion)" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.PgVector" Version="$(SemanticKernelVersion)-preview" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="10.0.203" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="10.6.0" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.76.0" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.PgVector" Version="1.74.0-preview" />
Comment thread
BenjaminMichaelis marked this conversation as resolved.
Comment thread
BenjaminMichaelis marked this conversation as resolved.
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="10.0.300" />
<!-- Pin to patched versions; NuGet.Protocol 6.12.5+ fixes GHSA-g4vj-cjjj-v7hg (pulled in by Microsoft.VisualStudio.Web.CodeGeneration.Design) -->
<PackageVersion Include="NuGet.Packaging" Version="7.3.1" />
<PackageVersion Include="NuGet.Protocol" Version="7.3.1" />
<PackageVersion Include="NuGet.Packaging" Version="7.6.0" />
<PackageVersion Include="NuGet.Protocol" Version="7.6.0" />
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" />
<PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="10.0.1-preview.1.25571.5" />
<PackageVersion Include="ModelContextProtocol" Version="1.2.0" />
<PackageVersion Include="ModelContextProtocol.AspNetCore" Version="1.2.0" />
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="10.5.0" />
<PackageVersion Include="ModelContextProtocol" Version="1.3.0" />
<PackageVersion Include="ModelContextProtocol.AspNetCore" Version="1.3.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.8" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="Moq.AutoMock" Version="4.0.2" />
<PackageVersion Include="System.CommandLine" Version="2.0.7" />
<PackageVersion Include="System.CommandLine" Version="2.0.8" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
<PackageVersion Include="Octokit" Version="14.0.0" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
Expand Down
52 changes: 27 additions & 25 deletions EssentialCSharp.Chat.Shared/Services/AIChatService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<AIChatService> _Logger;
Expand All @@ -34,7 +34,7 @@ public AIChatService(IOptions<AIOptions> 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();
Comment thread
BenjaminMichaelis marked this conversation as resolved.
#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.
}

Expand All @@ -47,8 +47,8 @@ public AIChatService(IOptions<AIOptions> options, AISearchService searchService,
/// <param name="tools">Optional tools for the AI to use</param>
/// <param name="reasoningEffortLevel">Optional reasoning effort level for reasoning models</param>
/// <param name="enableContextualSearch">Enable vector search for contextual information</param>
/// <param name="endUserId">Authenticated end-user identifier. Currently reserved for forwarding
/// to Azure OpenAI for abuse monitoring once the SDK exposes <c>ResponseCreationOptions.User</c>.</param>
/// <param name="endUserId">Forwarded to Azure OpenAI for abuse monitoring and Microsoft Defender
/// prompt-shield correlation via <c>CreateResponseOptions.EndUserId</c>.</param>
/// <param name="cancellationToken">Cancellation token</param>
Comment thread
BenjaminMichaelis marked this conversation as resolved.
/// <returns>The AI response text and response ID for conversation continuity</returns>
public async Task<(string response, string responseId)> GetChatCompletion(
Expand Down Expand Up @@ -78,8 +78,8 @@ public AIChatService(IOptions<AIOptions> options, AISearchService searchService,
/// <param name="tools">Optional tools for the AI to use</param>
/// <param name="reasoningEffortLevel">Optional reasoning effort level for reasoning models</param>
/// <param name="enableContextualSearch">Enable vector search for contextual information</param>
/// <param name="endUserId">Authenticated end-user identifier. Currently reserved for forwarding
/// to Azure OpenAI for abuse monitoring once the SDK exposes <c>ResponseCreationOptions.User</c>.</param>
/// <param name="endUserId">Forwarded to Azure OpenAI for abuse monitoring and Microsoft Defender
/// prompt-shield correlation via <c>CreateResponseOptions.EndUserId</c>.</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>An async enumerable of response text chunks and final response ID</returns>
public async IAsyncEnumerable<(string text, string? responseId)> GetChatCompletionStream(
Expand All @@ -100,12 +100,10 @@ public AIChatService(IOptions<AIOptions> 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<ResponseItem> 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))
{
Expand Down Expand Up @@ -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<StreamingResponseUpdate> 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,
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -338,7 +339,7 @@ private async Task<ResponseItem> ExecuteSingleToolCallAsync(
/// Creates response options with optional features
/// </summary>
#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<ResponseCreationOptions> CreateResponseOptionsAsync(
private async Task<CreateResponseOptions> CreateResponseOptionsAsync(
string? systemPrompt = null,
string? previousResponseId = null,
IEnumerable<ResponseTool>? tools = null,
Expand All @@ -348,8 +349,9 @@ private async Task<ResponseCreationOptions> 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.
Expand Down Expand Up @@ -423,7 +425,7 @@ private async Task<ResponseCreationOptions> 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,
Expand All @@ -436,13 +438,13 @@ private async Task<ResponseCreationOptions> CreateResponseOptionsAsync(
const int MaxToolCallIterations = 10;
for (int iteration = 0; iteration < MaxToolCallIterations; iteration++)
{
ClientResult<OpenAIResponse> response;
ClientResult<ResponseResult> 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))
{
Expand Down Expand Up @@ -532,16 +534,16 @@ private bool IsMcpToolAllowed(string toolName)

/// <summary>
/// Returns a clone of <paramref name="source"/> with
/// <see cref="ResponseCreationOptions.PreviousResponseId"/> replaced.
/// <see cref="CreateResponseOptions.PreviousResponseId"/> replaced.
/// All behavior-affecting properties are copied so that tool-call continuation legs
/// produce identical generation behavior to the initial leg.
/// </summary>
#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,
Expand Down
Loading