From 060699cc8f61eb2b103782f65a791f80bf9edf9e Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Mon, 3 Jun 2024 07:28:28 -0700 Subject: [PATCH] .Net: Added AzureChatExtensionsOptions property and deprecated WithData classes (#6454) ### Motivation and Context Fixes: https://github.com/microsoft/semantic-kernel/issues/4787 Fixes: https://github.com/microsoft/semantic-kernel/issues/6061 Fixes: https://github.com/microsoft/semantic-kernel/issues/6448 Fixes: https://github.com/microsoft/semantic-kernel/issues/6112 Discussion: https://github.com/microsoft/semantic-kernel/discussions/6016#discussioncomment-9459380 There are two reasons why this change is required: 1. Azure .NET SDK supports `chat completion on your data` functionality out-of-the-box, which means that now we don't need custom implementation. 2. `Chat completion on your data` new API has [breaking changes](https://learn.microsoft.com/en-us/azure/ai-services/openai/references/on-your-data), which makes current `WithData` classes non-functional. ### Description 1. `AzureChatExtensionsOptions` property was added to `OpenAIPromptExecutionSettings`. Initial idea was to add it on `AzureOpenAIChatCompletionService` class level (in all constructors and extension methods), but it looks like this configuration contains some properties which should be useful per request (e.g. index name, search filter etc). Later, we can also add it on service class level, if needed (although I would do that when Options classes for all `AddChatCompletion...` extension methods will be introduced, to avoid more parameters in these methods at the moment). 2. `AzureChatExtensionsOptions` property is marked with `JsonIgnore` attribute, because it's still needs to be investigated if this configuration is serializable. It supports not only Azure AI Search configuration, but also [other configurations](https://learn.microsoft.com/en-us/azure/ai-services/openai/references/on-your-data#data-source) like Azure Cosmos DB for MongoDB, Pinecone etc. We can release it without JSON serialization first, to unblock code-first customers, and we can add support for JSON serialization later (i.e. `config.json` files), if there will be such request. Note: It looks like `OpenAIPromptExecutionSettings` is not ideal place for Azure-specific `AzureChatExtensionsOptions` property. I was thinking to add derived `AzureOpenAIPromptExecutionSettings : OpenAIPromptExecutionSettings` class (and unmark `OpenAIPromptExecutionSettings` as sealed), but this looks like some scope of work which should be implemented when OpenAI and Azure OpenAI functionality will be separated by SDKs. ### Contribution Checklist - [x] The code builds clean without any errors or warnings - [x] The PR follows the [SK Contribution Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts) raises no violations - [x] All unit tests pass, and I have added new tests where possible - [x] I didn't break anyone :smile: --- .github/workflows/dotnet-build-and-test.yml | 2 +- dotnet/code-coverage.ps1 | 2 +- .../AzureOpenAIWithData_ChatCompletion.cs | 58 +++++++++++-------- .../AzureOpenAIWithDataChatMessageContent.cs | 1 + ...enAIWithDataStreamingChatMessageContent.cs | 1 + .../Connectors.OpenAI/AzureSdk/ClientCore.cs | 3 +- ...AzureOpenAIChatCompletionWithDataConfig.cs | 2 + ...zureOpenAIChatCompletionWithDataService.cs | 1 + .../ChatWithDataMessage.cs | 2 + .../ChatWithDataRequest.cs | 3 + .../ChatWithDataResponse.cs | 3 + .../ChatWithDataStreamingResponse.cs | 4 ++ .../OpenAIPromptExecutionSettings.cs | 22 ++++++- .../OpenAIServiceCollectionExtensions.cs | 2 + ...reOpenAIWithDataChatMessageContentTests.cs | 2 + ...ithDataStreamingChatMessageContentTests.cs | 2 + .../AzureOpenAIChatCompletionServiceTests.cs | 21 ++++++- .../AzureOpenAIChatCompletionWithDataTests.cs | 2 + .../OpenAIPromptExecutionSettingsTests.cs | 4 +- .../OpenAIServiceCollectionExtensionsTests.cs | 2 + 20 files changed, 110 insertions(+), 29 deletions(-) diff --git a/.github/workflows/dotnet-build-and-test.yml b/.github/workflows/dotnet-build-and-test.yml index 876a750480..6fbc987a6e 100644 --- a/.github/workflows/dotnet-build-and-test.yml +++ b/.github/workflows/dotnet-build-and-test.yml @@ -81,7 +81,7 @@ jobs: run: | export UT_PROJECTS=$(find ./dotnet -type f -name "*.UnitTests.csproj" | grep -v -E "(Experimental.Orchestration.Flow.UnitTests.csproj|Experimental.Assistants.UnitTests.csproj)" | tr '\n' ' ') for project in $UT_PROJECTS; do - dotnet test -c ${{ matrix.configuration }} $project --no-build -v Normal --logger trx --collect:"XPlat Code Coverage" --results-directory:"TestResults/Coverage/" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.ExcludeByAttribute=ObsoleteAttribute,GeneratedCodeAttribute,CompilerGeneratedAttribute,ExcludeFromCodeCoverageAttribute + dotnet test -c ${{ matrix.configuration }} $project --no-build -v Normal --logger trx --collect:"XPlat Code Coverage" --results-directory:"TestResults/Coverage/" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.ExcludeByAttribute=GeneratedCodeAttribute,CompilerGeneratedAttribute,ExcludeFromCodeCoverageAttribute done - name: Run Integration Tests diff --git a/dotnet/code-coverage.ps1 b/dotnet/code-coverage.ps1 index f2c662d921..1ec07fd1db 100644 --- a/dotnet/code-coverage.ps1 +++ b/dotnet/code-coverage.ps1 @@ -27,7 +27,7 @@ foreach ($project in $testProjects) { dotnet test $testProjectPath ` --collect:"XPlat Code Coverage" ` --results-directory:$coverageOutputPath ` - -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.ExcludeByAttribute=ObsoleteAttribute,GeneratedCodeAttribute,CompilerGeneratedAttribute,ExcludeFromCodeCoverageAttribute ` + -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.ExcludeByAttribute=GeneratedCodeAttribute,CompilerGeneratedAttribute,ExcludeFromCodeCoverageAttribute ` } diff --git a/dotnet/samples/Concepts/ChatCompletion/AzureOpenAIWithData_ChatCompletion.cs b/dotnet/samples/Concepts/ChatCompletion/AzureOpenAIWithData_ChatCompletion.cs index 2a3f8cf3a5..dcfdf7b511 100644 --- a/dotnet/samples/Concepts/ChatCompletion/AzureOpenAIWithData_ChatCompletion.cs +++ b/dotnet/samples/Concepts/ChatCompletion/AzureOpenAIWithData_ChatCompletion.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using Azure.AI.OpenAI; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel.Connectors.OpenAI; @@ -32,7 +33,13 @@ public class AzureOpenAIWithData_ChatCompletion(ITestOutputHelper output) : Base { Console.WriteLine("=== Example with Chat Completion ==="); - var chatCompletion = new AzureOpenAIChatCompletionWithDataService(GetCompletionWithDataConfig()); + var kernel = Kernel.CreateBuilder() + .AddAzureOpenAIChatCompletion( + TestConfiguration.AzureOpenAI.ChatDeploymentName, + TestConfiguration.AzureOpenAI.Endpoint, + TestConfiguration.AzureOpenAI.ApiKey) + .Build(); + var chatHistory = new ChatHistory(); // First question without previous context based on uploaded content. @@ -40,10 +47,14 @@ public class AzureOpenAIWithData_ChatCompletion(ITestOutputHelper output) : Base chatHistory.AddUserMessage(ask); // Chat Completion example - var chatMessage = (AzureOpenAIWithDataChatMessageContent)await chatCompletion.GetChatMessageContentAsync(chatHistory); + var chatExtensionsOptions = GetAzureChatExtensionsOptions(); + var promptExecutionSettings = new OpenAIPromptExecutionSettings { AzureChatExtensionsOptions = chatExtensionsOptions }; + + var chatCompletion = kernel.GetRequiredService(); + + var chatMessage = await chatCompletion.GetChatMessageContentAsync(chatHistory, promptExecutionSettings); var response = chatMessage.Content!; - var toolResponse = chatMessage.ToolContent; // Output // Ask: How did Emily and David meet? @@ -53,11 +64,6 @@ public class AzureOpenAIWithData_ChatCompletion(ITestOutputHelper output) : Base Console.WriteLine(); // Chat history maintenance - if (!string.IsNullOrEmpty(toolResponse)) - { - chatHistory.AddMessage(AuthorRole.Tool, toolResponse); - } - chatHistory.AddAssistantMessage(response); // Second question based on uploaded content. @@ -68,7 +74,7 @@ public class AzureOpenAIWithData_ChatCompletion(ITestOutputHelper output) : Base Console.WriteLine($"Ask: {ask}"); Console.WriteLine("Response: "); - await foreach (var word in chatCompletion.GetStreamingChatMessageContentsAsync(chatHistory)) + await foreach (var word in chatCompletion.GetStreamingChatMessageContentsAsync(chatHistory, promptExecutionSettings)) { Console.Write(word); } @@ -83,16 +89,20 @@ public class AzureOpenAIWithData_ChatCompletion(ITestOutputHelper output) : Base var ask = "How did Emily and David meet?"; - var completionWithDataConfig = GetCompletionWithDataConfig(); - - Kernel kernel = Kernel.CreateBuilder() - .AddAzureOpenAIChatCompletion(config: completionWithDataConfig) + var kernel = Kernel.CreateBuilder() + .AddAzureOpenAIChatCompletion( + TestConfiguration.AzureOpenAI.ChatDeploymentName, + TestConfiguration.AzureOpenAI.Endpoint, + TestConfiguration.AzureOpenAI.ApiKey) .Build(); var function = kernel.CreateFunctionFromPrompt("Question: {{$input}}"); + var chatExtensionsOptions = GetAzureChatExtensionsOptions(); + var promptExecutionSettings = new OpenAIPromptExecutionSettings { AzureChatExtensionsOptions = chatExtensionsOptions }; + // First question without previous context based on uploaded content. - var response = await kernel.InvokeAsync(function, new() { ["input"] = ask }); + var response = await kernel.InvokeAsync(function, new(promptExecutionSettings) { ["input"] = ask }); // Output // Ask: How did Emily and David meet? @@ -103,7 +113,7 @@ public class AzureOpenAIWithData_ChatCompletion(ITestOutputHelper output) : Base // Second question based on uploaded content. ask = "What are Emily and David studying?"; - response = await kernel.InvokeAsync(function, new() { ["input"] = ask }); + response = await kernel.InvokeAsync(function, new(promptExecutionSettings) { ["input"] = ask }); // Output // Ask: What are Emily and David studying? @@ -117,16 +127,18 @@ public class AzureOpenAIWithData_ChatCompletion(ITestOutputHelper output) : Base /// /// Initializes a new instance of the class. /// - private static AzureOpenAIChatCompletionWithDataConfig GetCompletionWithDataConfig() + private static AzureChatExtensionsOptions GetAzureChatExtensionsOptions() { - return new AzureOpenAIChatCompletionWithDataConfig + var azureSearchExtensionConfiguration = new AzureSearchChatExtensionConfiguration { - CompletionModelId = TestConfiguration.AzureOpenAI.ChatDeploymentName, - CompletionEndpoint = TestConfiguration.AzureOpenAI.Endpoint, - CompletionApiKey = TestConfiguration.AzureOpenAI.ApiKey, - DataSourceEndpoint = TestConfiguration.AzureAISearch.Endpoint, - DataSourceApiKey = TestConfiguration.AzureAISearch.ApiKey, - DataSourceIndex = TestConfiguration.AzureAISearch.IndexName + SearchEndpoint = new Uri(TestConfiguration.AzureAISearch.Endpoint), + Authentication = new OnYourDataApiKeyAuthenticationOptions(TestConfiguration.AzureAISearch.ApiKey), + IndexName = TestConfiguration.AzureAISearch.IndexName + }; + + return new AzureChatExtensionsOptions + { + Extensions = { azureSearchExtensionConfiguration } }; } } diff --git a/dotnet/src/Connectors/Connectors.OpenAI/AzureSdk/AzureOpenAIWithDataChatMessageContent.cs b/dotnet/src/Connectors/Connectors.OpenAI/AzureSdk/AzureOpenAIWithDataChatMessageContent.cs index 04b0e7a2c0..594b420bc5 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI/AzureSdk/AzureOpenAIWithDataChatMessageContent.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI/AzureSdk/AzureOpenAIWithDataChatMessageContent.cs @@ -12,6 +12,7 @@ namespace Microsoft.SemanticKernel.Connectors.OpenAI; /// OpenAI specialized with data chat message content /// [Experimental("SKEXP0010")] +[Obsolete("This class is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions")] public sealed class AzureOpenAIWithDataChatMessageContent : ChatMessageContent { /// diff --git a/dotnet/src/Connectors/Connectors.OpenAI/AzureSdk/AzureOpenAIWithDataStreamingChatMessageContent.cs b/dotnet/src/Connectors/Connectors.OpenAI/AzureSdk/AzureOpenAIWithDataStreamingChatMessageContent.cs index 457c2f0bab..ebe57f4462 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI/AzureSdk/AzureOpenAIWithDataStreamingChatMessageContent.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI/AzureSdk/AzureOpenAIWithDataStreamingChatMessageContent.cs @@ -16,6 +16,7 @@ namespace Microsoft.SemanticKernel.Connectors.OpenAI; /// Represents a chat message content chunk that was streamed from the remote model. /// [Experimental("SKEXP0010")] +[Obsolete("This class is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions")] public sealed class AzureOpenAIWithDataStreamingChatMessageContent : StreamingChatMessageContent { /// diff --git a/dotnet/src/Connectors/Connectors.OpenAI/AzureSdk/ClientCore.cs b/dotnet/src/Connectors/Connectors.OpenAI/AzureSdk/ClientCore.cs index 36addec5c8..6f5b4d3d29 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI/AzureSdk/ClientCore.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI/AzureSdk/ClientCore.cs @@ -1107,7 +1107,8 @@ internal abstract class ClientCore Seed = executionSettings.Seed, User = executionSettings.User, LogProbabilitiesPerToken = executionSettings.TopLogprobs, - EnableLogProbabilities = executionSettings.Logprobs + EnableLogProbabilities = executionSettings.Logprobs, + AzureExtensionsOptions = executionSettings.AzureChatExtensionsOptions }; switch (executionSettings.ResponseFormat) diff --git a/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/AzureOpenAIChatCompletionWithDataConfig.cs b/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/AzureOpenAIChatCompletionWithDataConfig.cs index 8b7ba40cfb..7f49e74c5f 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/AzureOpenAIChatCompletionWithDataConfig.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/AzureOpenAIChatCompletionWithDataConfig.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Diagnostics.CodeAnalysis; namespace Microsoft.SemanticKernel.Connectors.OpenAI; @@ -9,6 +10,7 @@ namespace Microsoft.SemanticKernel.Connectors.OpenAI; /// More information: /// [Experimental("SKEXP0010")] +[Obsolete("This class is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions")] public class AzureOpenAIChatCompletionWithDataConfig { /// diff --git a/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/AzureOpenAIChatCompletionWithDataService.cs b/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/AzureOpenAIChatCompletionWithDataService.cs index 02d253e461..793209704b 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/AzureOpenAIChatCompletionWithDataService.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/AzureOpenAIChatCompletionWithDataService.cs @@ -27,6 +27,7 @@ namespace Microsoft.SemanticKernel.Connectors.OpenAI; /// More information: /// [Experimental("SKEXP0010")] +[Obsolete("This class is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions")] public sealed class AzureOpenAIChatCompletionWithDataService : IChatCompletionService, ITextGenerationService { /// diff --git a/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/ChatWithDataMessage.cs b/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/ChatWithDataMessage.cs index 46d8d51863..ce3a5e5465 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/ChatWithDataMessage.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/ChatWithDataMessage.cs @@ -1,11 +1,13 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; namespace Microsoft.SemanticKernel.Connectors.OpenAI; [Experimental("SKEXP0010")] +[Obsolete("This class is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions")] internal sealed class ChatWithDataMessage { [JsonPropertyName("role")] diff --git a/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/ChatWithDataRequest.cs b/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/ChatWithDataRequest.cs index ed9d38f8a4..214b917a8a 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/ChatWithDataRequest.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/ChatWithDataRequest.cs @@ -8,6 +8,7 @@ using System.Text.Json.Serialization; namespace Microsoft.SemanticKernel.Connectors.OpenAI; [Experimental("SKEXP0010")] +[Obsolete("This class is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions")] internal sealed class ChatWithDataRequest { [JsonPropertyName("temperature")] @@ -42,6 +43,7 @@ internal sealed class ChatWithDataRequest } [Experimental("SKEXP0010")] +[Obsolete("This class is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions")] internal sealed class ChatWithDataSource { [JsonPropertyName("type")] @@ -55,6 +57,7 @@ internal sealed class ChatWithDataSource } [Experimental("SKEXP0010")] +[Obsolete("This class is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions")] internal sealed class ChatWithDataSourceParameters { [JsonPropertyName("endpoint")] diff --git a/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/ChatWithDataResponse.cs b/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/ChatWithDataResponse.cs index 3219cd04ea..4ba5e77613 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/ChatWithDataResponse.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/ChatWithDataResponse.cs @@ -10,6 +10,7 @@ namespace Microsoft.SemanticKernel.Connectors.OpenAI; #pragma warning disable CA1812 // Avoid uninstantiated internal classes [Experimental("SKEXP0010")] +[Obsolete("This class is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions")] [method: JsonConstructor] internal sealed class ChatWithDataResponse(ChatWithDataUsage usage) { @@ -33,6 +34,7 @@ internal sealed class ChatWithDataResponse(ChatWithDataUsage usage) } [Experimental("SKEXP0010")] +[Obsolete("This class is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions")] [SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Used for JSON deserialization")] internal sealed class ChatWithDataChoice { @@ -41,6 +43,7 @@ internal sealed class ChatWithDataChoice } [Experimental("SKEXP0010")] +[Obsolete("This class is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions")] internal sealed class ChatWithDataUsage { [JsonPropertyName("prompt_tokens")] diff --git a/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/ChatWithDataStreamingResponse.cs b/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/ChatWithDataStreamingResponse.cs index ee5cdcd0bd..9455553d96 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/ChatWithDataStreamingResponse.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI/ChatCompletionWithData/ChatWithDataStreamingResponse.cs @@ -8,6 +8,7 @@ using System.Text.Json.Serialization; namespace Microsoft.SemanticKernel.Connectors.OpenAI; [Experimental("SKEXP0010")] +[Obsolete("This class is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions")] [SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Used for JSON deserialization")] internal sealed class ChatWithDataStreamingResponse { @@ -28,6 +29,7 @@ internal sealed class ChatWithDataStreamingResponse } [Experimental("SKEXP0010")] +[Obsolete("This class is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions")] [SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Used for JSON deserialization")] internal sealed class ChatWithDataStreamingChoice { @@ -39,6 +41,7 @@ internal sealed class ChatWithDataStreamingChoice } [Experimental("SKEXP0010")] +[Obsolete("This class is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions")] [SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Used for JSON deserialization")] internal sealed class ChatWithDataStreamingMessage { @@ -50,6 +53,7 @@ internal sealed class ChatWithDataStreamingMessage } [Experimental("SKEXP0010")] +[Obsolete("This class is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions")] internal sealed class ChatWithDataStreamingDelta { [JsonPropertyName("role")] diff --git a/dotnet/src/Connectors/Connectors.OpenAI/OpenAIPromptExecutionSettings.cs b/dotnet/src/Connectors/Connectors.OpenAI/OpenAIPromptExecutionSettings.cs index b4097b7020..36796c62f7 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI/OpenAIPromptExecutionSettings.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI/OpenAIPromptExecutionSettings.cs @@ -287,6 +287,23 @@ public sealed class OpenAIPromptExecutionSettings : PromptExecutionSettings } } + /// + /// An abstraction of additional settings for chat completion, see https://learn.microsoft.com/en-us/dotnet/api/azure.ai.openai.azurechatextensionsoptions. + /// This property is compatible only with Azure OpenAI. + /// + [Experimental("SKEXP0010")] + [JsonIgnore] + public AzureChatExtensionsOptions? AzureChatExtensionsOptions + { + get => this._azureChatExtensionsOptions; + + set + { + this.ThrowIfFrozen(); + this._azureChatExtensionsOptions = value; + } + } + /// public override void Freeze() { @@ -329,7 +346,8 @@ public sealed class OpenAIPromptExecutionSettings : PromptExecutionSettings User = this.User, ChatSystemPrompt = this.ChatSystemPrompt, Logprobs = this.Logprobs, - TopLogprobs = this.TopLogprobs + TopLogprobs = this.TopLogprobs, + AzureChatExtensionsOptions = this.AzureChatExtensionsOptions, }; } @@ -376,6 +394,7 @@ public sealed class OpenAIPromptExecutionSettings : PromptExecutionSettings /// Template configuration /// Default max tokens /// An instance of OpenAIPromptExecutionSettings + [Obsolete("This method is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions")] public static OpenAIPromptExecutionSettings FromExecutionSettingsWithData(PromptExecutionSettings? executionSettings, int? defaultMaxTokens = null) { var settings = FromExecutionSettings(executionSettings, defaultMaxTokens); @@ -407,6 +426,7 @@ public sealed class OpenAIPromptExecutionSettings : PromptExecutionSettings private string? _chatSystemPrompt; private bool? _logprobs; private int? _topLogprobs; + private AzureChatExtensionsOptions? _azureChatExtensionsOptions; #endregion } diff --git a/dotnet/src/Connectors/Connectors.OpenAI/OpenAIServiceCollectionExtensions.cs b/dotnet/src/Connectors/Connectors.OpenAI/OpenAIServiceCollectionExtensions.cs index 1dea76706e..b9d8b861db 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI/OpenAIServiceCollectionExtensions.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI/OpenAIServiceCollectionExtensions.cs @@ -906,6 +906,7 @@ public static class OpenAIServiceCollectionExtensions /// More information: /// [Experimental("SKEXP0010")] + [Obsolete("This method is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions")] public static IKernelBuilder AddAzureOpenAIChatCompletion( this IKernelBuilder builder, AzureOpenAIChatCompletionWithDataConfig config, @@ -936,6 +937,7 @@ public static class OpenAIServiceCollectionExtensions /// More information: /// [Experimental("SKEXP0010")] + [Obsolete("This method is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions")] public static IServiceCollection AddAzureOpenAIChatCompletion( this IServiceCollection services, AzureOpenAIChatCompletionWithDataConfig config, diff --git a/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/AzureSdk/AzureOpenAIWithDataChatMessageContentTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/AzureSdk/AzureOpenAIWithDataChatMessageContentTests.cs index 2dd599a9fd..f3dd1850d5 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/AzureSdk/AzureOpenAIWithDataChatMessageContentTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/AzureSdk/AzureOpenAIWithDataChatMessageContentTests.cs @@ -9,6 +9,8 @@ using Xunit; namespace SemanticKernel.Connectors.UnitTests.OpenAI.AzureSdk; +#pragma warning disable CS0618 // AzureOpenAIChatCompletionWithData is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions + /// /// Unit tests for class. /// diff --git a/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/AzureSdk/AzureOpenAIWithDataStreamingChatMessageContentTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/AzureSdk/AzureOpenAIWithDataStreamingChatMessageContentTests.cs index d3fd86820e..45597c6162 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/AzureSdk/AzureOpenAIWithDataStreamingChatMessageContentTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/AzureSdk/AzureOpenAIWithDataStreamingChatMessageContentTests.cs @@ -6,6 +6,8 @@ using Xunit; namespace SemanticKernel.Connectors.UnitTests.OpenAI.AzureSdk; +#pragma warning disable CS0618 // AzureOpenAIChatCompletionWithData is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions + /// /// Unit tests for class. /// diff --git a/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/ChatCompletion/AzureOpenAIChatCompletionServiceTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/ChatCompletion/AzureOpenAIChatCompletionServiceTests.cs index 159fcd7d85..22be8458c2 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/ChatCompletion/AzureOpenAIChatCompletionServiceTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/ChatCompletion/AzureOpenAIChatCompletionServiceTests.cs @@ -163,7 +163,18 @@ public sealed class AzureOpenAIChatCompletionServiceTests : IDisposable TokenSelectionBiases = new Dictionary { { 2, 3 } }, StopSequences = ["stop_sequence"], Logprobs = true, - TopLogprobs = 5 + TopLogprobs = 5, + AzureChatExtensionsOptions = new AzureChatExtensionsOptions + { + Extensions = + { + new AzureSearchChatExtensionConfiguration + { + SearchEndpoint = new Uri("http://test-search-endpoint"), + IndexName = "test-index-name" + } + } + } }; var chatHistory = new ChatHistory(); @@ -222,6 +233,14 @@ public sealed class AzureOpenAIChatCompletionServiceTests : IDisposable Assert.Equal("stop_sequence", content.GetProperty("stop")[0].GetString()); Assert.True(content.GetProperty("logprobs").GetBoolean()); Assert.Equal(5, content.GetProperty("top_logprobs").GetInt32()); + + var dataSources = content.GetProperty("data_sources"); + Assert.Equal(1, dataSources.GetArrayLength()); + Assert.Equal("azure_search", dataSources[0].GetProperty("type").GetString()); + + var dataSourceParameters = dataSources[0].GetProperty("parameters"); + Assert.Equal("http://test-search-endpoint/", dataSourceParameters.GetProperty("endpoint").GetString()); + Assert.Equal("test-index-name", dataSourceParameters.GetProperty("index_name").GetString()); } [Theory] diff --git a/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/ChatCompletionWithData/AzureOpenAIChatCompletionWithDataTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/ChatCompletionWithData/AzureOpenAIChatCompletionWithDataTests.cs index 8d2abbcd2a..782267039c 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/ChatCompletionWithData/AzureOpenAIChatCompletionWithDataTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/ChatCompletionWithData/AzureOpenAIChatCompletionWithDataTests.cs @@ -13,6 +13,8 @@ using Xunit; namespace SemanticKernel.Connectors.UnitTests.OpenAI.ChatCompletionWithData; +#pragma warning disable CS0618 // AzureOpenAIChatCompletionWithData is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions + /// /// Unit tests for /// diff --git a/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/OpenAIPromptExecutionSettingsTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/OpenAIPromptExecutionSettingsTests.cs index c951f821b3..b64649230d 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/OpenAIPromptExecutionSettingsTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/OpenAIPromptExecutionSettingsTests.cs @@ -32,6 +32,7 @@ public class OpenAIPromptExecutionSettingsTests Assert.Null(executionSettings.TokenSelectionBiases); Assert.Null(executionSettings.TopLogprobs); Assert.Null(executionSettings.Logprobs); + Assert.Null(executionSettings.AzureChatExtensionsOptions); Assert.Equal(128, executionSettings.MaxTokens); } @@ -248,8 +249,9 @@ public class OpenAIPromptExecutionSettingsTests var executionSettings = new OpenAIPromptExecutionSettings { StopSequences = [] }; // Act +#pragma warning disable CS0618 // AzureOpenAIChatCompletionWithData is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions var executionSettingsWithData = OpenAIPromptExecutionSettings.FromExecutionSettingsWithData(executionSettings); - +#pragma warning restore CS0618 // Assert Assert.Null(executionSettingsWithData.StopSequences); } diff --git a/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/OpenAIServiceCollectionExtensionsTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/OpenAIServiceCollectionExtensionsTests.cs index bc20179999..5cc41c3c88 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/OpenAIServiceCollectionExtensionsTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/OpenAIServiceCollectionExtensionsTests.cs @@ -17,6 +17,8 @@ using Xunit; namespace SemanticKernel.Connectors.UnitTests.OpenAI; +#pragma warning disable CS0618 // AzureOpenAIChatCompletionWithData is deprecated in favor of OpenAIPromptExecutionSettings.AzureChatExtensionsOptions + /// /// Unit tests for class. ///