diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Connectors.AzureOpenAI.UnitTests.csproj b/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Connectors.AzureOpenAI.UnitTests.csproj
index 5952d571a09f..a0a695a6719c 100644
--- a/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Connectors.AzureOpenAI.UnitTests.csproj
+++ b/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Connectors.AzureOpenAI.UnitTests.csproj
@@ -8,7 +8,7 @@
true
enable
false
- $(NoWarn);SKEXP0001;SKEXP0010;CA2007,CA1806,CA1869,CA1861,IDE0300,VSTHRD111
+ $(NoWarn);SKEXP0001;SKEXP0010;CA2007,CA1806,CA1869,CA1861,IDE0300,VSTHRD111,IDE1006
@@ -27,7 +27,7 @@
-
+
diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Extensions/AzureOpenAIServiceCollectionExtensionsTests.cs b/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Extensions/AzureOpenAIServiceCollectionExtensionsTests.cs
index 152a968a6bb1..ca4899258b21 100644
--- a/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Extensions/AzureOpenAIServiceCollectionExtensionsTests.cs
+++ b/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Extensions/AzureOpenAIServiceCollectionExtensionsTests.cs
@@ -7,6 +7,7 @@
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.AzureOpenAI;
+using Microsoft.SemanticKernel.Embeddings;
using Microsoft.SemanticKernel.TextGeneration;
namespace SemanticKernel.Connectors.AzureOpenAI.UnitTests.Extensions;
@@ -21,8 +22,8 @@ public sealed class AzureOpenAIServiceCollectionExtensionsTests
[Theory]
[InlineData(InitializationType.ApiKey)]
[InlineData(InitializationType.TokenCredential)]
- [InlineData(InitializationType.OpenAIClientInline)]
- [InlineData(InitializationType.OpenAIClientInServiceProvider)]
+ [InlineData(InitializationType.ClientInline)]
+ [InlineData(InitializationType.ClientInServiceProvider)]
public void ServiceCollectionAddAzureOpenAIChatCompletionAddsValidService(InitializationType type)
{
// Arrange
@@ -37,8 +38,8 @@ public void ServiceCollectionAddAzureOpenAIChatCompletionAddsValidService(Initia
{
InitializationType.ApiKey => builder.Services.AddAzureOpenAIChatCompletion("deployment-name", "https://endpoint", "api-key"),
InitializationType.TokenCredential => builder.Services.AddAzureOpenAIChatCompletion("deployment-name", "https://endpoint", credentials),
- InitializationType.OpenAIClientInline => builder.Services.AddAzureOpenAIChatCompletion("deployment-name", client),
- InitializationType.OpenAIClientInServiceProvider => builder.Services.AddAzureOpenAIChatCompletion("deployment-name"),
+ InitializationType.ClientInline => builder.Services.AddAzureOpenAIChatCompletion("deployment-name", client),
+ InitializationType.ClientInServiceProvider => builder.Services.AddAzureOpenAIChatCompletion("deployment-name"),
_ => builder.Services
};
@@ -52,12 +53,47 @@ public void ServiceCollectionAddAzureOpenAIChatCompletionAddsValidService(Initia
#endregion
+ #region Text embeddings
+
+ [Theory]
+ [InlineData(InitializationType.ApiKey)]
+ [InlineData(InitializationType.TokenCredential)]
+ [InlineData(InitializationType.ClientInline)]
+ [InlineData(InitializationType.ClientInServiceProvider)]
+ public void ServiceCollectionAddAzureOpenAITextEmbeddingGenerationAddsValidService(InitializationType type)
+ {
+ // Arrange
+ var credentials = DelegatedTokenCredential.Create((_, _) => new AccessToken());
+ var client = new AzureOpenAIClient(new Uri("http://localhost"), "key");
+ var builder = Kernel.CreateBuilder();
+
+ builder.Services.AddSingleton(client);
+
+ // Act
+ IServiceCollection collection = type switch
+ {
+ InitializationType.ApiKey => builder.Services.AddAzureOpenAITextEmbeddingGeneration("deployment-name", "https://endpoint", "api-key"),
+ InitializationType.TokenCredential => builder.Services.AddAzureOpenAITextEmbeddingGeneration("deployment-name", "https://endpoint", credentials),
+ InitializationType.ClientInline => builder.Services.AddAzureOpenAITextEmbeddingGeneration("deployment-name", client),
+ InitializationType.ClientInServiceProvider => builder.Services.AddAzureOpenAITextEmbeddingGeneration("deployment-name"),
+ _ => builder.Services
+ };
+
+ // Assert
+ var service = builder.Build().GetRequiredService();
+
+ Assert.NotNull(service);
+ Assert.True(service is AzureOpenAITextEmbeddingGenerationService);
+ }
+
+ #endregion
+
public enum InitializationType
{
ApiKey,
TokenCredential,
- OpenAIClientInline,
- OpenAIClientInServiceProvider,
- OpenAIClientEndpoint,
+ ClientInline,
+ ClientInServiceProvider,
+ ClientEndpoint,
}
}
diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Extensions/AzureOpenAIServiceKernelBuilderExtensionsTests.cs b/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Extensions/AzureOpenAIServiceKernelBuilderExtensionsTests.cs
index 13c5d31ce427..8c5515516ca5 100644
--- a/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Extensions/AzureOpenAIServiceKernelBuilderExtensionsTests.cs
+++ b/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Extensions/AzureOpenAIServiceKernelBuilderExtensionsTests.cs
@@ -7,6 +7,7 @@
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.AzureOpenAI;
+using Microsoft.SemanticKernel.Embeddings;
using Microsoft.SemanticKernel.TextGeneration;
namespace SemanticKernel.Connectors.AzureOpenAI.UnitTests.Extensions;
@@ -52,6 +53,41 @@ public void KernelBuilderAddAzureOpenAIChatCompletionAddsValidService(Initializa
#endregion
+ #region Text embeddings
+
+ [Theory]
+ [InlineData(InitializationType.ApiKey)]
+ [InlineData(InitializationType.TokenCredential)]
+ [InlineData(InitializationType.OpenAIClientInline)]
+ [InlineData(InitializationType.OpenAIClientInServiceProvider)]
+ public void KernelBuilderAddAzureOpenAITextEmbeddingGenerationAddsValidService(InitializationType type)
+ {
+ // Arrange
+ var credentials = DelegatedTokenCredential.Create((_, _) => new AccessToken());
+ var client = new AzureOpenAIClient(new Uri("http://localhost"), "key");
+ var builder = Kernel.CreateBuilder();
+
+ builder.Services.AddSingleton(client);
+
+ // Act
+ builder = type switch
+ {
+ InitializationType.ApiKey => builder.AddAzureOpenAITextEmbeddingGeneration("deployment-name", "https://endpoint", "api-key"),
+ InitializationType.TokenCredential => builder.AddAzureOpenAITextEmbeddingGeneration("deployment-name", "https://endpoint", credentials),
+ InitializationType.OpenAIClientInline => builder.AddAzureOpenAITextEmbeddingGeneration("deployment-name", client),
+ InitializationType.OpenAIClientInServiceProvider => builder.AddAzureOpenAITextEmbeddingGeneration("deployment-name"),
+ _ => builder
+ };
+
+ // Assert
+ var service = builder.Build().GetRequiredService();
+
+ Assert.NotNull(service);
+ Assert.True(service is AzureOpenAITextEmbeddingGenerationService);
+ }
+
+ #endregion
+
public enum InitializationType
{
ApiKey,
diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Services/AzureOpenAITextEmbeddingGenerationServiceTests.cs b/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Services/AzureOpenAITextEmbeddingGenerationServiceTests.cs
new file mode 100644
index 000000000000..738364429cff
--- /dev/null
+++ b/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Services/AzureOpenAITextEmbeddingGenerationServiceTests.cs
@@ -0,0 +1,90 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.ClientModel;
+using System.IO;
+using System.Net;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using Azure.AI.OpenAI;
+using Microsoft.SemanticKernel;
+using Microsoft.SemanticKernel.Connectors.AzureOpenAI;
+using Microsoft.SemanticKernel.Services;
+
+namespace SemanticKernel.Connectors.AzureOpenAI.UnitTests.Services;
+
+///
+/// Unit tests for class.
+///
+public class AzureOpenAITextEmbeddingGenerationServiceTests
+{
+ [Fact]
+ public void ItCanBeInstantiatedAndPropertiesSetAsExpected()
+ {
+ // Arrange
+ var sut = new AzureOpenAITextEmbeddingGenerationService("deployment-name", "https://endpoint", "api-key", modelId: "model", dimensions: 2);
+ var sutWithAzureOpenAIClient = new AzureOpenAITextEmbeddingGenerationService("deployment-name", new AzureOpenAIClient(new Uri("https://endpoint"), new ApiKeyCredential("apiKey")), modelId: "model", dimensions: 2);
+
+ // Assert
+ Assert.NotNull(sut);
+ Assert.NotNull(sutWithAzureOpenAIClient);
+ Assert.Equal("model", sut.Attributes[AIServiceExtensions.ModelIdKey]);
+ Assert.Equal("model", sutWithAzureOpenAIClient.Attributes[AIServiceExtensions.ModelIdKey]);
+ }
+
+ [Fact]
+ public async Task ItGetEmbeddingsAsyncReturnsEmptyWhenProvidedDataIsEmpty()
+ {
+ // Arrange
+ var sut = new AzureOpenAITextEmbeddingGenerationService("deployment-name", "https://endpoint", "api-key");
+
+ // Act
+ var result = await sut.GenerateEmbeddingsAsync([], null, CancellationToken.None);
+
+ // Assert
+ Assert.Empty(result);
+ }
+
+ [Fact]
+ public async Task GetEmbeddingsAsyncReturnsEmptyWhenProvidedDataIsWhitespace()
+ {
+ // Arrange
+ using HttpMessageHandlerStub handler = new()
+ {
+ ResponseToReturn = new HttpResponseMessage(HttpStatusCode.OK)
+ {
+ Content = new StringContent(File.ReadAllText("./TestData/text-embeddings-response.txt"))
+ }
+ };
+ using HttpClient client = new(handler);
+
+ var sut = new AzureOpenAITextEmbeddingGenerationService("deployment-name", "https://endpoint", "api-key", httpClient: client);
+
+ // Act
+ var result = await sut.GenerateEmbeddingsAsync(["test"], null, CancellationToken.None);
+
+ // Assert
+ Assert.Single(result);
+ Assert.Equal(4, result[0].Length);
+ }
+
+ [Fact]
+ public async Task ItThrowsIfNumberOfResultsDiffersFromInputsAsync()
+ {
+ // Arrange
+ using HttpMessageHandlerStub handler = new()
+ {
+ ResponseToReturn = new HttpResponseMessage(HttpStatusCode.OK)
+ {
+ Content = new StringContent(File.ReadAllText("./TestData/text-embeddings-multiple-response.txt"))
+ }
+ };
+ using HttpClient client = new(handler);
+
+ var sut = new AzureOpenAITextEmbeddingGenerationService("deployment-name", "https://endpoint", "api-key", httpClient: client);
+
+ // Act & Assert
+ await Assert.ThrowsAsync(async () => await sut.GenerateEmbeddingsAsync(["test"], null, CancellationToken.None));
+ }
+}
diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/TestData/text-embeddings-multiple-response.txt b/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/TestData/text-embeddings-multiple-response.txt
new file mode 100644
index 000000000000..46a9581cf0cc
--- /dev/null
+++ b/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/TestData/text-embeddings-multiple-response.txt
@@ -0,0 +1,20 @@
+{
+ "object": "list",
+ "data": [
+ {
+ "object": "embedding",
+ "index": 0,
+ "embedding": "zcyMP83MDEAzM1NAzcyMQA=="
+ },
+ {
+ "object": "embedding",
+ "index": 1,
+ "embedding": "zcyMP83MDEAzM1NAzcyMQA=="
+ }
+ ],
+ "model": "text-embedding-ada-002",
+ "usage": {
+ "prompt_tokens": 7,
+ "total_tokens": 7
+ }
+}
diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/TestData/text-embeddings-response.txt b/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/TestData/text-embeddings-response.txt
new file mode 100644
index 000000000000..c715b851b78c
--- /dev/null
+++ b/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/TestData/text-embeddings-response.txt
@@ -0,0 +1,15 @@
+{
+ "object": "list",
+ "data": [
+ {
+ "object": "embedding",
+ "index": 0,
+ "embedding": "zcyMP83MDEAzM1NAzcyMQA=="
+ }
+ ],
+ "model": "text-embedding-ada-002",
+ "usage": {
+ "prompt_tokens": 7,
+ "total_tokens": 7
+ }
+}
diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI/Connectors.AzureOpenAI.csproj b/dotnet/src/Connectors/Connectors.AzureOpenAI/Connectors.AzureOpenAI.csproj
index 29fbd3da46d3..35c31788610d 100644
--- a/dotnet/src/Connectors/Connectors.AzureOpenAI/Connectors.AzureOpenAI.csproj
+++ b/dotnet/src/Connectors/Connectors.AzureOpenAI/Connectors.AzureOpenAI.csproj
@@ -21,18 +21,10 @@
Semantic Kernel connectors for Azure OpenAI. Contains clients for text generation, chat completion, embedding and DALL-E text to image.
-
-
-
-
-
-
-
-
diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI/Extensions/AzureOpenAIServiceCollectionExtensions.cs b/dotnet/src/Connectors/Connectors.AzureOpenAI/Extensions/AzureOpenAIServiceCollectionExtensions.cs
index f946d09026a0..e25eac02789b 100644
--- a/dotnet/src/Connectors/Connectors.AzureOpenAI/Extensions/AzureOpenAIServiceCollectionExtensions.cs
+++ b/dotnet/src/Connectors/Connectors.AzureOpenAI/Extensions/AzureOpenAIServiceCollectionExtensions.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
+using System.Diagnostics.CodeAnalysis;
using System.Net.Http;
using Azure;
using Azure.AI.OpenAI;
@@ -9,6 +10,7 @@
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.AzureOpenAI;
+using Microsoft.SemanticKernel.Embeddings;
using Microsoft.SemanticKernel.Http;
using Microsoft.SemanticKernel.TextGeneration;
@@ -44,7 +46,6 @@ public static IKernelBuilder AddAzureOpenAIChatCompletion(
HttpClient? httpClient = null)
{
Verify.NotNull(builder);
- Verify.NotNullOrWhiteSpace(deploymentName);
Verify.NotNullOrWhiteSpace(endpoint);
Verify.NotNullOrWhiteSpace(apiKey);
@@ -83,7 +84,6 @@ public static IServiceCollection AddAzureOpenAIChatCompletion(
string? modelId = null)
{
Verify.NotNull(services);
- Verify.NotNullOrWhiteSpace(deploymentName);
Verify.NotNullOrWhiteSpace(endpoint);
Verify.NotNullOrWhiteSpace(apiKey);
@@ -124,7 +124,6 @@ public static IKernelBuilder AddAzureOpenAIChatCompletion(
HttpClient? httpClient = null)
{
Verify.NotNull(builder);
- Verify.NotNullOrWhiteSpace(deploymentName);
Verify.NotNullOrWhiteSpace(endpoint);
Verify.NotNull(credentials);
@@ -163,7 +162,6 @@ public static IServiceCollection AddAzureOpenAIChatCompletion(
string? modelId = null)
{
Verify.NotNull(services);
- Verify.NotNullOrWhiteSpace(deploymentName);
Verify.NotNullOrWhiteSpace(endpoint);
Verify.NotNull(credentials);
@@ -241,6 +239,218 @@ public static IServiceCollection AddAzureOpenAIChatCompletion(
#endregion
+ #region Text Embedding
+
+ ///
+ /// Adds an Azure OpenAI text embeddings service to the list.
+ ///
+ /// The instance to augment.
+ /// Azure OpenAI deployment name, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource
+ /// Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart
+ /// Azure OpenAI API key, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart
+ /// A local identifier for the given AI service
+ /// Model identifier, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart
+ /// The HttpClient to use with this service.
+ /// The number of dimensions the resulting output embeddings should have. Only supported in "text-embedding-3" and later models.
+ /// The same instance as .
+ [Experimental("SKEXP0010")]
+ public static IKernelBuilder AddAzureOpenAITextEmbeddingGeneration(
+ this IKernelBuilder builder,
+ string deploymentName,
+ string endpoint,
+ string apiKey,
+ string? serviceId = null,
+ string? modelId = null,
+ HttpClient? httpClient = null,
+ int? dimensions = null)
+ {
+ Verify.NotNull(builder);
+
+ builder.Services.AddKeyedSingleton(serviceId, (serviceProvider, _) =>
+ new AzureOpenAITextEmbeddingGenerationService(
+ deploymentName,
+ endpoint,
+ apiKey,
+ modelId,
+ HttpClientProvider.GetHttpClient(httpClient, serviceProvider),
+ serviceProvider.GetService(),
+ dimensions));
+
+ return builder;
+ }
+
+ ///
+ /// Adds an Azure OpenAI text embeddings service to the list.
+ ///
+ /// The instance to augment.
+ /// Azure OpenAI deployment name, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource
+ /// Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart
+ /// Azure OpenAI API key, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart
+ /// A local identifier for the given AI service
+ /// Model identifier, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart
+ /// The number of dimensions the resulting output embeddings should have. Only supported in "text-embedding-3" and later models.
+ /// The same instance as .
+ [Experimental("SKEXP0010")]
+ public static IServiceCollection AddAzureOpenAITextEmbeddingGeneration(
+ this IServiceCollection services,
+ string deploymentName,
+ string endpoint,
+ string apiKey,
+ string? serviceId = null,
+ string? modelId = null,
+ int? dimensions = null)
+ {
+ Verify.NotNull(services);
+
+ return services.AddKeyedSingleton(serviceId, (serviceProvider, _) =>
+ new AzureOpenAITextEmbeddingGenerationService(
+ deploymentName,
+ endpoint,
+ apiKey,
+ modelId,
+ HttpClientProvider.GetHttpClient(serviceProvider),
+ serviceProvider.GetService(),
+ dimensions));
+ }
+
+ ///
+ /// Adds an Azure OpenAI text embeddings service to the list.
+ ///
+ /// The instance to augment.
+ /// Azure OpenAI deployment name, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource
+ /// Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart
+ /// Token credentials, e.g. DefaultAzureCredential, ManagedIdentityCredential, EnvironmentCredential, etc.
+ /// A local identifier for the given AI service
+ /// Model identifier, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart
+ /// The HttpClient to use with this service.
+ /// The number of dimensions the resulting output embeddings should have. Only supported in "text-embedding-3" and later models.
+ /// The same instance as .
+ [Experimental("SKEXP0010")]
+ public static IKernelBuilder AddAzureOpenAITextEmbeddingGeneration(
+ this IKernelBuilder builder,
+ string deploymentName,
+ string endpoint,
+ TokenCredential credential,
+ string? serviceId = null,
+ string? modelId = null,
+ HttpClient? httpClient = null,
+ int? dimensions = null)
+ {
+ Verify.NotNull(builder);
+ Verify.NotNull(credential);
+
+ builder.Services.AddKeyedSingleton(serviceId, (serviceProvider, _) =>
+ new AzureOpenAITextEmbeddingGenerationService(
+ deploymentName,
+ endpoint,
+ credential,
+ modelId,
+ HttpClientProvider.GetHttpClient(httpClient, serviceProvider),
+ serviceProvider.GetService(),
+ dimensions));
+
+ return builder;
+ }
+
+ ///
+ /// Adds an Azure OpenAI text embeddings service to the list.
+ ///
+ /// The instance to augment.
+ /// Azure OpenAI deployment name, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource
+ /// Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart
+ /// Token credentials, e.g. DefaultAzureCredential, ManagedIdentityCredential, EnvironmentCredential, etc.
+ /// A local identifier for the given AI service
+ /// Model identifier, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart
+ /// The number of dimensions the resulting output embeddings should have. Only supported in "text-embedding-3" and later models.
+ /// The same instance as .
+ [Experimental("SKEXP0010")]
+ public static IServiceCollection AddAzureOpenAITextEmbeddingGeneration(
+ this IServiceCollection services,
+ string deploymentName,
+ string endpoint,
+ TokenCredential credential,
+ string? serviceId = null,
+ string? modelId = null,
+ int? dimensions = null)
+ {
+ Verify.NotNull(services);
+ Verify.NotNull(credential);
+
+ return services.AddKeyedSingleton(serviceId, (serviceProvider, _) =>
+ new AzureOpenAITextEmbeddingGenerationService(
+ deploymentName,
+ endpoint,
+ credential,
+ modelId,
+ HttpClientProvider.GetHttpClient(serviceProvider),
+ serviceProvider.GetService(),
+ dimensions));
+ }
+
+ ///
+ /// Adds an Azure OpenAI text embeddings service to the list.
+ ///
+ /// The instance to augment.
+ /// Azure OpenAI deployment name, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource
+ /// to use for the service. If null, one must be available in the service provider when this service is resolved.
+ /// A local identifier for the given AI service
+ /// Model identifier, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart
+ /// The number of dimensions the resulting output embeddings should have. Only supported in "text-embedding-3" and later models.
+ /// The same instance as .
+ [Experimental("SKEXP0010")]
+ public static IKernelBuilder AddAzureOpenAITextEmbeddingGeneration(
+ this IKernelBuilder builder,
+ string deploymentName,
+ AzureOpenAIClient? azureOpenAIClient = null,
+ string? serviceId = null,
+ string? modelId = null,
+ int? dimensions = null)
+ {
+ Verify.NotNull(builder);
+
+ builder.Services.AddKeyedSingleton(serviceId, (serviceProvider, _) =>
+ new AzureOpenAITextEmbeddingGenerationService(
+ deploymentName,
+ azureOpenAIClient ?? serviceProvider.GetRequiredService(),
+ modelId,
+ serviceProvider.GetService(),
+ dimensions));
+
+ return builder;
+ }
+
+ ///
+ /// Adds an Azure OpenAI text embeddings service to the list.
+ ///
+ /// The instance to augment.
+ /// Azure OpenAI deployment name, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource
+ /// to use for the service. If null, one must be available in the service provider when this service is resolved.
+ /// A local identifier for the given AI service
+ /// Model identifier, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart
+ /// The number of dimensions the resulting output embeddings should have. Only supported in "text-embedding-3" and later models.
+ /// The same instance as .
+ [Experimental("SKEXP0010")]
+ public static IServiceCollection AddAzureOpenAITextEmbeddingGeneration(
+ this IServiceCollection services,
+ string deploymentName,
+ AzureOpenAIClient? azureOpenAIClient = null,
+ string? serviceId = null,
+ string? modelId = null,
+ int? dimensions = null)
+ {
+ Verify.NotNull(services);
+
+ return services.AddKeyedSingleton(serviceId, (serviceProvider, _) =>
+ new AzureOpenAITextEmbeddingGenerationService(
+ deploymentName,
+ azureOpenAIClient ?? serviceProvider.GetRequiredService(),
+ modelId,
+ serviceProvider.GetService(),
+ dimensions));
+ }
+
+ #endregion
+
private static AzureOpenAIClient CreateAzureOpenAIClient(string endpoint, AzureKeyCredential credentials, HttpClient? httpClient) =>
new(new Uri(endpoint), credentials, ClientCore.GetAzureOpenAIClientOptions(httpClient));
diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI/Services/AzureOpenAITextEmbeddingGenerationService.cs b/dotnet/src/Connectors/Connectors.AzureOpenAI/Services/AzureOpenAITextEmbeddingGenerationService.cs
index 9119a9005939..31159da6f0a5 100644
--- a/dotnet/src/Connectors/Connectors.AzureOpenAI/Services/AzureOpenAITextEmbeddingGenerationService.cs
+++ b/dotnet/src/Connectors/Connectors.AzureOpenAI/Services/AzureOpenAITextEmbeddingGenerationService.cs
@@ -79,13 +79,13 @@ public AzureOpenAITextEmbeddingGenerationService(
/// Creates a new client.
///
/// Azure OpenAI deployment name, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource
- /// Custom for HTTP requests.
+ /// Custom for HTTP requests.
/// Azure OpenAI model id, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource
/// The to use for logging. If null, no logging will be performed.
/// The number of dimensions the resulting output embeddings should have. Only supported in "text-embedding-3" and later models.
public AzureOpenAITextEmbeddingGenerationService(
string deploymentName,
- OpenAIClient openAIClient,
+ AzureOpenAIClient openAIClient,
string? modelId = null,
ILoggerFactory? loggerFactory = null,
int? dimensions = null)
diff --git a/dotnet/src/IntegrationTestsV2/Connectors/AzureOpenAI/AzureOpenAITextEmbeddingTests.cs b/dotnet/src/IntegrationTestsV2/Connectors/AzureOpenAI/AzureOpenAITextEmbeddingTests.cs
new file mode 100644
index 000000000000..1dfc39670416
--- /dev/null
+++ b/dotnet/src/IntegrationTestsV2/Connectors/AzureOpenAI/AzureOpenAITextEmbeddingTests.cs
@@ -0,0 +1,71 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Threading.Tasks;
+using Microsoft.Extensions.Configuration;
+using Microsoft.SemanticKernel.Connectors.AzureOpenAI;
+using Microsoft.SemanticKernel.Embeddings;
+using SemanticKernel.IntegrationTests.TestSettings;
+using Xunit;
+
+namespace SemanticKernel.IntegrationTestsV2.Connectors.AzureOpenAI;
+
+public sealed class AzureOpenAITextEmbeddingTests
+{
+ public AzureOpenAITextEmbeddingTests()
+ {
+ var config = this._configuration.GetSection("AzureOpenAIEmbeddings").Get();
+ Assert.NotNull(config);
+ this._azureOpenAIConfiguration = config;
+ }
+
+ [Theory]
+ [InlineData("test sentence")]
+ public async Task AzureOpenAITestAsync(string testInputString)
+ {
+ // Arrange
+ var embeddingGenerator = new AzureOpenAITextEmbeddingGenerationService(
+ this._azureOpenAIConfiguration.DeploymentName,
+ this._azureOpenAIConfiguration.Endpoint,
+ this._azureOpenAIConfiguration.ApiKey);
+
+ // Act
+ var singleResult = await embeddingGenerator.GenerateEmbeddingAsync(testInputString);
+ var batchResult = await embeddingGenerator.GenerateEmbeddingsAsync([testInputString, testInputString, testInputString]);
+
+ // Assert
+ Assert.Equal(AdaVectorLength, singleResult.Length);
+ Assert.Equal(3, batchResult.Count);
+ }
+
+ [Theory]
+ [InlineData(null, 3072)]
+ [InlineData(1024, 1024)]
+ public async Task AzureOpenAIWithDimensionsAsync(int? dimensions, int expectedVectorLength)
+ {
+ // Arrange
+ const string TestInputString = "test sentence";
+
+ var embeddingGenerator = new AzureOpenAITextEmbeddingGenerationService(
+ "text-embedding-3-large",
+ this._azureOpenAIConfiguration.Endpoint,
+ this._azureOpenAIConfiguration.ApiKey,
+ dimensions: dimensions);
+
+ // Act
+ var result = await embeddingGenerator.GenerateEmbeddingAsync(TestInputString);
+
+ // Assert
+ Assert.Equal(expectedVectorLength, result.Length);
+ }
+
+ private readonly AzureOpenAIConfiguration _azureOpenAIConfiguration;
+
+ private const int AdaVectorLength = 1536;
+
+ private readonly IConfigurationRoot _configuration = new ConfigurationBuilder()
+ .AddJsonFile(path: "testsettings.json", optional: true, reloadOnChange: true)
+ .AddJsonFile(path: "testsettings.development.json", optional: true, reloadOnChange: true)
+ .AddEnvironmentVariables()
+ .AddUserSecrets()
+ .Build();
+}
diff --git a/dotnet/src/InternalUtilities/test/AssertExtensions.cs b/dotnet/src/InternalUtilities/test/AssertExtensions.cs
index cf201d169366..4caf63589cbc 100644
--- a/dotnet/src/InternalUtilities/test/AssertExtensions.cs
+++ b/dotnet/src/InternalUtilities/test/AssertExtensions.cs
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
-using Xunit;
+using Assert = Xunit.Assert;
namespace SemanticKernel.UnitTests;