diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index 496252dfa542..d72c90fd5089 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -6,10 +6,10 @@ - + - + diff --git a/dotnet/docs/OPENAI-CONNECTOR-MIGRATION.md b/dotnet/docs/OPENAI-CONNECTOR-MIGRATION.md index 784de5347fb0..00cf243fc193 100644 --- a/dotnet/docs/OPENAI-CONNECTOR-MIGRATION.md +++ b/dotnet/docs/OPENAI-CONNECTOR-MIGRATION.md @@ -53,13 +53,33 @@ tags: `ResultsPerPrompt`,`results_per_prompt` The `OpenAIFileService` was deprecated in the latest version of the OpenAI Connector. We strongly recommend to update your code to use the new `OpenAIClient.GetFileClient()` for file management operations. -## 5. SemanticKernel MetaPackage +## 5. OpenAI ChatCompletion custom endpoint + +The `OpenAIChatCompletionService` **experimental** constructor for custom endpoints will not attempt to auto-correct the endpoint and use it as is. + +We have the two only specific cases where we attempted to auto-correct the endpoint. + +1. If you provided `chat/completions` path before. Now those need to be removed as they are added automatically to the end of your original endpoint by `OpenAI SDK`. + + ```diff + - http://any-host-and-port/v1/chat/completions + + http://any-host-and-port/v1 + ``` + +2. If you provided a custom endpoint without any path. We won't be adding the `v1/` as the first path. Now the `v1` path needs to provided as part of your endpoint. + + ```diff + - http://any-host-and-port/ + + http://any-host-and-port/v1 + ``` + +## 6. SemanticKernel MetaPackage To be retro compatible with the new OpenAI and AzureOpenAI Connectors, our `Microsoft.SemanticKernel` meta package changed its dependency to use the new `Microsoft.SemanticKernel.Connectors.AzureOpenAI` package that depends on the `Microsoft.SemanticKernel.Connectors.OpenAI` package. This way if you are using the metapackage, no change is needed to get access to `Azure` related types. -## 6. Contents +## 7. Contents -### 6.1 OpenAIChatMessageContent +### 7.1 OpenAIChatMessageContent - The `Tools` property type has changed from `IReadOnlyList` to `IReadOnlyList`. @@ -67,13 +87,13 @@ To be retro compatible with the new OpenAI and AzureOpenAI Connectors, our `Micr - Metadata type `FunctionToolCalls` has changed from `IEnumerable` to `IEnumerable`. -### 6.2 OpenAIStreamingChatMessageContent +### 7.2 OpenAIStreamingChatMessageContent - The `FinishReason` property type has changed from `CompletionsFinishReason` to `FinishReason`. - The `ToolCallUpdate` property has been renamed to `ToolCallUpdates` and its type has changed from `StreamingToolCallUpdate?` to `IReadOnlyList?`. - The `AuthorName` property is not initialized because it's not provided by the underlying library anymore. -## 6.3 Metrics for AzureOpenAI Connector +## 7.3 Metrics for AzureOpenAI Connector The meter `s_meter = new("Microsoft.SemanticKernel.Connectors.OpenAI");` and the relevant counters still have old names that contain "openai" in them, such as: @@ -81,7 +101,7 @@ The meter `s_meter = new("Microsoft.SemanticKernel.Connectors.OpenAI");` and the - `semantic_kernel.connectors.openai.tokens.completion` - `semantic_kernel.connectors.openai.tokens.total` -## 7. Using Azure with your data (Data Sources) +## 8. Using Azure with your data (Data Sources) With the new `AzureOpenAIClient`, you can now specify your datasource thru the options and that requires a small change in your code to the new type. @@ -116,41 +136,41 @@ var promptExecutionSettings = new AzureOpenAIPromptExecutionSettings }; ``` -## 8. Breaking glass scenarios +## 9. Breaking glass scenarios Breaking glass scenarios are scenarios where you may need to update your code to use the new OpenAI Connector. Below are some of the breaking changes that you may need to be aware of. -#### 8.1 KernelContent Metadata +#### 9.1 KernelContent Metadata Some of the keys in the content metadata dictionary have changed, you will need to update your code to when using the previous key names. - `Created` -> `CreatedAt` -#### 8.2 Prompt Filter Results +#### 9.2 Prompt Filter Results The `PromptFilterResults` metadata type has changed from `IReadOnlyList` to `ContentFilterResultForPrompt`. -#### 8.3 Content Filter Results +#### 9.3 Content Filter Results The `ContentFilterResultsForPrompt` type has changed from `ContentFilterResultsForChoice` to `ContentFilterResultForResponse`. -#### 8.4 Finish Reason +#### 9.4 Finish Reason The FinishReason metadata string value has changed from `stop` to `Stop` -#### 8.5 Tool Calls +#### 9.5 Tool Calls The ToolCalls metadata string value has changed from `tool_calls` to `ToolCalls` -#### 8.6 LogProbs / Log Probability Info +#### 9.6 LogProbs / Log Probability Info The `LogProbabilityInfo` type has changed from `ChatChoiceLogProbabilityInfo` to `IReadOnlyList`. -#### 8.7 Finish Details, Index, and Enhancements +#### 9.7 Finish Details, Index, and Enhancements All of above have been removed. -#### 8.8 Token Usage +#### 9.8 Token Usage The Token usage naming convention from `OpenAI` changed from `Completion`, `Prompt` tokens to `Output` and `Input` respectively. You will need to update your code to use the new naming. @@ -172,13 +192,13 @@ The type also changed from `CompletionsUsage` to `ChatTokenUsage`. totalTokens: usage?.TotalTokens ?? 0; ``` -#### 8.9 OpenAIClient +#### 9.9 OpenAIClient The `OpenAIClient` type previously was a Azure specific namespace type but now it is an `OpenAI` SDK namespace type, you will need to update your code to use the new `OpenAIClient` type. When using Azure, you will need to update your code to use the new `AzureOpenAIClient` type. -#### 8.10 Pipeline Configuration +#### 9.10 Pipeline Configuration The new `OpenAI` SDK uses a different pipeline configuration, and has a dependency on `System.ClientModel` package. You will need to update your code to use the new `HttpClientPipelineTransport` transport configuration where before you were using `HttpClientTransport` from `Azure.Core.Pipeline`. diff --git a/dotnet/src/Agents/Core/History/ChatHistorySummarizationReducer.cs b/dotnet/src/Agents/Core/History/ChatHistorySummarizationReducer.cs index 8c2f022830d1..67720ab45112 100644 --- a/dotnet/src/Agents/Core/History/ChatHistorySummarizationReducer.cs +++ b/dotnet/src/Agents/Core/History/ChatHistorySummarizationReducer.cs @@ -29,7 +29,7 @@ public class ChatHistorySummarizationReducer : IChatHistoryReducer /// public const string DefaultSummarizationPrompt = """ - Provide a concise and complete summarizion of the entire dialog that does not exceed 5 sentences + Provide a concise and complete summarization of the entire dialog that does not exceed 5 sentences This summary must always: - Consider both user and assistant interactions diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI/Core/AzureClientCore.ChatCompletion.cs b/dotnet/src/Connectors/Connectors.AzureOpenAI/Core/AzureClientCore.ChatCompletion.cs index 1f68ada62532..7ed2beb563a3 100644 --- a/dotnet/src/Connectors/Connectors.AzureOpenAI/Core/AzureClientCore.ChatCompletion.cs +++ b/dotnet/src/Connectors/Connectors.AzureOpenAI/Core/AzureClientCore.ChatCompletion.cs @@ -43,7 +43,9 @@ protected override ChatCompletionOptions CreateChatCompletionOptions( TopP = (float?)executionSettings.TopP, FrequencyPenalty = (float?)executionSettings.FrequencyPenalty, PresencePenalty = (float?)executionSettings.PresencePenalty, +#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. Seed = executionSettings.Seed, +#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. EndUserId = executionSettings.User, TopLogProbabilityCount = executionSettings.TopLogprobs, IncludeLogProbabilities = executionSettings.Logprobs, diff --git a/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Core/ClientCoreTests.cs b/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Core/ClientCoreTests.cs index 017732f6d19c..2991afad740e 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Core/ClientCoreTests.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Core/ClientCoreTests.cs @@ -67,9 +67,9 @@ public void ItUsesEndpointAsExpected(string? clientBaseAddress, string? provided var clientCore = new ClientCore("model", "apiKey", endpoint: endpoint, httpClient: client); // Assert - Assert.Equal(endpoint ?? client?.BaseAddress ?? new Uri("https://api.openai.com/"), clientCore.Endpoint); + Assert.Equal(endpoint ?? client?.BaseAddress ?? new Uri("https://api.openai.com/v1"), clientCore.Endpoint); Assert.True(clientCore.Attributes.ContainsKey(AIServiceExtensions.EndpointKey)); - Assert.Equal(endpoint?.ToString() ?? client?.BaseAddress?.ToString() ?? "https://api.openai.com/", clientCore.Attributes[AIServiceExtensions.EndpointKey]); + Assert.Equal(endpoint?.ToString() ?? client?.BaseAddress?.ToString() ?? "https://api.openai.com/v1", clientCore.Attributes[AIServiceExtensions.EndpointKey]); client?.Dispose(); } diff --git a/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Services/OpenAIChatCompletionServiceTests.cs b/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Services/OpenAIChatCompletionServiceTests.cs index f560c9924977..93ca662446e0 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Services/OpenAIChatCompletionServiceTests.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Services/OpenAIChatCompletionServiceTests.cs @@ -76,10 +76,11 @@ public void ConstructorWithApiKeyWorksCorrectly(bool includeLoggerFactory) } [Theory] - [InlineData("http://localhost:1234/v1/chat/completions", "http://localhost:1234/v1/chat/completions")] // Uses full path when provided - [InlineData("http://localhost:1234/", "http://localhost:1234/v1/chat/completions")] - [InlineData("http://localhost:8080", "http://localhost:8080/v1/chat/completions")] - [InlineData("https://something:8080", "https://something:8080/v1/chat/completions")] // Accepts TLS Secured endpoints + [InlineData("http://localhost:1234", "http://localhost:1234/chat/completions")] + [InlineData("http://localhost:8080", "http://localhost:8080/chat/completions")] + [InlineData("https://something:8080", "https://something:8080/chat/completions")] // Accepts TLS Secured endpoints + [InlineData("http://localhost:1234/v2", "http://localhost:1234/v2/chat/completions")] + [InlineData("http://localhost:8080/v2", "http://localhost:8080/v2/chat/completions")] public async Task ItUsesCustomEndpointsWhenProvidedDirectlyAsync(string endpointProvided, string expectedEndpoint) { // Arrange @@ -95,10 +96,11 @@ public async Task ItUsesCustomEndpointsWhenProvidedDirectlyAsync(string endpoint } [Theory] - [InlineData("http://localhost:1234/v1/chat/completions", "http://localhost:1234/v1/chat/completions")] // Uses full path when provided - [InlineData("http://localhost:1234/", "http://localhost:1234/v1/chat/completions")] - [InlineData("http://localhost:8080", "http://localhost:8080/v1/chat/completions")] - [InlineData("https://something:8080", "https://something:8080/v1/chat/completions")] // Accepts TLS Secured endpoints + [InlineData("http://localhost:1234", "http://localhost:1234/chat/completions")] + [InlineData("http://localhost:8080", "http://localhost:8080/chat/completions")] + [InlineData("https://something:8080", "https://something:8080/chat/completions")] // Accepts TLS Secured endpoints + [InlineData("http://localhost:1234/v2", "http://localhost:1234/v2/chat/completions")] + [InlineData("http://localhost:8080/v2", "http://localhost:8080/v2/chat/completions")] public async Task ItUsesCustomEndpointsWhenProvidedAsBaseAddressAsync(string endpointProvided, string expectedEndpoint) { // Arrange @@ -127,7 +129,7 @@ public async Task ItUsesHttpClientEndpointIfProvidedEndpointIsMissingAsync() await chatCompletion.GetChatMessageContentsAsync(this._chatHistoryForTest, this._executionSettings); // Assert - Assert.Equal("http://localhost:12312/v1/chat/completions", this._messageHandlerStub.RequestUri!.ToString()); + Assert.Equal("http://localhost:12312/chat/completions", this._messageHandlerStub.RequestUri!.ToString()); } [Fact] diff --git a/dotnet/src/Connectors/Connectors.OpenAI/Core/ClientCore.ChatCompletion.cs b/dotnet/src/Connectors/Connectors.OpenAI/Core/ClientCore.ChatCompletion.cs index ca511b55c31a..7e45b058b9db 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI/Core/ClientCore.ChatCompletion.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI/Core/ClientCore.ChatCompletion.cs @@ -650,7 +650,9 @@ protected virtual ChatCompletionOptions CreateChatCompletionOptions( TopP = (float?)executionSettings.TopP, FrequencyPenalty = (float?)executionSettings.FrequencyPenalty, PresencePenalty = (float?)executionSettings.PresencePenalty, +#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. Seed = executionSettings.Seed, +#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. EndUserId = executionSettings.User, TopLogProbabilityCount = executionSettings.TopLogprobs, IncludeLogProbabilities = executionSettings.Logprobs, diff --git a/dotnet/src/Connectors/Connectors.OpenAI/Core/ClientCore.TextToAudio.cs b/dotnet/src/Connectors/Connectors.OpenAI/Core/ClientCore.TextToAudio.cs index 1a34fe7a0230..0b5af67aaea7 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI/Core/ClientCore.TextToAudio.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI/Core/ClientCore.TextToAudio.cs @@ -37,7 +37,7 @@ internal async Task> GetAudioContentsAsync( SpeechGenerationOptions options = new() { ResponseFormat = responseFormat, - Speed = audioExecutionSettings.Speed, + SpeedRatio = audioExecutionSettings.Speed, }; ClientResult response = await RunRequestAsync(() => this.Client!.GetAudioClient(targetModel).GenerateSpeechAsync(prompt, GetGeneratedSpeechVoice(audioExecutionSettings?.Voice), options, cancellationToken)).ConfigureAwait(false); @@ -58,15 +58,17 @@ private static GeneratedSpeechVoice GetGeneratedSpeechVoice(string? voice) }; private static (GeneratedSpeechFormat? Format, string? MimeType) GetGeneratedSpeechFormatAndMimeType(string? format) - => format?.ToUpperInvariant() switch + { + switch (format?.ToUpperInvariant()) { - "WAV" => (GeneratedSpeechFormat.Wav, "audio/wav"), - "MP3" => (GeneratedSpeechFormat.Mp3, "audio/mpeg"), - "OPUS" => (GeneratedSpeechFormat.Opus, "audio/opus"), - "FLAC" => (GeneratedSpeechFormat.Flac, "audio/flac"), - "AAC" => (GeneratedSpeechFormat.Aac, "audio/aac"), - "PCM" => (GeneratedSpeechFormat.Pcm, "audio/l16"), - null => (null, null), - _ => throw new NotSupportedException($"The format '{format}' is not supported.") - }; + case "WAV": return (GeneratedSpeechFormat.Wav, "audio/wav"); + case "MP3": return (GeneratedSpeechFormat.Mp3, "audio/mpeg"); + case "OPUS": return (GeneratedSpeechFormat.Opus, "audio/opus"); + case "FLAC": return (GeneratedSpeechFormat.Flac, "audio/flac"); + case "AAC": return (GeneratedSpeechFormat.Aac, "audio/aac"); + case "PCM": return (GeneratedSpeechFormat.Pcm, "audio/l16"); + case null: return (null, null); + default: throw new NotSupportedException($"The format '{format}' is not supported."); + } + } } diff --git a/dotnet/src/Connectors/Connectors.OpenAI/Core/ClientCore.cs b/dotnet/src/Connectors/Connectors.OpenAI/Core/ClientCore.cs index 271aa2321ea2..843768bc17c2 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI/Core/ClientCore.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI/Core/ClientCore.cs @@ -36,7 +36,7 @@ internal partial class ClientCore /// /// Default OpenAI API endpoint. /// - private const string OpenAIEndpoint = "https://api.openai.com/"; + private const string OpenAIV1Endpoint = "https://api.openai.com/v1"; /// /// Identifier of the default model to use @@ -104,7 +104,7 @@ internal ClientCore( if (this.Endpoint is null) { Verify.NotNullOrWhiteSpace(apiKey); // For Public OpenAI Endpoint a key must be provided. - this.Endpoint = new Uri(OpenAIEndpoint); + this.Endpoint = new Uri(OpenAIV1Endpoint); } else if (string.IsNullOrEmpty(apiKey)) { diff --git a/dotnet/src/Connectors/Connectors.OpenAI/Services/OpenAIChatCompletionService.cs b/dotnet/src/Connectors/Connectors.OpenAI/Services/OpenAIChatCompletionService.cs index f544d8a5c61c..a3f8d96d6e51 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI/Services/OpenAIChatCompletionService.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI/Services/OpenAIChatCompletionService.cs @@ -67,29 +67,11 @@ public OpenAIChatCompletionService( HttpClient? httpClient = null, ILoggerFactory? loggerFactory = null) { - Uri? internalClientEndpoint = null; - var providedEndpoint = endpoint ?? httpClient?.BaseAddress; - if (providedEndpoint is not null) - { - // As OpenAI Client automatically adds the chat completions endpoint, we remove it to avoid duplication. - const string PathAndQueryPattern = "v1/chat/completions"; - var providedEndpointText = providedEndpoint.ToString(); - int index = providedEndpointText.IndexOf(PathAndQueryPattern, StringComparison.OrdinalIgnoreCase); - if (index >= 0) - { - internalClientEndpoint = new Uri($"{providedEndpointText.Substring(0, index)}{providedEndpointText.Substring(index + PathAndQueryPattern.Length)}"); - } - else - { - internalClientEndpoint = providedEndpoint; - } - } - this._client = new( modelId, apiKey, organization, - internalClientEndpoint, + endpoint ?? httpClient?.BaseAddress, httpClient, loggerFactory?.CreateLogger(typeof(OpenAIChatCompletionService))); }