diff --git a/dotnet/Directory.Build.targets b/dotnet/Directory.Build.targets
index 266991482b..f4fc12c847 100644
--- a/dotnet/Directory.Build.targets
+++ b/dotnet/Directory.Build.targets
@@ -3,7 +3,6 @@
-
/// HuggingFace embedding generation service.
///
-public sealed class HuggingFaceTextEmbeddingGeneration : IEmbeddingGeneration, IDisposable
+public sealed class HuggingFaceTextEmbeddingGeneration : ITextEmbeddingGeneration, IDisposable
{
private const string HttpUserAgent = "Microsoft Semantic Kernel";
diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/Connectors.AI.OpenAI.csproj b/dotnet/src/Connectors/Connectors.AI.OpenAI/Connectors.AI.OpenAI.csproj
index 616cda3962..5478eaac19 100644
--- a/dotnet/src/Connectors/Connectors.AI.OpenAI/Connectors.AI.OpenAI.csproj
+++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/Connectors.AI.OpenAI.csproj
@@ -1,4 +1,4 @@
-
+
@@ -20,7 +20,7 @@
-
+
diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/KernelConfigOpenAIExtensions.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/KernelConfigOpenAIExtensions.cs
index 3e8b327bea..9b2facd590 100644
--- a/dotnet/src/Connectors/Connectors.AI.OpenAI/KernelConfigOpenAIExtensions.cs
+++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/KernelConfigOpenAIExtensions.cs
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
+using System;
using System.Net.Http;
using Azure.Core;
using Microsoft.Extensions.Logging;
@@ -34,6 +35,7 @@ public static class KernelConfigOpenAIExtensions
/// Custom for HTTP requests.
/// Application logger
/// Self instance
+ [Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions. Please use the corresponding extension method in the KernelBuilder class instead.")]
public static KernelConfig AddAzureTextCompletionService(this KernelConfig config,
string deploymentName,
string endpoint,
@@ -66,6 +68,7 @@ public static class KernelConfigOpenAIExtensions
/// Custom for HTTP requests.
/// Application logger
/// Self instance
+ [Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions. Please use the corresponding extension method in the KernelBuilder class instead.")]
public static KernelConfig AddAzureTextCompletionService(this KernelConfig config,
string deploymentName,
string endpoint,
@@ -98,6 +101,7 @@ public static class KernelConfigOpenAIExtensions
/// Custom for HTTP requests.
/// Application logger
/// Self instance
+ [Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions. Please use the corresponding extension method in the KernelBuilder class instead.")]
public static KernelConfig AddOpenAITextCompletionService(this KernelConfig config,
string modelId,
string apiKey,
@@ -134,6 +138,7 @@ public static class KernelConfigOpenAIExtensions
/// Custom for HTTP requests.
/// Application logger
/// Self instance
+ [Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions. Please use the corresponding extension method in the KernelBuilder class instead.")]
public static KernelConfig AddAzureTextEmbeddingGenerationService(this KernelConfig config,
string deploymentName,
string endpoint,
@@ -166,6 +171,7 @@ public static class KernelConfigOpenAIExtensions
/// Custom for HTTP requests.
/// Application logger
/// Self instance
+ [Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions. Please use the corresponding extension method in the KernelBuilder class instead.")]
public static KernelConfig AddAzureTextEmbeddingGenerationService(this KernelConfig config,
string deploymentName,
string endpoint,
@@ -198,6 +204,7 @@ public static class KernelConfigOpenAIExtensions
/// Custom for HTTP requests.
/// Application logger
/// Self instance
+ [Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions. Please use the corresponding extension method in the KernelBuilder class instead.")]
public static KernelConfig AddOpenAITextEmbeddingGenerationService(this KernelConfig config,
string modelId,
string apiKey,
@@ -235,6 +242,7 @@ public static class KernelConfigOpenAIExtensions
/// Custom for HTTP requests.
/// Application logger
/// Self instance
+ [Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions. Please use the corresponding extension method in the KernelBuilder class instead.")]
public static KernelConfig AddAzureChatCompletionService(this KernelConfig config,
string deploymentName,
string endpoint,
@@ -278,6 +286,7 @@ public static class KernelConfigOpenAIExtensions
/// Custom for HTTP requests.
/// Application logger
/// Self instance
+ [Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions. Please use the corresponding extension method in the KernelBuilder class instead.")]
public static KernelConfig AddAzureChatCompletionService(this KernelConfig config,
string deploymentName,
string endpoint,
@@ -325,6 +334,7 @@ public static class KernelConfigOpenAIExtensions
/// Custom for HTTP requests.
/// Application logger
/// Self instance
+ [Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions. Please use the corresponding extension method in the KernelBuilder class instead.")]
public static KernelConfig AddOpenAIChatCompletionService(this KernelConfig config,
string modelId,
string apiKey,
@@ -373,6 +383,7 @@ public static class KernelConfigOpenAIExtensions
/// Custom for HTTP requests.
/// Application logger
/// Self instance
+ [Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions. Please use the corresponding extension method in the KernelBuilder class instead.")]
public static KernelConfig AddOpenAIImageGenerationService(this KernelConfig config,
string apiKey,
string? orgId = null,
diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/OpenAIKernelBuilderExtensions.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/OpenAIKernelBuilderExtensions.cs
new file mode 100644
index 0000000000..c3259d7f88
--- /dev/null
+++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/OpenAIKernelBuilderExtensions.cs
@@ -0,0 +1,380 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Net.Http;
+using Azure.Core;
+using Microsoft.Extensions.Logging;
+using Microsoft.SemanticKernel.AI.ChatCompletion;
+using Microsoft.SemanticKernel.AI.Embeddings;
+using Microsoft.SemanticKernel.AI.TextCompletion;
+using Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletion;
+using Microsoft.SemanticKernel.Connectors.AI.OpenAI.ImageGeneration;
+using Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextCompletion;
+using Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextEmbedding;
+using Microsoft.SemanticKernel.Reliability;
+
+#pragma warning disable IDE0130
+// ReSharper disable once CheckNamespace - Using NS of KernelConfig
+namespace Microsoft.SemanticKernel;
+#pragma warning restore IDE0130
+
+public static class OpenAIKernelBuilderExtensions
+{
+ #region Text Completion
+
+ ///
+ /// Adds an Azure OpenAI text completion service to the list.
+ /// See https://learn.microsoft.com/azure/cognitive-services/openai for service details.
+ ///
+ /// The instance
+ /// 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
+ /// Whether the service should be the default for its type.
+ /// Custom for HTTP requests.
+ /// Self instance
+ public static KernelBuilder WithAzureTextCompletionService(this KernelBuilder builder,
+ string deploymentName,
+ string endpoint,
+ string apiKey,
+ string? serviceId = null,
+ bool setAsDefault = false,
+ HttpClient? httpClient = null)
+ {
+ builder.WithAIService(serviceId, (parameters) =>
+ new AzureTextCompletion(
+ deploymentName,
+ endpoint,
+ apiKey,
+ httpClient ?? parameters.Config.HttpHandlerFactory.CreateHttpClient(parameters.Logger),
+ parameters.Logger),
+ setAsDefault);
+
+ return builder;
+ }
+
+ ///
+ /// Adds an Azure OpenAI text completion service to the list.
+ /// See https://learn.microsoft.com/azure/cognitive-services/openai for service details.
+ ///
+ /// The instance
+ /// 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
+ /// Whether the service should be the default for its type.
+ /// Custom for HTTP requests.
+ /// Self instance
+ public static KernelBuilder WithAzureTextCompletionService(this KernelBuilder builder,
+ string deploymentName,
+ string endpoint,
+ TokenCredential credentials,
+ string? serviceId = null,
+ bool setAsDefault = false,
+ HttpClient? httpClient = null)
+ {
+ builder.WithAIService(serviceId, (parameters) =>
+ new AzureTextCompletion(
+ deploymentName,
+ endpoint,
+ credentials,
+ httpClient ?? parameters.Config.HttpHandlerFactory.CreateHttpClient(parameters.Logger),
+ parameters.Logger),
+ setAsDefault);
+
+ return builder;
+ }
+
+ ///
+ /// Adds the OpenAI text completion service to the list.
+ /// See https://platform.openai.com/docs for service details.
+ ///
+ /// The instance
+ /// OpenAI model name, see https://platform.openai.com/docs/models
+ /// OpenAI API key, see https://platform.openai.com/account/api-keys
+ /// OpenAI organization id. This is usually optional unless your account belongs to multiple organizations.
+ /// A local identifier for the given AI service
+ /// Whether the service should be the default for its type.
+ /// Custom for HTTP requests.
+ /// Self instance
+ public static KernelBuilder WithOpenAITextCompletionService(this KernelBuilder builder,
+ string modelId,
+ string apiKey,
+ string? orgId = null,
+ string? serviceId = null,
+ bool setAsDefault = false,
+ HttpClient? httpClient = null)
+ {
+ builder.WithAIService(serviceId, (parameters) =>
+ new OpenAITextCompletion(
+ modelId,
+ apiKey,
+ orgId,
+ httpClient ?? parameters.Config.HttpHandlerFactory.CreateHttpClient(parameters.Logger),
+ parameters.Logger),
+ setAsDefault);
+ return builder;
+ }
+
+ #endregion
+
+ #region Text Embedding
+
+ ///
+ /// Adds an Azure OpenAI text embeddings service to the list.
+ /// See https://learn.microsoft.com/azure/cognitive-services/openai for service details.
+ ///
+ /// The instance
+ /// 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
+ /// Whether the service should be the default for its type.
+ /// Custom for HTTP requests.
+ /// Self instance
+ public static KernelBuilder WithAzureTextEmbeddingGenerationService(this KernelBuilder builder,
+ string deploymentName,
+ string endpoint,
+ string apiKey,
+ string? serviceId = null,
+ bool setAsDefault = false,
+ HttpClient? httpClient = null)
+ {
+ builder.WithAIService(serviceId, (parameters) =>
+ new AzureTextEmbeddingGeneration(
+ deploymentName,
+ endpoint,
+ apiKey,
+ httpClient ?? parameters.Config.HttpHandlerFactory.CreateHttpClient(parameters.Logger),
+ parameters.Logger),
+ setAsDefault);
+ return builder;
+ }
+
+ ///
+ /// Adds an Azure OpenAI text embeddings service to the list.
+ /// See https://learn.microsoft.com/azure/cognitive-services/openai for service details.
+ ///
+ /// The instance
+ /// 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
+ /// Whether the service should be the default for its type.
+ /// Custom for HTTP requests.
+ /// Self instance
+ public static KernelBuilder WithAzureTextEmbeddingGenerationService(this KernelBuilder builder,
+ string deploymentName,
+ string endpoint,
+ TokenCredential credential,
+ string? serviceId = null,
+ bool setAsDefault = false,
+ HttpClient? httpClient = null)
+ {
+ builder.WithAIService(serviceId, (parameters) =>
+ new AzureTextEmbeddingGeneration(
+ deploymentName,
+ endpoint,
+ credential,
+ httpClient ?? parameters.Config.HttpHandlerFactory.CreateHttpClient(parameters.Logger),
+ parameters.Logger),
+ setAsDefault);
+ return builder;
+ }
+
+ ///
+ /// Adds the OpenAI text embeddings service to the list.
+ /// See https://platform.openai.com/docs for service details.
+ ///
+ /// The instance
+ /// OpenAI model name, see https://platform.openai.com/docs/models
+ /// OpenAI API key, see https://platform.openai.com/account/api-keys
+ /// OpenAI organization id. This is usually optional unless your account belongs to multiple organizations.
+ /// A local identifier for the given AI service
+ /// Whether the service should be the default for its type.
+ /// Custom for HTTP requests.
+ /// Self instance
+ public static KernelBuilder WithOpenAITextEmbeddingGenerationService(this KernelBuilder builder,
+ string modelId,
+ string apiKey,
+ string? orgId = null,
+ string? serviceId = null,
+ bool setAsDefault = false,
+ HttpClient? httpClient = null)
+ {
+ builder.WithAIService(serviceId, (parameters) =>
+ new OpenAITextEmbeddingGeneration(
+ modelId,
+ apiKey,
+ orgId,
+ httpClient ?? parameters.Config.HttpHandlerFactory.CreateHttpClient(parameters.Logger),
+ parameters.Logger),
+ setAsDefault);
+ return builder;
+ }
+
+ #endregion
+
+ #region Chat Completion
+
+ ///
+ /// Adds the Azure OpenAI ChatGPT completion service to the list.
+ /// See https://platform.openai.com/docs for service details.
+ ///
+ /// The instance
+ /// 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
+ /// Whether to use the service also for text completion, if supported
+ /// A local identifier for the given AI service
+ /// Whether the service should be the default for its type.
+ /// Custom for HTTP requests.
+ /// Self instance
+ public static KernelBuilder WithAzureChatCompletionService(this KernelBuilder builder,
+ string deploymentName,
+ string endpoint,
+ string apiKey,
+ bool alsoAsTextCompletion = true,
+ string? serviceId = null,
+ bool setAsDefault = false,
+ HttpClient? httpClient = null)
+ {
+ AzureChatCompletion Factory((ILogger Logger, KernelConfig Config) parameters) => new(
+ deploymentName,
+ endpoint,
+ apiKey,
+ httpClient ?? parameters.Config.HttpHandlerFactory.CreateHttpClient(parameters.Logger),
+ parameters.Logger);
+
+ builder.WithAIService(serviceId, Factory, setAsDefault);
+
+ // If the class implements the text completion interface, allow to use it also for semantic functions
+ if (alsoAsTextCompletion && typeof(ITextCompletion).IsAssignableFrom(typeof(AzureChatCompletion)))
+ {
+ builder.WithAIService(serviceId, Factory, setAsDefault);
+ }
+
+ return builder;
+ }
+
+ ///
+ /// Adds the Azure OpenAI ChatGPT completion service to the list.
+ /// See https://platform.openai.com/docs for service details.
+ ///
+ /// The instance
+ /// 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.
+ /// Whether to use the service also for text completion, if supported
+ /// A local identifier for the given AI service
+ /// Whether the service should be the default for its type.
+ /// Custom for HTTP requests.
+ /// Self instance
+ public static KernelBuilder WithAzureChatCompletionService(this KernelBuilder builder,
+ string deploymentName,
+ string endpoint,
+ TokenCredential credentials,
+ bool alsoAsTextCompletion = true,
+ string? serviceId = null,
+ bool setAsDefault = false,
+ HttpClient? httpClient = null)
+ {
+ AzureChatCompletion Factory((ILogger Logger, KernelConfig Config) parameters) => new(
+ deploymentName,
+ endpoint,
+ credentials,
+ httpClient ?? parameters.Config.HttpHandlerFactory.CreateHttpClient(parameters.Logger),
+ parameters.Logger);
+
+ builder.WithAIService(serviceId, Factory, setAsDefault);
+
+ // If the class implements the text completion interface, allow to use it also for semantic functions
+ if (alsoAsTextCompletion && typeof(ITextCompletion).IsAssignableFrom(typeof(AzureChatCompletion)))
+ {
+ builder.WithAIService(serviceId, Factory, setAsDefault);
+ }
+
+ return builder;
+ }
+
+ ///
+ /// Adds the OpenAI ChatGPT completion service to the list.
+ /// See https://platform.openai.com/docs for service details.
+ ///
+ /// The instance
+ /// OpenAI model name, see https://platform.openai.com/docs/models
+ /// OpenAI API key, see https://platform.openai.com/account/api-keys
+ /// OpenAI organization id. This is usually optional unless your account belongs to multiple organizations.
+ /// A local identifier for the given AI service
+ /// Whether to use the service also for text completion, if supported
+ /// Whether the service should be the default for its type.
+ /// Custom for HTTP requests.
+ /// Self instance
+ public static KernelBuilder WithOpenAIChatCompletionService(this KernelBuilder builder,
+ string modelId,
+ string apiKey,
+ string? orgId = null,
+ string? serviceId = null,
+ bool alsoAsTextCompletion = true,
+ bool setAsDefault = false,
+ HttpClient? httpClient = null)
+ {
+ OpenAIChatCompletion Factory((ILogger Logger, KernelConfig Config) parameters) => new(
+ modelId,
+ apiKey,
+ orgId,
+ httpClient ?? parameters.Config.HttpHandlerFactory.CreateHttpClient(parameters.Logger),
+ parameters.Logger);
+
+ builder.WithAIService(serviceId, Factory, setAsDefault);
+
+ // If the class implements the text completion interface, allow to use it also for semantic functions
+ if (alsoAsTextCompletion && typeof(ITextCompletion).IsAssignableFrom(typeof(OpenAIChatCompletion)))
+ {
+ builder.WithAIService(serviceId, Factory, setAsDefault);
+ }
+
+ return builder;
+ }
+
+ private static HttpClient CreateHttpClient(this IDelegatingHandlerFactory handlerFactory, ILogger? logger)
+ {
+ var retryHandler = handlerFactory.Create(logger);
+ retryHandler.InnerHandler = new HttpClientHandler { CheckCertificateRevocationList = true };
+ return new HttpClient(retryHandler);
+ }
+
+ #endregion
+
+ #region Images
+
+ ///
+ /// Add the OpenAI DallE image generation service to the list
+ ///
+ /// The instance
+ /// OpenAI API key, see https://platform.openai.com/account/api-keys
+ /// OpenAI organization id. This is usually optional unless your account belongs to multiple organizations.
+ /// A local identifier for the given AI service
+ /// Whether the service should be the default for its type.
+ /// Custom for HTTP requests.
+ /// Self instance
+ public static KernelBuilder WithOpenAIImageGenerationService(this KernelBuilder builder,
+ string apiKey,
+ string? orgId = null,
+ string? serviceId = null,
+ bool setAsDefault = false,
+ HttpClient? httpClient = null)
+ {
+ builder.WithAIService(serviceId, ((ILogger Logger, KernelConfig Config) parameters) =>
+ new OpenAIImageGeneration(
+ apiKey,
+ orgId,
+ httpClient ?? parameters.Config.HttpHandlerFactory.CreateHttpClient(parameters.Logger),
+ parameters.Logger),
+ setAsDefault);
+
+ return builder;
+ }
+
+ #endregion
+}
diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/TextEmbedding/AzureTextEmbeddingGeneration.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/TextEmbedding/AzureTextEmbeddingGeneration.cs
index 985d278e05..bb1c5bed47 100644
--- a/dotnet/src/Connectors/Connectors.AI.OpenAI/TextEmbedding/AzureTextEmbeddingGeneration.cs
+++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/TextEmbedding/AzureTextEmbeddingGeneration.cs
@@ -14,7 +14,7 @@ namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextEmbedding;
///
/// Azure OpenAI text embedding service.
///
-public sealed class AzureTextEmbeddingGeneration : AzureOpenAIClientBase, IEmbeddingGeneration
+public sealed class AzureTextEmbeddingGeneration : AzureOpenAIClientBase, ITextEmbeddingGeneration
{
///
/// Creates a new AzureTextCompletion client instance using API Key auth
diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/TextEmbedding/OpenAITextEmbeddingGeneration.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/TextEmbedding/OpenAITextEmbeddingGeneration.cs
index 53a36e4e06..b93fdeff67 100644
--- a/dotnet/src/Connectors/Connectors.AI.OpenAI/TextEmbedding/OpenAITextEmbeddingGeneration.cs
+++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/TextEmbedding/OpenAITextEmbeddingGeneration.cs
@@ -13,7 +13,7 @@ namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextEmbedding;
///
/// OpenAI text embedding service.
///
-public sealed class OpenAITextEmbeddingGeneration : OpenAIClientBase, IEmbeddingGeneration
+public sealed class OpenAITextEmbeddingGeneration : OpenAIClientBase, ITextEmbeddingGeneration
{
///
/// Create an instance of the OpenAI text embedding connector
diff --git a/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/AIServicesOpenAIExtensionsTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/AIServicesOpenAIExtensionsTests.cs
new file mode 100644
index 0000000000..36664cef1d
--- /dev/null
+++ b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/AIServicesOpenAIExtensionsTests.cs
@@ -0,0 +1,85 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.SemanticKernel;
+using Microsoft.SemanticKernel.AI.Embeddings;
+using Microsoft.SemanticKernel.AI.TextCompletion;
+using Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextCompletion;
+using Xunit;
+
+namespace SemanticKernel.Connectors.UnitTests.OpenAI;
+
+///
+/// Unit tests of .
+///
+public class AIServicesOpenAIExtensionsTests
+{
+ [Fact]
+ public void ItSucceedsWhenAddingDifferentServiceTypeWithSameId()
+ {
+ KernelBuilder targetBuilder = Kernel.Builder;
+ targetBuilder.WithAzureTextCompletionService("depl", "https://url", "key", "azure");
+ targetBuilder.WithAzureTextEmbeddingGenerationService("depl2", "https://url", "key", "azure");
+
+ IKernel targetKernel = targetBuilder.Build();
+ Assert.NotNull(targetKernel.GetService("azure"));
+ Assert.NotNull(targetKernel.GetService("azure"));
+ }
+
+ [Fact]
+ public void ItTellsIfAServiceIsAvailable()
+ {
+ KernelBuilder targetBuilder = Kernel.Builder;
+ targetBuilder.WithAzureTextCompletionService("depl", "https://url", "key", serviceId: "azure");
+ targetBuilder.WithOpenAITextCompletionService("model", "apikey", serviceId: "oai");
+ targetBuilder.WithAzureTextEmbeddingGenerationService("depl2", "https://url2", "key", serviceId: "azure");
+ targetBuilder.WithOpenAITextEmbeddingGenerationService("model2", "apikey2", serviceId: "oai2");
+
+ // Assert
+ IKernel targetKernel = targetBuilder.Build();
+ Assert.NotNull(targetKernel.GetService("azure"));
+ Assert.NotNull(targetKernel.GetService("oai"));
+ Assert.NotNull(targetKernel.GetService("azure"));
+ Assert.NotNull(targetKernel.GetService("oai"));
+ }
+
+ [Fact]
+ public void ItCanOverwriteServices()
+ {
+ // Arrange
+ KernelBuilder targetBuilder = Kernel.Builder;
+
+ // Act - Assert no exception occurs
+ targetBuilder.WithAzureTextCompletionService("dep", "https://localhost", "key", serviceId: "one");
+ targetBuilder.WithAzureTextCompletionService("dep", "https://localhost", "key", serviceId: "one");
+
+ targetBuilder.WithOpenAITextCompletionService("model", "key", serviceId: "one");
+ targetBuilder.WithOpenAITextCompletionService("model", "key", serviceId: "one");
+
+ targetBuilder.WithAzureTextEmbeddingGenerationService("dep", "https://localhost", "key", serviceId: "one");
+ targetBuilder.WithAzureTextEmbeddingGenerationService("dep", "https://localhost", "key", serviceId: "one");
+
+ targetBuilder.WithOpenAITextEmbeddingGenerationService("model", "key", serviceId: "one");
+ targetBuilder.WithOpenAITextEmbeddingGenerationService("model", "key", serviceId: "one");
+
+ targetBuilder.WithAzureChatCompletionService("dep", "https://localhost", "key", serviceId: "one");
+ targetBuilder.WithAzureChatCompletionService("dep", "https://localhost", "key", serviceId: "one");
+
+ targetBuilder.WithOpenAIChatCompletionService("model", "key", serviceId: "one");
+ targetBuilder.WithOpenAIChatCompletionService("model", "key", serviceId: "one");
+
+ targetBuilder.WithOpenAIImageGenerationService("model", "key", serviceId: "one");
+ targetBuilder.WithOpenAIImageGenerationService("model", "key", serviceId: "one");
+
+ targetBuilder.WithDefaultAIService(new OpenAITextCompletion("model", "key"));
+ targetBuilder.WithDefaultAIService(new OpenAITextCompletion("model", "key"));
+
+ targetBuilder.WithDefaultAIService((_) => new OpenAITextCompletion("model", "key"));
+ targetBuilder.WithDefaultAIService((_) => new OpenAITextCompletion("model", "key"));
+
+ targetBuilder.WithAIService("one", new OpenAITextCompletion("model", "key"));
+ targetBuilder.WithAIService("one", new OpenAITextCompletion("model", "key"));
+
+ targetBuilder.WithAIService("one", (_) => new OpenAITextCompletion("model", "key"));
+ targetBuilder.WithAIService("one", (_) => new OpenAITextCompletion("model", "key"));
+ }
+}
diff --git a/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/KernelConfigOpenAIExtensionsTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/KernelConfigOpenAIExtensionsTests.cs
index 1d0c27a64a..982c8fe0a3 100644
--- a/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/KernelConfigOpenAIExtensionsTests.cs
+++ b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/KernelConfigOpenAIExtensionsTests.cs
@@ -8,6 +8,7 @@ namespace SemanticKernel.Connectors.UnitTests.OpenAI;
///
/// Unit tests of .
///
+[System.Obsolete("All the methods of this class are deprecated and it will be removed in one of the next SK SDK versions.")]
public class KernelConfigOpenAIExtensionsTests
{
[Fact]
diff --git a/dotnet/src/IntegrationTests/Connectors/OpenAI/OpenAICompletionTests.cs b/dotnet/src/IntegrationTests/Connectors/OpenAI/OpenAICompletionTests.cs
index c9e0475c7a..1e2cd79e30 100644
--- a/dotnet/src/IntegrationTests/Connectors/OpenAI/OpenAICompletionTests.cs
+++ b/dotnet/src/IntegrationTests/Connectors/OpenAI/OpenAICompletionTests.cs
@@ -36,9 +36,6 @@ public sealed class OpenAICompletionTests : IDisposable
.AddEnvironmentVariables()
.AddUserSecrets()
.Build();
-
- this._serviceConfiguration.Add(AIServiceType.OpenAI, this.ConfigureOpenAI);
- this._serviceConfiguration.Add(AIServiceType.AzureOpenAI, this.ConfigureAzureOpenAI);
}
[Theory(Skip = "OpenAI will often throttle requests. This test is for manual verification.")]
@@ -46,9 +43,17 @@ public sealed class OpenAICompletionTests : IDisposable
public async Task OpenAITestAsync(string prompt, string expectedAnswerContains)
{
// Arrange
- IKernel target = Kernel.Builder.WithLogger(this._logger).Build();
+ var openAIConfiguration = this._configuration.GetSection("OpenAI").Get();
+ Assert.NotNull(openAIConfiguration);
- this.ConfigureOpenAI(target);
+ IKernel target = Kernel.Builder
+ .WithLogger(this._logger)
+ .WithOpenAITextCompletionService(
+ serviceId: openAIConfiguration.ServiceId,
+ modelId: openAIConfiguration.ModelId,
+ apiKey: openAIConfiguration.ApiKey,
+ setAsDefault: true)
+ .Build();
IDictionary skill = TestHelpers.GetSkills(target, "ChatSkill");
@@ -64,9 +69,11 @@ public sealed class OpenAICompletionTests : IDisposable
public async Task OpenAIChatAsTextTestAsync(string prompt, string expectedAnswerContains)
{
// Arrange
- IKernel target = Kernel.Builder.WithLogger(this._logger).Build();
+ KernelBuilder builder = Kernel.Builder.WithLogger(this._logger);
- this.ConfigureChatOpenAI(target);
+ this.ConfigureChatOpenAI(builder);
+
+ IKernel target = builder.Build();
IDictionary skill = TestHelpers.GetSkills(target, "ChatSkill");
@@ -81,8 +88,10 @@ public sealed class OpenAICompletionTests : IDisposable
public async Task CanUseOpenAiChatForTextCompletionAsync()
{
// Note: we use OpenAi Chat Completion and GPT 3.5 Turbo
- IKernel target = Kernel.Builder.WithLogger(this._logger).Build();
- this.ConfigureChatOpenAI(target);
+ KernelBuilder builder = Kernel.Builder.WithLogger(this._logger);
+ this.ConfigureChatOpenAI(builder);
+
+ IKernel target = builder.Build();
var func = target.CreateSemanticFunction(
"List the two planets after '{{$input}}', excluding moons, using bullet points.");
@@ -101,17 +110,23 @@ public sealed class OpenAICompletionTests : IDisposable
public async Task AzureOpenAITestAsync(bool useChatModel, string prompt, string expectedAnswerContains)
{
// Arrange
- IKernel target = Kernel.Builder.WithLogger(this._logger).Build();
+
+ var azureOpenAIConfiguration = this._configuration.GetSection("AzureOpenAI").Get();
+ Assert.NotNull(azureOpenAIConfiguration);
+
+ var builder = Kernel.Builder.WithLogger(this._logger);
if (useChatModel)
{
- this.ConfigureAzureOpenAIChatAsText(target);
+ this.ConfigureAzureOpenAIChatAsText(builder);
}
else
{
- this.ConfigureAzureOpenAI(target);
+ this.ConfigureAzureOpenAI(builder);
}
+ IKernel target = builder.Build();
+
IDictionary skill = TestHelpers.GetSkills(target, "ChatSkill");
// Act
@@ -132,15 +147,18 @@ public sealed class OpenAICompletionTests : IDisposable
// Arrange
var retryConfig = new HttpRetryConfig();
retryConfig.RetryableStatusCodes.Add(HttpStatusCode.Unauthorized);
- IKernel target = Kernel.Builder.WithLogger(this._testOutputHelper).Configure(c => c.SetDefaultHttpRetryConfig(retryConfig)).Build();
OpenAIConfiguration? openAIConfiguration = this._configuration.GetSection("OpenAI").Get();
Assert.NotNull(openAIConfiguration);
- // Use an invalid API key to force a 401 Unauthorized response
- target.Config.AddOpenAITextCompletionService(
- modelId: openAIConfiguration.ModelId,
- apiKey: "INVALID_KEY");
+ IKernel target = Kernel.Builder
+ .WithLogger(this._testOutputHelper)
+ .Configure(c => c.SetDefaultHttpRetryConfig(retryConfig))
+ .WithOpenAITextCompletionService(
+ serviceId: openAIConfiguration.ServiceId,
+ modelId: openAIConfiguration.ModelId,
+ apiKey: "INVALID_KEY") // Use an invalid API key to force a 401 Unauthorized response
+ .Build();
IDictionary skill = TestHelpers.GetSkills(target, "SummarizeSkill");
@@ -160,17 +178,21 @@ public sealed class OpenAICompletionTests : IDisposable
// Arrange
var retryConfig = new HttpRetryConfig();
retryConfig.RetryableStatusCodes.Add(HttpStatusCode.Unauthorized);
- IKernel target = Kernel.Builder.WithLogger(this._testOutputHelper).Configure(c => c.SetDefaultHttpRetryConfig(retryConfig)).Build();
+ KernelBuilder builder = Kernel.Builder
+ .WithLogger(this._testOutputHelper)
+ .Configure(c => c.SetDefaultHttpRetryConfig(retryConfig));
var azureOpenAIConfiguration = this._configuration.GetSection("AzureOpenAI").Get();
Assert.NotNull(azureOpenAIConfiguration);
// Use an invalid API key to force a 401 Unauthorized response
- target.Config.AddAzureTextCompletionService(
+ builder.WithAzureTextCompletionService(
deploymentName: azureOpenAIConfiguration.DeploymentName,
endpoint: azureOpenAIConfiguration.Endpoint,
apiKey: "INVALID_KEY");
+ IKernel target = builder.Build();
+
IDictionary skill = TestHelpers.GetSkills(target, "SummarizeSkill");
// Act
@@ -184,15 +206,16 @@ public sealed class OpenAICompletionTests : IDisposable
public async Task OpenAIHttpInvalidKeyShouldReturnErrorDetailAsync()
{
// Arrange
- IKernel target = Kernel.Builder.WithLogger(this._testOutputHelper).Build();
-
OpenAIConfiguration? openAIConfiguration = this._configuration.GetSection("OpenAI").Get();
Assert.NotNull(openAIConfiguration);
// Use an invalid API key to force a 401 Unauthorized response
- target.Config.AddOpenAITextCompletionService(
- modelId: openAIConfiguration.ModelId,
- apiKey: "INVALID_KEY");
+ IKernel target = Kernel.Builder
+ .WithOpenAITextCompletionService(
+ modelId: openAIConfiguration.ModelId,
+ apiKey: "INVALID_KEY",
+ serviceId: openAIConfiguration.ServiceId)
+ .Build();
IDictionary skill = TestHelpers.GetSkills(target, "SummarizeSkill");
@@ -210,16 +233,17 @@ public sealed class OpenAICompletionTests : IDisposable
public async Task AzureOpenAIHttpInvalidKeyShouldReturnErrorDetailAsync()
{
// Arrange
- IKernel target = Kernel.Builder.WithLogger(this._testOutputHelper).Build();
-
var azureOpenAIConfiguration = this._configuration.GetSection("AzureOpenAI").Get();
-
Assert.NotNull(azureOpenAIConfiguration);
- target.Config.AddAzureTextCompletionService(
- deploymentName: azureOpenAIConfiguration.DeploymentName,
- endpoint: azureOpenAIConfiguration.Endpoint,
- apiKey: "INVALID_KEY");
+ IKernel target = Kernel.Builder
+ .WithLogger(this._testOutputHelper)
+ .WithAzureTextCompletionService(
+ deploymentName: azureOpenAIConfiguration.DeploymentName,
+ endpoint: azureOpenAIConfiguration.Endpoint,
+ apiKey: "INVALID_KEY",
+ serviceId: azureOpenAIConfiguration.ServiceId)
+ .Build();
IDictionary skill = TestHelpers.GetSkills(target, "SummarizeSkill");
@@ -236,17 +260,18 @@ public sealed class OpenAICompletionTests : IDisposable
[Fact]
public async Task AzureOpenAIHttpExceededMaxTokensShouldReturnErrorDetailAsync()
{
- // Arrange
- IKernel target = Kernel.Builder.WithLogger(this._testOutputHelper).Build();
-
var azureOpenAIConfiguration = this._configuration.GetSection("AzureOpenAI").Get();
-
Assert.NotNull(azureOpenAIConfiguration);
- target.Config.AddAzureTextCompletionService(
- deploymentName: azureOpenAIConfiguration.DeploymentName,
- endpoint: azureOpenAIConfiguration.Endpoint,
- apiKey: azureOpenAIConfiguration.ApiKey);
+ // Arrange
+ IKernel target = Kernel.Builder
+ .WithLogger(this._testOutputHelper)
+ .WithAzureTextCompletionService(
+ deploymentName: azureOpenAIConfiguration.DeploymentName,
+ endpoint: azureOpenAIConfiguration.Endpoint,
+ apiKey: azureOpenAIConfiguration.ApiKey,
+ serviceId: azureOpenAIConfiguration.ServiceId)
+ .Build();
IDictionary skill = TestHelpers.GetSkills(target, "SummarizeSkill");
@@ -316,7 +341,7 @@ public sealed class OpenAICompletionTests : IDisposable
}
}
- private void ConfigureOpenAI(IKernel kernel)
+ private void ConfigureOpenAI(KernelBuilder kernelBuilder)
{
var openAIConfiguration = this._configuration.GetSection("OpenAI").Get();
@@ -325,15 +350,14 @@ public sealed class OpenAICompletionTests : IDisposable
Assert.NotNull(openAIConfiguration.ApiKey);
Assert.NotNull(openAIConfiguration.ServiceId);
- kernel.Config.AddOpenAITextCompletionService(
+ kernelBuilder.WithOpenAITextCompletionService(
modelId: openAIConfiguration.ModelId,
apiKey: openAIConfiguration.ApiKey,
- serviceId: openAIConfiguration.ServiceId);
-
- kernel.Config.SetDefaultTextCompletionService(openAIConfiguration.ServiceId);
+ serviceId: openAIConfiguration.ServiceId,
+ setAsDefault: true);
}
- private void ConfigureChatOpenAI(IKernel kernel)
+ private void ConfigureChatOpenAI(KernelBuilder kernelBuilder)
{
var openAIConfiguration = this._configuration.GetSection("OpenAI").Get();
@@ -342,15 +366,14 @@ public sealed class OpenAICompletionTests : IDisposable
Assert.NotNull(openAIConfiguration.ApiKey);
Assert.NotNull(openAIConfiguration.ServiceId);
- kernel.Config.AddOpenAIChatCompletionService(
+ kernelBuilder.WithOpenAIChatCompletionService(
modelId: openAIConfiguration.ChatModelId,
apiKey: openAIConfiguration.ApiKey,
- serviceId: openAIConfiguration.ServiceId);
-
- kernel.Config.SetDefaultTextCompletionService(openAIConfiguration.ServiceId);
+ serviceId: openAIConfiguration.ServiceId,
+ setAsDefault: true);
}
- private void ConfigureAzureOpenAI(IKernel kernel)
+ private void ConfigureAzureOpenAI(KernelBuilder kernelBuilder)
{
var azureOpenAIConfiguration = this._configuration.GetSection("AzureOpenAI").Get();
@@ -360,16 +383,15 @@ public sealed class OpenAICompletionTests : IDisposable
Assert.NotNull(azureOpenAIConfiguration.ApiKey);
Assert.NotNull(azureOpenAIConfiguration.ServiceId);
- kernel.Config.AddAzureTextCompletionService(
+ kernelBuilder.WithAzureTextCompletionService(
deploymentName: azureOpenAIConfiguration.DeploymentName,
endpoint: azureOpenAIConfiguration.Endpoint,
apiKey: azureOpenAIConfiguration.ApiKey,
- serviceId: azureOpenAIConfiguration.ServiceId);
-
- kernel.Config.SetDefaultTextCompletionService(azureOpenAIConfiguration.ServiceId);
+ serviceId: azureOpenAIConfiguration.ServiceId,
+ setAsDefault: true);
}
- private void ConfigureAzureOpenAIChatAsText(IKernel kernel)
+ private void ConfigureAzureOpenAIChatAsText(KernelBuilder kernelBuilder)
{
var azureOpenAIConfiguration = this._configuration.GetSection("AzureOpenAI").Get();
@@ -379,13 +401,11 @@ public sealed class OpenAICompletionTests : IDisposable
Assert.NotNull(azureOpenAIConfiguration.Endpoint);
Assert.NotNull(azureOpenAIConfiguration.ServiceId);
- kernel.Config.AddAzureChatCompletionService(
+ kernelBuilder.WithAzureChatCompletionService(
deploymentName: azureOpenAIConfiguration.ChatDeploymentName,
endpoint: azureOpenAIConfiguration.Endpoint,
apiKey: azureOpenAIConfiguration.ApiKey,
serviceId: azureOpenAIConfiguration.ServiceId);
-
- kernel.Config.SetDefaultTextCompletionService(azureOpenAIConfiguration.ServiceId);
}
#endregion
diff --git a/dotnet/src/IntegrationTests/Planning/PlanTests.cs b/dotnet/src/IntegrationTests/Planning/PlanTests.cs
index 63f71a72af..dc326b7b06 100644
--- a/dotnet/src/IntegrationTests/Planning/PlanTests.cs
+++ b/dotnet/src/IntegrationTests/Planning/PlanTests.cs
@@ -478,37 +478,31 @@ public sealed class PlanTests : IDisposable
AzureOpenAIConfiguration? azureOpenAIEmbeddingsConfiguration = this._configuration.GetSection("AzureOpenAIEmbeddings").Get();
Assert.NotNull(azureOpenAIEmbeddingsConfiguration);
- var builder = Kernel.Builder
- .WithLogger(this._logger)
- .Configure(config =>
- {
- if (useChatModel)
- {
- config.AddAzureChatCompletionService(
+ var builder = Kernel.Builder.WithLogger(this._logger);
+
+ if (useChatModel)
+ {
+ builder.WithAzureChatCompletionService(
deploymentName: azureOpenAIConfiguration.ChatDeploymentName!,
endpoint: azureOpenAIConfiguration.Endpoint,
apiKey: azureOpenAIConfiguration.ApiKey);
- }
- else
- {
- config.AddAzureTextCompletionService(
+ }
+ else
+ {
+ builder.WithAzureTextCompletionService(
deploymentName: azureOpenAIConfiguration.DeploymentName,
endpoint: azureOpenAIConfiguration.Endpoint,
apiKey: azureOpenAIConfiguration.ApiKey);
- }
-
- if (useEmbeddings)
- {
- config.AddAzureTextEmbeddingGenerationService(
- deploymentName: azureOpenAIEmbeddingsConfiguration.DeploymentName,
- endpoint: azureOpenAIEmbeddingsConfiguration.Endpoint,
- apiKey: azureOpenAIEmbeddingsConfiguration.ApiKey);
- }
- });
+ }
if (useEmbeddings)
{
- builder = builder.WithMemoryStorage(new VolatileMemoryStore());
+ builder
+ .WithAzureTextEmbeddingGenerationService(
+ deploymentName: azureOpenAIEmbeddingsConfiguration.DeploymentName,
+ endpoint: azureOpenAIEmbeddingsConfiguration.Endpoint,
+ apiKey: azureOpenAIEmbeddingsConfiguration.ApiKey)
+ .WithMemoryStorage(new VolatileMemoryStore());
}
var kernel = builder.Build();
diff --git a/dotnet/src/IntegrationTests/Planning/SequentialPlanner/SequentialPlanParserTests.cs b/dotnet/src/IntegrationTests/Planning/SequentialPlanner/SequentialPlanParserTests.cs
index 5546a3007b..fa78fd901f 100644
--- a/dotnet/src/IntegrationTests/Planning/SequentialPlanner/SequentialPlanParserTests.cs
+++ b/dotnet/src/IntegrationTests/Planning/SequentialPlanner/SequentialPlanParserTests.cs
@@ -32,13 +32,12 @@ public class SequentialPlanParserTests
Assert.NotNull(azureOpenAIConfiguration);
IKernel kernel = Kernel.Builder
- .Configure(config =>
- {
- config.AddAzureTextCompletionService(
- deploymentName: azureOpenAIConfiguration.DeploymentName,
- endpoint: azureOpenAIConfiguration.Endpoint,
- apiKey: azureOpenAIConfiguration.ApiKey);
- })
+ .WithAzureTextCompletionService(
+ deploymentName: azureOpenAIConfiguration.DeploymentName,
+ endpoint: azureOpenAIConfiguration.Endpoint,
+ apiKey: azureOpenAIConfiguration.ApiKey,
+ serviceId: azureOpenAIConfiguration.ServiceId,
+ setAsDefault: true)
.Build();
kernel.ImportSkill(new EmailSkillFake(), "email");
TestHelpers.GetSkills(kernel, "SummarizeSkill", "WriterSkill");
diff --git a/dotnet/src/IntegrationTests/Planning/SequentialPlanner/SequentialPlannerTests.cs b/dotnet/src/IntegrationTests/Planning/SequentialPlanner/SequentialPlannerTests.cs
index 8c4a077812..afd4db0f7e 100644
--- a/dotnet/src/IntegrationTests/Planning/SequentialPlanner/SequentialPlannerTests.cs
+++ b/dotnet/src/IntegrationTests/Planning/SequentialPlanner/SequentialPlannerTests.cs
@@ -109,37 +109,30 @@ public sealed class SequentialPlannerTests : IDisposable
AzureOpenAIConfiguration? azureOpenAIEmbeddingsConfiguration = this._configuration.GetSection("AzureOpenAIEmbeddings").Get();
Assert.NotNull(azureOpenAIEmbeddingsConfiguration);
- var builder = Kernel.Builder
- .WithLogger(this._logger)
- .Configure(config =>
- {
- if (useChatModel)
- {
- config.AddAzureChatCompletionService(
- deploymentName: azureOpenAIConfiguration.ChatDeploymentName!,
- endpoint: azureOpenAIConfiguration.Endpoint,
- apiKey: azureOpenAIConfiguration.ApiKey);
- }
- else
- {
- config.AddAzureTextCompletionService(
- deploymentName: azureOpenAIConfiguration.DeploymentName,
- endpoint: azureOpenAIConfiguration.Endpoint,
- apiKey: azureOpenAIConfiguration.ApiKey);
- }
+ var builder = Kernel.Builder.WithLogger(this._logger);
- if (useEmbeddings)
- {
- config.AddAzureTextEmbeddingGenerationService(
- deploymentName: azureOpenAIEmbeddingsConfiguration.DeploymentName,
- endpoint: azureOpenAIEmbeddingsConfiguration.Endpoint,
- apiKey: azureOpenAIEmbeddingsConfiguration.ApiKey);
- }
- });
+ if (useChatModel)
+ {
+ builder.WithAzureChatCompletionService(
+ deploymentName: azureOpenAIConfiguration.ChatDeploymentName!,
+ endpoint: azureOpenAIConfiguration.Endpoint,
+ apiKey: azureOpenAIConfiguration.ApiKey);
+ }
+ else
+ {
+ builder.WithAzureTextCompletionService(
+ deploymentName: azureOpenAIConfiguration.DeploymentName,
+ endpoint: azureOpenAIConfiguration.Endpoint,
+ apiKey: azureOpenAIConfiguration.ApiKey);
+ }
if (useEmbeddings)
{
- builder = builder.WithMemoryStorage(new VolatileMemoryStore());
+ builder.WithAzureTextEmbeddingGenerationService(
+ deploymentName: azureOpenAIEmbeddingsConfiguration.DeploymentName,
+ endpoint: azureOpenAIEmbeddingsConfiguration.Endpoint,
+ apiKey: azureOpenAIEmbeddingsConfiguration.ApiKey)
+ .WithMemoryStorage(new VolatileMemoryStore());
}
var kernel = builder.Build();
diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/ChatCompletionServiceExtensions.cs b/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/ChatCompletionServiceExtensions.cs
new file mode 100644
index 0000000000..f6edaf5a2e
--- /dev/null
+++ b/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/ChatCompletionServiceExtensions.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.SemanticKernel.AI.ChatCompletion;
+using Microsoft.SemanticKernel.Services;
+
+// Use base namespace for better discoverability and to avoid conflicts with other extensions.
+#pragma warning disable IDE0130 // Namespace does not match folder structure
+namespace Microsoft.SemanticKernel;
+#pragma warning restore IDE0130 // Namespace does not match folder structure
+
+public static class ChatCompletionServiceExtensions
+{
+ ///
+ /// Get the matching the given , or
+ /// the default if is not provided or not found.
+ ///
+ /// The service provider.
+ /// Optional identifier of the desired service.
+ /// The completion service id matching the given id or the default.
+ /// Thrown when no suitable service is found.
+ public static IChatCompletion GetChatCompletionService(
+ this IAIServiceProvider services,
+ string? serviceId = null) => services.GetService(serviceId)
+ ?? throw new KernelException(KernelException.ErrorCodes.ServiceNotFound, "Chat completion service not found");
+
+ ///
+ /// Returns true if a exist with the specified ID.
+ ///
+ /// The service provider.
+ /// The service ID to search for. If null, it will look for a default service.
+ /// True if the service ID is registered, false otherwise.
+ public static bool HasChatCompletionService(
+ this IAIServiceProvider services,
+ string? serviceId = null)
+ => services.TryGetService(serviceId, out _);
+}
diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/IChatCompletion.cs b/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/IChatCompletion.cs
index 608dd85c32..9138488f3d 100644
--- a/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/IChatCompletion.cs
+++ b/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/IChatCompletion.cs
@@ -3,10 +3,11 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.SemanticKernel.Services;
namespace Microsoft.SemanticKernel.AI.ChatCompletion;
-public interface IChatCompletion
+public interface IChatCompletion : IAIService
{
///
/// Create a new empty chat instance
diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/EmbeddingGenerationServiceExtensions.cs b/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/EmbeddingGenerationServiceExtensions.cs
new file mode 100644
index 0000000000..a15e767890
--- /dev/null
+++ b/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/EmbeddingGenerationServiceExtensions.cs
@@ -0,0 +1,31 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.SemanticKernel.Diagnostics;
+
+namespace Microsoft.SemanticKernel.AI.Embeddings;
+
+///
+/// Provides a collection of static methods for operating on objects.
+///
+public static class EmbeddingGenerationExtensions
+{
+ ///
+ /// Generates an embedding from the given .
+ ///
+ /// The type from which embeddings will be generated.
+ /// The numeric type of the embedding data.
+ /// The embedding generator.
+ /// A value from which an will be generated.
+ /// Cancellation token
+ /// A list of structs representing the input .
+ public static async Task> GenerateEmbeddingAsync
+ (this IEmbeddingGeneration generator, TValue value, CancellationToken cancellationToken = default)
+ where TEmbedding : unmanaged
+ {
+ Verify.NotNull(generator);
+ return (await generator.GenerateEmbeddingsAsync(new[] { value }, cancellationToken).ConfigureAwait(false)).FirstOrDefault();
+ }
+}
diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/IEmbeddingGeneration.cs b/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/IEmbeddingGeneration.cs
index c3128b921a..33d3e1ec7f 100644
--- a/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/IEmbeddingGeneration.cs
+++ b/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/IEmbeddingGeneration.cs
@@ -1,10 +1,8 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
-using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using Microsoft.SemanticKernel.Diagnostics;
namespace Microsoft.SemanticKernel.AI.Embeddings;
@@ -24,26 +22,3 @@ public interface IEmbeddingGeneration
/// List of embeddings
Task>> GenerateEmbeddingsAsync(IList data, CancellationToken cancellationToken = default);
}
-
-///
-/// Provides a collection of static methods for operating on objects.
-///
-public static class EmbeddingGenerationExtensions
-{
- ///
- /// Generates an embedding from the given .
- ///
- /// The type from which embeddings will be generated.
- /// The numeric type of the embedding data.
- /// The embedding generator.
- /// A value from which an will be generated.
- /// The to monitor for cancellation requests. The default is .
- /// A list of structs representing the input .
- public static async Task> GenerateEmbeddingAsync
- (this IEmbeddingGeneration generator, TValue value, CancellationToken cancellationToken = default)
- where TEmbedding : unmanaged
- {
- Verify.NotNull(generator);
- return (await generator.GenerateEmbeddingsAsync(new[] { value }, cancellationToken).ConfigureAwait(false)).FirstOrDefault();
- }
-}
diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/ITextEmbeddingGeneration.cs b/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/ITextEmbeddingGeneration.cs
new file mode 100644
index 0000000000..372a2a2b51
--- /dev/null
+++ b/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/ITextEmbeddingGeneration.cs
@@ -0,0 +1,12 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.SemanticKernel.Services;
+
+namespace Microsoft.SemanticKernel.AI.Embeddings;
+
+///
+/// Represents a generator of text embeddings of type float.
+///
+public interface ITextEmbeddingGeneration : IEmbeddingGeneration, IAIService
+{
+}
diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/TextEmbeddingServiceExtensions.cs b/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/TextEmbeddingServiceExtensions.cs
new file mode 100644
index 0000000000..613161f680
--- /dev/null
+++ b/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/TextEmbeddingServiceExtensions.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.SemanticKernel.AI.Embeddings;
+using Microsoft.SemanticKernel.Services;
+
+// Use base namespace for better discoverability and to avoid conflicts with other extensions.
+#pragma warning disable IDE0130 // Namespace does not match folder structure
+namespace Microsoft.SemanticKernel;
+#pragma warning restore IDE0130 // Namespace does not match folder structure
+
+public static class TextEmbeddingServiceExtensions
+{
+ ///
+ /// Get the matching the given , or the default
+ /// if the is not provided or not found.
+ ///
+ /// The service provider.
+ /// Optional identifier of the desired service.
+ /// The embedding service matching the given id or the default service.
+ /// Thrown when no suitable service is found.
+ public static ITextEmbeddingGeneration GetTextEmbeddingService(
+ this IAIServiceProvider services,
+ string? serviceId = null)
+ => services.GetService(serviceId)
+ ?? throw new KernelException(KernelException.ErrorCodes.ServiceNotFound, "Text embedding service not available");
+
+ ///
+ /// Returns true if a exist with the specified ID.
+ ///
+ /// The service provider.
+ /// The service ID to search for. If null, it will look for a default service.
+ /// True if the service ID is registered, false otherwise.
+ public static bool HasTextEmbeddingService(
+ this IAIServiceProvider services,
+ string? serviceId = null)
+ => services.TryGetService(serviceId, out _);
+}
diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/ImageGeneration/IImageGeneration.cs b/dotnet/src/SemanticKernel.Abstractions/AI/ImageGeneration/IImageGeneration.cs
index 4336506c43..ded72f7855 100644
--- a/dotnet/src/SemanticKernel.Abstractions/AI/ImageGeneration/IImageGeneration.cs
+++ b/dotnet/src/SemanticKernel.Abstractions/AI/ImageGeneration/IImageGeneration.cs
@@ -2,10 +2,11 @@
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.SemanticKernel.Services;
namespace Microsoft.SemanticKernel.AI.ImageGeneration;
-public interface IImageGeneration
+public interface IImageGeneration : IAIService
{
///
/// Generate an image matching the given description
diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/ImageGeneration/ImageGenerationServiceExtensions.cs b/dotnet/src/SemanticKernel.Abstractions/AI/ImageGeneration/ImageGenerationServiceExtensions.cs
new file mode 100644
index 0000000000..17541b3e3a
--- /dev/null
+++ b/dotnet/src/SemanticKernel.Abstractions/AI/ImageGeneration/ImageGenerationServiceExtensions.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.SemanticKernel.AI.ImageGeneration;
+using Microsoft.SemanticKernel.Services;
+
+// Use base namespace for better discoverability and to avoid conflicts with other extensions.
+#pragma warning disable IDE0130 // Namespace does not match folder structure
+namespace Microsoft.SemanticKernel;
+#pragma warning restore IDE0130 // Namespace does not match folder structure
+
+public static class ImageGenerationServiceExtensions
+{
+ ///
+ /// Get the matching the given , or the default
+ /// if the is not provided or not found.
+ ///
+ /// The service provider.
+ /// Optional identifier of the desired service.
+ /// The id matching the given id or the default.
+ /// Thrown when no suitable service is found.
+ public static IImageGeneration GetImageGenerationService(
+ this IAIServiceProvider services,
+ string? serviceId = null) => services.GetService(serviceId)
+ ?? throw new KernelException(KernelException.ErrorCodes.ServiceNotFound, "Image generation service not found");
+
+ ///
+ /// Returns true if a exist with the specified ID.
+ ///
+ /// The service provider.
+ /// The service ID to search for. If null, it will look for a default service.
+ /// True if the service ID is registered, false otherwise.
+ public static bool HasImageGenerationService(
+ this IAIServiceProvider services,
+ string? serviceId = null)
+ => services.TryGetService(serviceId, out _);
+}
diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/TextCompletion/ITextCompletion.cs b/dotnet/src/SemanticKernel.Abstractions/AI/TextCompletion/ITextCompletion.cs
index eb584a6b76..a84df6e838 100644
--- a/dotnet/src/SemanticKernel.Abstractions/AI/TextCompletion/ITextCompletion.cs
+++ b/dotnet/src/SemanticKernel.Abstractions/AI/TextCompletion/ITextCompletion.cs
@@ -3,13 +3,14 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.SemanticKernel.Services;
namespace Microsoft.SemanticKernel.AI.TextCompletion;
///
/// Interface for text completion services
///
-public interface ITextCompletion
+public interface ITextCompletion : IAIService
{
///
/// Get completion results for the prompt and settings.
diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/TextCompletion/TextCompletionServiceExtensions.cs b/dotnet/src/SemanticKernel.Abstractions/AI/TextCompletion/TextCompletionServiceExtensions.cs
new file mode 100644
index 0000000000..2d592c5f6c
--- /dev/null
+++ b/dotnet/src/SemanticKernel.Abstractions/AI/TextCompletion/TextCompletionServiceExtensions.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.SemanticKernel.AI.TextCompletion;
+using Microsoft.SemanticKernel.Services;
+
+// Use base namespace for better discoverability and to avoid conflicts with other extensions.
+#pragma warning disable IDE0130 // Namespace does not match folder structure
+namespace Microsoft.SemanticKernel;
+#pragma warning restore IDE0130 // Namespace does not match folder structure
+
+public static class TextCompletionServiceExtensions
+{
+ ///
+ /// Get the matching the given , or the default
+ /// if the is not provided or not found.
+ ///
+ /// The service provider.
+ /// Optional identifier of the desired service.
+ /// The text completion service id matching the given ID or the default.
+ /// Thrown when no suitable service is found.
+ public static ITextCompletion GetTextCompletionServiceOrDefault(
+ this IAIServiceProvider services,
+ string? serviceId = null) => services.GetService()
+ ?? throw new KernelException(KernelException.ErrorCodes.ServiceNotFound, "Text completion service not found");
+
+ ///
+ /// Returns true if a exist with the specified ID.
+ ///
+ /// The service provider.
+ /// The service ID to search for. If null, it will look for a default service.
+ /// True if the service ID is registered, false otherwise.
+ public static bool HasTextCompletionService(
+ this IAIServiceProvider services,
+ string? serviceId = null)
+ => services.TryGetService(serviceId, out _);
+}
diff --git a/dotnet/src/SemanticKernel.Abstractions/IKernel.cs b/dotnet/src/SemanticKernel.Abstractions/IKernel.cs
index 1391dd5c13..7ed41af984 100644
--- a/dotnet/src/SemanticKernel.Abstractions/IKernel.cs
+++ b/dotnet/src/SemanticKernel.Abstractions/IKernel.cs
@@ -7,6 +7,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel.Memory;
using Microsoft.SemanticKernel.Orchestration;
using Microsoft.SemanticKernel.SemanticFunctions;
+using Microsoft.SemanticKernel.Services;
using Microsoft.SemanticKernel.SkillDefinition;
using Microsoft.SemanticKernel.TemplateEngine;
@@ -171,5 +172,5 @@ public interface IKernel
/// Optional name. If the name is not provided, returns the default T available
/// Service type
/// Instance of T
- T GetService(string? name = null);
+ T GetService(string? name = null) where T : IAIService;
}
diff --git a/dotnet/src/SemanticKernel.Abstractions/KernelConfig.cs b/dotnet/src/SemanticKernel.Abstractions/KernelConfig.cs
index 4ca40d688c..44ad00c1a8 100644
--- a/dotnet/src/SemanticKernel.Abstractions/KernelConfig.cs
+++ b/dotnet/src/SemanticKernel.Abstractions/KernelConfig.cs
@@ -29,21 +29,25 @@ public sealed class KernelConfig
///
/// Text completion service factories
///
+ [Obsolete("This property is deprecated and will be removed in one of the next SK SDK versions.")]
public Dictionary> TextCompletionServices { get; } = new();
///
/// Chat completion service factories
///
+ [Obsolete("This property is deprecated and will be removed in one of the next SK SDK versions.")]
public Dictionary> ChatCompletionServices { get; } = new();
///
/// Text embedding generation service factories
///
+ [Obsolete("This property is deprecated and will be removed in one of the next SK SDK versions.")]
public Dictionary>> TextEmbeddingGenerationServices { get; } = new();
///
/// Image generation service factories
///
+ [Obsolete("This property is deprecated and will be removed in one of the next SK SDK versions.")]
public Dictionary> ImageGenerationServices { get; } = new();
///
@@ -58,6 +62,7 @@ public sealed class KernelConfig
/// Id used to identify the service
/// Current object instance
/// Failure if a service with the same id already exists
+ [Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions. Please use one of the WithAIService extension methods in the KernelBuilder class instead.")]
public KernelConfig AddTextCompletionService(
Func serviceFactory,
string? serviceId = null)
@@ -87,6 +92,7 @@ public sealed class KernelConfig
/// Id used to identify the service
/// Current object instance
/// Failure if a service with the same id already exists
+ [Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions. Please use one of the WithAIService extension methods in the KernelBuilder class instead.")]
public KernelConfig AddChatCompletionService(
Func serviceFactory,
string? serviceId = null)
@@ -116,6 +122,7 @@ public sealed class KernelConfig
/// Id used to identify the service
/// Current object instance
/// Failure if a service with the same id already exists
+ [Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions. Please use one of the WithAIService extension methods in the KernelBuilder class instead.")]
public KernelConfig AddTextEmbeddingGenerationService(
Func> serviceFactory,
string? serviceId = null)
@@ -145,6 +152,7 @@ public sealed class KernelConfig
/// Id used to identify the service
/// Current object instance
/// Failure if a service with the same id already exists
+ [Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions. Please use one of the WithAIService extension methods in the KernelBuilder class instead.")]
public KernelConfig AddImageGenerationService(
Func serviceFactory,
string? serviceId = null)
@@ -201,6 +209,7 @@ public sealed class KernelConfig
/// Identifier of completion service to use.
/// The updated kernel configuration.
/// Thrown if the requested service doesn't exist.
+ [Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions. Please use one of the WithDefaultAIService extension methods in the KernelBuilder class instead.")]
public KernelConfig SetDefaultTextCompletionService(string serviceId)
{
if (!this.TextCompletionServices.ContainsKey(serviceId))
@@ -220,6 +229,7 @@ public sealed class KernelConfig
/// Identifier of text embedding service to use.
/// The updated kernel configuration.
/// Thrown if the requested service doesn't exist.
+ [Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions. Please use one of the WithDefaultAIService extension methods in the KernelBuilder class instead.")]
public KernelConfig SetDefaultTextEmbeddingGenerationService(string serviceId)
{
if (!this.TextEmbeddingGenerationServices.ContainsKey(serviceId))
@@ -241,6 +251,7 @@ public sealed class KernelConfig
/// Remove all text completion services.
///
/// The updated kernel configuration.
+ [Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions.")]
public KernelConfig RemoveAllTextCompletionServices()
{
this.TextCompletionServices.Clear();
@@ -251,6 +262,7 @@ public sealed class KernelConfig
/// Remove all chat completion services.
///
/// The updated kernel configuration.
+ [Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions.")]
public KernelConfig RemoveAllChatCompletionServices()
{
this.ChatCompletionServices.Clear();
@@ -261,6 +273,7 @@ public sealed class KernelConfig
/// Remove all text embedding generation services.
///
/// The updated kernel configuration.
+ [Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions.")]
public KernelConfig RemoveAllTextEmbeddingGenerationServices()
{
this.TextEmbeddingGenerationServices.Clear();
diff --git a/dotnet/src/SemanticKernel.Abstractions/Services/IAIService.cs b/dotnet/src/SemanticKernel.Abstractions/Services/IAIService.cs
new file mode 100644
index 0000000000..3d2ab690bf
--- /dev/null
+++ b/dotnet/src/SemanticKernel.Abstractions/Services/IAIService.cs
@@ -0,0 +1,10 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.SemanticKernel.Services;
+
+[SuppressMessage("Design", "CA1040:Avoid empty interfaces")]
+public interface IAIService
+{
+}
diff --git a/dotnet/src/SemanticKernel.Abstractions/Services/IAIServiceProvider.cs b/dotnet/src/SemanticKernel.Abstractions/Services/IAIServiceProvider.cs
new file mode 100644
index 0000000000..5f4ff9a507
--- /dev/null
+++ b/dotnet/src/SemanticKernel.Abstractions/Services/IAIServiceProvider.cs
@@ -0,0 +1,7 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+namespace Microsoft.SemanticKernel.Services;
+
+public interface IAIServiceProvider : INamedServiceProvider
+{
+}
diff --git a/dotnet/src/SemanticKernel.Abstractions/Services/INamedServiceProvider.cs b/dotnet/src/SemanticKernel.Abstractions/Services/INamedServiceProvider.cs
new file mode 100644
index 0000000000..ca0ec11eb2
--- /dev/null
+++ b/dotnet/src/SemanticKernel.Abstractions/Services/INamedServiceProvider.cs
@@ -0,0 +1,14 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+namespace Microsoft.SemanticKernel.Services;
+
+public interface INamedServiceProvider
+{
+ ///
+ /// Gets the service of the specified type and name, or null if not found.
+ ///
+ /// The type of the service.
+ /// The name of the service, or null for the default service.
+ /// The service instance, or null if not found.
+ T? GetService(string? name = null) where T : TService;
+}
diff --git a/dotnet/src/SemanticKernel.Abstractions/Services/ServiceExtensions.cs b/dotnet/src/SemanticKernel.Abstractions/Services/ServiceExtensions.cs
new file mode 100644
index 0000000000..05c0ec77f3
--- /dev/null
+++ b/dotnet/src/SemanticKernel.Abstractions/Services/ServiceExtensions.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.SemanticKernel.Services;
+internal static class AIServiceProviderExtensions
+{
+ ///
+ /// Tries to get the service of the specified type and name, and returns a value indicating whether the operation succeeded.
+ ///
+ /// The type of the service.
+ /// The service provider.
+ /// The output parameter to receive the service instance, or null if not found.
+ /// True if the service was found, false otherwise.
+ public static bool TryGetService(this IAIServiceProvider serviceProvider,
+ [NotNullWhen(true)] out T? service) where T : IAIService
+ {
+ service = serviceProvider.GetService();
+ return service != null;
+ }
+
+ ///
+ /// Tries to get the service of the specified type and name, and returns a value indicating whether the operation succeeded.
+ ///
+ /// The type of the service.
+ /// The service provider.
+ /// The name of the service, or null for the default service.
+ /// The output parameter to receive the service instance, or null if not found.
+ /// True if the service was found, false otherwise.
+ public static bool TryGetService(this IAIServiceProvider serviceProvider,
+ string? name, [NotNullWhen(true)] out T? service) where T : IAIService
+ {
+ service = serviceProvider.GetService(name);
+ return service != null;
+ }
+}
diff --git a/dotnet/src/SemanticKernel.UnitTests/AI/ChatCompletion/ChatCompletionServiceExtensionTests.cs b/dotnet/src/SemanticKernel.UnitTests/AI/ChatCompletion/ChatCompletionServiceExtensionTests.cs
new file mode 100644
index 0000000000..86788477d4
--- /dev/null
+++ b/dotnet/src/SemanticKernel.UnitTests/AI/ChatCompletion/ChatCompletionServiceExtensionTests.cs
@@ -0,0 +1,147 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using Microsoft.SemanticKernel;
+using Microsoft.SemanticKernel.AI.ChatCompletion;
+using Microsoft.SemanticKernel.Services;
+using Moq;
+using Xunit;
+
+namespace SemanticKernel.UnitTests.AI.ChatCompletion;
+
+///
+/// Unit tests of .
+///
+public class ChatCompletionServiceExtensionsTests
+{
+ [Fact]
+ public void ItCanAddChatCompletionServiceInstance()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId = "test";
+ var instance = Mock.Of();
+
+ // Act
+ services.SetService(serviceId, instance);
+ var provider = services.Build();
+
+ // Assert
+ Assert.True(provider.TryGetService(serviceId, out var instanceRetrieved));
+ Assert.Same(instance, instanceRetrieved);
+ }
+
+ [Fact]
+ public void ItCanAddChatCompletionServiceFactory()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId = "test";
+ var instance = Mock.Of();
+ var factory = new Func(() => instance);
+
+ // Act
+ services.SetService(serviceId, factory);
+ var provider = services.Build();
+
+ // Assert
+ Assert.True(provider.TryGetService(serviceId, out _));
+ }
+
+ [Fact]
+ public void ItCanSetDefaultChatCompletionService()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId1 = "test1";
+ var serviceId2 = "test2";
+ var instance1 = Mock.Of();
+ var instance2 = Mock.Of();
+ services.SetService(serviceId1, instance1);
+
+ // Act
+ services.SetService(serviceId2, instance2, true);
+ var provider = services.Build();
+
+ // Assert
+ Assert.True(provider.TryGetService(out var instanceRetrieved));
+ Assert.Same(instance2, instanceRetrieved);
+ }
+
+ [Fact]
+ public void ItReturnsFalseIfNoDefaultChatCompletionServiceIsSet()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var provider = services.Build();
+
+ // Assert
+ Assert.False(provider.TryGetService(out var instanceRetrieved));
+ }
+
+ [Fact]
+ public void ItReturnsTrueIfHasChatCompletionServiceWithValidId()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId = "test";
+ var instance = Mock.Of();
+ services.SetService(serviceId, instance);
+
+ // Act
+ var provider = services.Build();
+ var result = provider.HasChatCompletionService(serviceId);
+
+ // Assert
+ Assert.True(result);
+ }
+
+ [Fact]
+ public void ItReturnsFalseIfHasChatCompletionServiceWithInvalidId()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId1 = "test1";
+ var serviceId2 = "test2";
+ var instance = Mock.Of();
+ services.SetService(serviceId1, instance);
+ var provider = services.Build();
+
+ // Act
+ var result = provider.HasChatCompletionService(serviceId2);
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void ItReturnsTrueIfHasChatCompletionServiceWithNullIdAndDefaultIsSet()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId = "test";
+ var instance = Mock.Of();
+ services.SetService(serviceId, instance, setAsDefault: true);
+ var provider = services.Build();
+
+ // Act
+ var result = provider.HasChatCompletionService();
+
+ // Assert
+ Assert.True(result);
+ }
+
+ [Fact]
+ public void ItReturnsFalseIfHasChatCompletionServiceWithNullIdAndNoDefaultExists()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var provider = services.Build();
+
+ // Act
+ var result = provider.HasChatCompletionService();
+
+ // Assert
+ Assert.False(result);
+ }
+}
diff --git a/dotnet/src/SemanticKernel.UnitTests/AI/Embeddings/TextEmbeddingServiceExtensionTests.cs b/dotnet/src/SemanticKernel.UnitTests/AI/Embeddings/TextEmbeddingServiceExtensionTests.cs
new file mode 100644
index 0000000000..0638ce0085
--- /dev/null
+++ b/dotnet/src/SemanticKernel.UnitTests/AI/Embeddings/TextEmbeddingServiceExtensionTests.cs
@@ -0,0 +1,147 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using Microsoft.SemanticKernel;
+using Microsoft.SemanticKernel.AI.Embeddings;
+using Microsoft.SemanticKernel.Services;
+using Moq;
+using Xunit;
+
+namespace SemanticKernel.UnitTests.AI.Embeddings;
+
+///
+/// Unit tests of .
+///
+public class TextEmbeddingServiceExtensionsTests
+{
+ [Fact]
+ public void ItCanAddTextEmbeddingServiceInstance()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId = "test";
+ var instance = Mock.Of();
+
+ // Act
+ services.SetService(serviceId, instance);
+ var provider = services.Build();
+
+ // Assert
+ Assert.True(provider.TryGetService(serviceId, out var instanceRetrieved));
+ Assert.Same(instance, instanceRetrieved);
+ }
+
+ [Fact]
+ public void ItCanAddTextEmbeddingServiceFactory()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId = "test";
+ var instance = Mock.Of();
+ var factory = new Func(() => instance);
+
+ // Act
+ services.SetService(serviceId, factory);
+ var provider = services.Build();
+
+ // Assert
+ Assert.True(provider.TryGetService(serviceId, out _));
+ }
+
+ [Fact]
+ public void ItCanSetDefaultTextEmbeddingService()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId1 = "test1";
+ var serviceId2 = "test2";
+ var instance1 = Mock.Of();
+ var instance2 = Mock.Of();
+ services.SetService(serviceId1, instance1);
+
+ // Act
+ services.SetService(serviceId2, instance2, setAsDefault: true);
+ var provider = services.Build();
+
+ // Assert
+ Assert.True(provider.TryGetService(out var instanceRetrieved));
+ Assert.Same(instance2, instanceRetrieved);
+ }
+
+ [Fact]
+ public void ItReturnsFalseIfNoDefaultTextEmbeddingServiceIsSet()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var provider = services.Build();
+
+ // Assert
+ Assert.False(provider.TryGetService(out var instanceRetrieved));
+ }
+
+ [Fact]
+ public void ItReturnsTrueIfHasTextEmbeddingServiceWithValidId()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId = "test";
+ var instance = Mock.Of();
+ services.SetService(serviceId, instance);
+ var provider = services.Build();
+
+ // Act
+ var result = provider.HasTextEmbeddingService(serviceId);
+
+ // Assert
+ Assert.True(result);
+ }
+
+ [Fact]
+ public void ItReturnsFalseIfHasTextEmbeddingServiceWithInvalidId()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId1 = "test1";
+ var serviceId2 = "test2";
+ var instance = Mock.Of();
+ services.SetService(serviceId1, instance);
+ var provider = services.Build();
+
+ // Act
+ var result = provider.HasTextEmbeddingService(serviceId2);
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void ItReturnsTrueIfHasTextEmbeddingServiceWithNullIdAndDefaultIsSet()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId = "test";
+ var instance = Mock.Of();
+ services.SetService(serviceId, instance, setAsDefault: true);
+ var provider = services.Build();
+
+ // Act
+ var result = provider.HasTextEmbeddingService();
+
+ // Assert
+ Assert.True(result);
+ }
+
+ [Fact]
+ public void ItReturnsFalseIfHasTextEmbeddingServiceWithNullIdAndNoDefaultExists()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var provider = services.Build();
+
+ // Act
+ var result = provider.HasTextEmbeddingService();
+
+ // Assert
+ Assert.False(result);
+ }
+}
diff --git a/dotnet/src/SemanticKernel.UnitTests/AI/ImageGeneration/ImageCompletionServiceExtensionTests.cs b/dotnet/src/SemanticKernel.UnitTests/AI/ImageGeneration/ImageCompletionServiceExtensionTests.cs
new file mode 100644
index 0000000000..165f5e636d
--- /dev/null
+++ b/dotnet/src/SemanticKernel.UnitTests/AI/ImageGeneration/ImageCompletionServiceExtensionTests.cs
@@ -0,0 +1,146 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using Microsoft.SemanticKernel;
+using Microsoft.SemanticKernel.AI.ImageGeneration;
+using Microsoft.SemanticKernel.Services;
+using Moq;
+using Xunit;
+
+namespace SemanticKernel.UnitTests.AI.ImageGeneration;
+
+///
+/// Unit tests of .
+///
+public class ImageGenerationServiceExtensionsTests
+{
+ [Fact]
+ public void ItCanSetServiceImageGenerationInstance()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId = "test";
+ var instance = Mock.Of();
+
+ // Act
+ services.SetService(serviceId, instance);
+ var provider = services.Build();
+
+ // Assert
+ Assert.True(provider.TryGetService(serviceId, out var instanceRetrieved));
+ Assert.Same(instance, instanceRetrieved);
+ }
+
+ [Fact]
+ public void ItCanSetServiceImageGenerationFactory()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId = "test";
+ var instance = Mock.Of();
+ var factory = new Func(() => instance);
+
+ // Act
+ services.SetService(serviceId, factory);
+ var provider = services.Build();
+
+ // Assert
+ Assert.True(provider.TryGetService(out var instanceRetrieved));
+ }
+
+ [Fact]
+ public void ItCanSetDefaultImageGenerationService()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId1 = "test1";
+ var serviceId2 = "test2";
+ var instance1 = Mock.Of();
+ var instance2 = Mock.Of();
+ services.SetService(serviceId1, instance1);
+
+ // Act
+ services.SetService(serviceId2, instance2, setAsDefault: true);
+ var provider = services.Build();
+
+ // Assert
+ Assert.True(provider.TryGetService(out var instanceRetrieved));
+ Assert.Same(instance2, instanceRetrieved);
+ }
+
+ [Fact]
+ public void ItReturnsFalseIfNoDefaultImageGenerationServiceIsSet()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var provider = services.Build();
+
+ Assert.False(provider.TryGetService(out var instanceRetrieved));
+ }
+
+ [Fact]
+ public void ItReturnsTrueIfHasImageGenerationServiceWithValidId()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId = "test";
+ var instance = Mock.Of();
+ services.SetService(serviceId, instance);
+ var provider = services.Build();
+
+ // Act
+ var result = provider.HasImageGenerationService(serviceId);
+
+ // Assert
+ Assert.True(result);
+ }
+
+ [Fact]
+ public void ItReturnsFalseIfHasImageGenerationServiceWithInvalidId()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId1 = "test1";
+ var serviceId2 = "test2";
+ var instance = Mock.Of();
+ services.SetService(serviceId1, instance);
+ var provider = services.Build();
+
+ // Act
+ var result = provider.HasImageGenerationService(serviceId2);
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void ItReturnsTrueIfHasImageGenerationServiceWithNullIdAndDefaultIsSet()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId = "test";
+ var instance = Mock.Of();
+ services.SetService(serviceId, instance, setAsDefault: true);
+ var provider = services.Build();
+
+ // Act
+ var result = provider.HasImageGenerationService();
+
+ // Assert
+ Assert.True(result);
+ }
+
+ [Fact]
+ public void ItReturnsFalseIfHasImageGenerationServiceWithNullIdAndNoDefaultExists()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var provider = services.Build();
+
+ // Act
+ var result = provider.HasImageGenerationService();
+
+ // Assert
+ Assert.False(result);
+ }
+}
diff --git a/dotnet/src/SemanticKernel.UnitTests/AI/TextCompletion/TextCompletionServiceExtensionTests.cs b/dotnet/src/SemanticKernel.UnitTests/AI/TextCompletion/TextCompletionServiceExtensionTests.cs
new file mode 100644
index 0000000000..689b88d115
--- /dev/null
+++ b/dotnet/src/SemanticKernel.UnitTests/AI/TextCompletion/TextCompletionServiceExtensionTests.cs
@@ -0,0 +1,147 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using Microsoft.SemanticKernel;
+using Microsoft.SemanticKernel.AI.TextCompletion;
+using Microsoft.SemanticKernel.Services;
+using Moq;
+using Xunit;
+
+namespace SemanticKernel.UnitTests.AI.TextCompletion;
+
+///
+/// Unit tests of .
+///
+public class TextCompletionServiceExtensionsTests
+{
+ [Fact]
+ public void ItCanAddTextCompletionServiceInstance()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId = "test";
+ var instance = Mock.Of();
+
+ // Act
+ services.SetService(serviceId, instance);
+ var provider = services.Build();
+
+ // Assert
+ Assert.True(provider.TryGetService(serviceId, out var instanceRetrieved));
+ Assert.Same(instance, instanceRetrieved);
+ }
+
+ [Fact]
+ public void ItCanAddTextCompletionServiceFactory()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId = "test";
+ var instance = Mock.Of();
+ var factory = new Func(() => instance);
+
+ // Act
+ services.SetService(serviceId, factory);
+ var provider = services.Build();
+
+ // Assert
+ Assert.True(provider.TryGetService(serviceId, out var _));
+ }
+
+ [Fact]
+ public void ItCanSetDefaultTextCompletionService()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId1 = "test1";
+ var serviceId2 = "test2";
+ var instance1 = Mock.Of();
+ var instance2 = Mock.Of();
+ services.SetService(serviceId1, instance1);
+
+ // Act
+ services.SetService(serviceId2, instance2, setAsDefault: true);
+ var provider = services.Build();
+
+ // Assert
+ Assert.True(provider.TryGetService(out var instanceRetrieved));
+ Assert.Same(instance2, instanceRetrieved);
+ }
+
+ [Fact]
+ public void ItReturnsFalseIfNoDefaultTextCompletionServiceIsSet()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var provider = services.Build();
+
+ // Assert
+ Assert.False(provider.TryGetService(out var _));
+ }
+
+ [Fact]
+ public void ItReturnsTrueIfHasTextCompletionServiceWithValidId()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId = "test";
+ var instance = Mock.Of();
+ services.SetService(serviceId, instance);
+ var provider = services.Build();
+
+ // Act
+ var result = provider.HasTextCompletionService(serviceId);
+
+ // Assert
+ Assert.True(result);
+ }
+
+ [Fact]
+ public void ItReturnsFalseIfHasTextCompletionServiceWithInvalidId()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId1 = "test1";
+ var serviceId2 = "test2";
+ var instance = Mock.Of();
+ services.SetService(serviceId1, instance);
+ var provider = services.Build();
+
+ // Act
+ var result = provider.HasTextCompletionService(serviceId2);
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void ItReturnsTrueIfHasTextCompletionServiceWithNullIdAndDefaultIsSet()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var serviceId = "test";
+ var instance = Mock.Of();
+ services.SetService(serviceId, instance, setAsDefault: true);
+ var provider = services.Build();
+
+ // Act
+ var result = provider.HasTextCompletionService();
+
+ // Assert
+ Assert.True(result);
+ }
+
+ [Fact]
+ public void ItReturnsFalseIfHasTextCompletionServiceWithNullIdAndNoDefaultExists()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var provider = services.Build();
+
+ // Act
+ var result = provider.HasTextCompletionService();
+
+ // Assert
+ Assert.False(result);
+ }
+}
diff --git a/dotnet/src/SemanticKernel.UnitTests/CoreSkills/FileIOSkillTests.cs b/dotnet/src/SemanticKernel.UnitTests/CoreSkills/FileIOSkillTests.cs
index f617856a59..0dfe8b2d53 100644
--- a/dotnet/src/SemanticKernel.UnitTests/CoreSkills/FileIOSkillTests.cs
+++ b/dotnet/src/SemanticKernel.UnitTests/CoreSkills/FileIOSkillTests.cs
@@ -27,7 +27,7 @@ public class FileIOSkillTests
public void ItCanBeImported()
{
// Arrange
- var kernel = KernelBuilder.Create();
+ var kernel = Kernel.Builder.Build();
// Act - Assert no exception occurs e.g. due to reflection
_ = kernel.ImportSkill(new FileIOSkill(), "fileIO");
diff --git a/dotnet/src/SemanticKernel.UnitTests/CoreSkills/MathSkillTests.cs b/dotnet/src/SemanticKernel.UnitTests/CoreSkills/MathSkillTests.cs
index 6f4239bcd3..1c3cc2f405 100644
--- a/dotnet/src/SemanticKernel.UnitTests/CoreSkills/MathSkillTests.cs
+++ b/dotnet/src/SemanticKernel.UnitTests/CoreSkills/MathSkillTests.cs
@@ -26,7 +26,7 @@ public class MathSkillTests
public void ItCanBeImported()
{
// Arrange
- var kernel = KernelBuilder.Create();
+ var kernel = Kernel.Builder.Build();
// Act - Assert no exception occurs e.g. due to reflection
kernel.ImportSkill(new MathSkill(), "math");
diff --git a/dotnet/src/SemanticKernel.UnitTests/CoreSkills/TextSkillTests.cs b/dotnet/src/SemanticKernel.UnitTests/CoreSkills/TextSkillTests.cs
index 300339d292..138e17982b 100644
--- a/dotnet/src/SemanticKernel.UnitTests/CoreSkills/TextSkillTests.cs
+++ b/dotnet/src/SemanticKernel.UnitTests/CoreSkills/TextSkillTests.cs
@@ -24,7 +24,7 @@ public class TextSkillTests
public void ItCanBeImported()
{
// Arrange
- var kernel = KernelBuilder.Create();
+ var kernel = Kernel.Builder.Build();
// Act - Assert no exception occurs e.g. due to reflection
kernel.ImportSkill(new TextSkill(), "text");
diff --git a/dotnet/src/SemanticKernel.UnitTests/CoreSkills/TimeSkillTests.cs b/dotnet/src/SemanticKernel.UnitTests/CoreSkills/TimeSkillTests.cs
index b6a22b2aed..9f63f9cdfa 100644
--- a/dotnet/src/SemanticKernel.UnitTests/CoreSkills/TimeSkillTests.cs
+++ b/dotnet/src/SemanticKernel.UnitTests/CoreSkills/TimeSkillTests.cs
@@ -20,7 +20,7 @@ public class TimeSkillTests
public void ItCanBeImported()
{
// Arrange
- var kernel = KernelBuilder.Create();
+ var kernel = Kernel.Builder.Build();
// Act - Assert no exception occurs e.g. due to reflection
kernel.ImportSkill(new TimeSkill(), "time");
diff --git a/dotnet/src/SemanticKernel.UnitTests/CoreSkills/WaitSkillTests.cs b/dotnet/src/SemanticKernel.UnitTests/CoreSkills/WaitSkillTests.cs
index b9c669d34d..1911a96ecd 100644
--- a/dotnet/src/SemanticKernel.UnitTests/CoreSkills/WaitSkillTests.cs
+++ b/dotnet/src/SemanticKernel.UnitTests/CoreSkills/WaitSkillTests.cs
@@ -23,7 +23,7 @@ public class WaitSkillTests
public void ItCanBeImported()
{
// Arrange
- var kernel = KernelBuilder.Create();
+ var kernel = Kernel.Builder.Build();
// Act - Assert no exception occurs e.g. due to reflection
kernel.ImportSkill(new WaitSkill(), "wait");
diff --git a/dotnet/src/SemanticKernel.UnitTests/KernelConfigTests.cs b/dotnet/src/SemanticKernel.UnitTests/KernelConfigTests.cs
index 50ecb9ce1a..1507911406 100644
--- a/dotnet/src/SemanticKernel.UnitTests/KernelConfigTests.cs
+++ b/dotnet/src/SemanticKernel.UnitTests/KernelConfigTests.cs
@@ -76,6 +76,7 @@ public class KernelConfigTests
}
[Fact]
+ [System.Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions.")]
public void ItFailsWhenSetNonExistentTextCompletionService()
{
var target = new KernelConfig();
@@ -87,6 +88,7 @@ public class KernelConfigTests
}
[Fact]
+ [System.Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions.")]
public void ItFailsWhenSetNonExistentEmbeddingService()
{
var target = new KernelConfig();
diff --git a/dotnet/src/SemanticKernel.UnitTests/KernelTests.cs b/dotnet/src/SemanticKernel.UnitTests/KernelTests.cs
index df8d929562..6b50359ff7 100644
--- a/dotnet/src/SemanticKernel.UnitTests/KernelTests.cs
+++ b/dotnet/src/SemanticKernel.UnitTests/KernelTests.cs
@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.AI.TextCompletion;
using Microsoft.SemanticKernel.Orchestration;
@@ -18,7 +19,8 @@ namespace SemanticKernel.UnitTests;
public class KernelTests
{
[Fact]
- public void ItProvidesAccessToFunctionsViaSkillCollection()
+ [System.Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions.")]
+ public void ItProvidesAccessToFunctionsViaSkillCollectionObsolete()
{
// Arrange
var kernel = KernelBuilder.Create();
@@ -46,7 +48,8 @@ public class KernelTests
}
[Fact]
- public async Task ItProvidesAccessToFunctionsViaSKContextAsync()
+ [System.Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions.")]
+ public async Task ItProvidesAccessToFunctionsViaSKContextObsoleteAsync()
{
// Arrange
var kernel = KernelBuilder.Create();
@@ -69,11 +72,65 @@ public class KernelTests
Assert.Equal("Export info.", result["mysk.readskillcollectionasync"]);
}
+ [Fact]
+ public void ItProvidesAccessToFunctionsViaSkillCollection()
+ {
+ // Arrange
+ var factory = new Mock>();
+ var kernel = Kernel.Builder
+ .WithDefaultAIService(factory.Object)
+ .Build();
+
+ var nativeSkill = new MySkill();
+ kernel.CreateSemanticFunction(promptTemplate: "Tell me a joke", functionName: "joker", skillName: "jk", description: "Nice fun");
+ kernel.ImportSkill(nativeSkill, "mySk");
+
+ // Act
+ FunctionsView data = kernel.Skills.GetFunctionsView();
+
+ // Assert - 3 functions, var name is not case sensitive
+ Assert.True(data.IsSemantic("jk", "joker"));
+ Assert.True(data.IsSemantic("JK", "JOKER"));
+ Assert.False(data.IsNative("jk", "joker"));
+ Assert.False(data.IsNative("JK", "JOKER"));
+ Assert.True(data.IsNative("mySk", "sayhello"));
+ Assert.True(data.IsNative("MYSK", "SayHello"));
+ Assert.True(data.IsNative("mySk", "ReadSkillCollectionAsync"));
+ Assert.True(data.IsNative("MYSK", "readskillcollectionasync"));
+ Assert.Single(data.SemanticFunctions["Jk"]);
+ Assert.Equal(3, data.NativeFunctions["mySk"].Count);
+ }
+
+ [Fact]
+ public async Task ItProvidesAccessToFunctionsViaSKContextAsync()
+ {
+ // Arrange
+ var factory = new Mock>();
+ var kernel = Kernel.Builder
+ .WithAIService("x", factory.Object)
+ .Build();
+
+ var nativeSkill = new MySkill();
+ kernel.CreateSemanticFunction("Tell me a joke", functionName: "joker", skillName: "jk", description: "Nice fun");
+ var skill = kernel.ImportSkill(nativeSkill, "mySk");
+
+ // Act
+ SKContext result = await kernel.RunAsync(skill["ReadSkillCollectionAsync"]);
+
+ // Assert - 3 functions, var name is not case sensitive
+ Assert.Equal("Nice fun", result["jk.joker"]);
+ Assert.Equal("Nice fun", result["JK.JOKER"]);
+ Assert.Equal("Just say hello", result["mySk.sayhello"]);
+ Assert.Equal("Just say hello", result["mySk.SayHello"]);
+ Assert.Equal("Export info.", result["mySk.ReadSkillCollectionAsync"]);
+ Assert.Equal("Export info.", result["mysk.readskillcollectionasync"]);
+ }
+
[Fact]
public async Task RunAsyncDoesNotRunWhenCancelledAsync()
{
// Arrange
- var kernel = KernelBuilder.Create();
+ var kernel = Kernel.Builder.Build();
var nativeSkill = new MySkill();
var skill = kernel.ImportSkill(nativeSkill, "mySk");
@@ -93,7 +150,7 @@ public class KernelTests
public async Task RunAsyncRunsWhenNotCancelledAsync()
{
// Arrange
- var kernel = KernelBuilder.Create();
+ var kernel = Kernel.Builder.Build();
var nativeSkill = new MySkill();
kernel.ImportSkill(nativeSkill, "mySk");
@@ -112,7 +169,7 @@ public class KernelTests
public void ItImportsSkillsNotCaseSensitive()
{
// Act
- IDictionary skill = KernelBuilder.Create().ImportSkill(new MySkill(), "test");
+ IDictionary skill = Kernel.Builder.Build().ImportSkill(new MySkill(), "test");
// Assert
Assert.Equal(3, skill.Count);
@@ -125,7 +182,7 @@ public class KernelTests
public void ItAllowsToImportSkillsInTheGlobalNamespace()
{
// Arrange
- var kernel = KernelBuilder.Create();
+ var kernel = Kernel.Builder.Build();
// Act
IDictionary skill = kernel.ImportSkill(new MySkill());
@@ -140,7 +197,7 @@ public class KernelTests
public void ItAllowsToImportTheSameSkillMultipleTimes()
{
// Arrange
- var kernel = KernelBuilder.Create();
+ var kernel = Kernel.Builder.Build();
// Act - Assert no exception occurs
kernel.ImportSkill(new MySkill());
@@ -148,16 +205,6 @@ public class KernelTests
kernel.ImportSkill(new MySkill());
}
- [Fact]
- public void ItFailsIfTextCompletionServiceConfigIsNotSet()
- {
- // Arrange
- var kernel = KernelBuilder.Create();
-
- var exception = Assert.Throws(
- () => kernel.CreateSemanticFunction(promptTemplate: "Tell me a joke", functionName: "joker", skillName: "jk", description: "Nice fun"));
- }
-
public class MySkill
{
[SKFunction("Return any value.")]
diff --git a/dotnet/src/SemanticKernel.UnitTests/SemanticKernel.UnitTests.csproj b/dotnet/src/SemanticKernel.UnitTests/SemanticKernel.UnitTests.csproj
index 6931774463..3e9cae6a87 100644
--- a/dotnet/src/SemanticKernel.UnitTests/SemanticKernel.UnitTests.csproj
+++ b/dotnet/src/SemanticKernel.UnitTests/SemanticKernel.UnitTests.csproj
@@ -27,6 +27,7 @@
+
diff --git a/dotnet/src/SemanticKernel.UnitTests/Services/ServiceRegistryTests.cs b/dotnet/src/SemanticKernel.UnitTests/Services/ServiceRegistryTests.cs
new file mode 100644
index 0000000000..346d402bbc
--- /dev/null
+++ b/dotnet/src/SemanticKernel.UnitTests/Services/ServiceRegistryTests.cs
@@ -0,0 +1,219 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.SemanticKernel.Services;
+using Xunit;
+
+namespace SemanticKernel.UnitTests.Services;
+
+///
+/// Unit tests of .
+///
+public class ServiceRegistryTests
+{
+ [Fact]
+ public void ItCanSetAndRetrieveServiceInstance()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var service = new TestService();
+
+ // Act
+ services.SetService(service);
+ var provider = services.Build();
+ var result = provider.GetService();
+
+ // Assert
+ Assert.Same(service, result);
+ }
+
+ [Fact]
+ public void ItCanSetAndRetrieveServiceInstanceWithName()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var service1 = new TestService();
+ var service2 = new TestService();
+
+ // Act
+ services.SetService("foo", service1);
+ services.SetService("bar", service2);
+ var provider = services.Build();
+
+ // Assert
+ Assert.Same(service1, provider.GetService("foo"));
+ Assert.Same(service2, provider.GetService("bar"));
+ }
+
+ [Fact]
+ public void ItCanSetAndRetrieveServiceFactory()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var service = new TestService();
+
+ // Act
+ services.SetService(() => service);
+ var provider = services.Build();
+
+ // Assert
+ Assert.Same(service, provider.GetService());
+ }
+
+ [Fact]
+ public void ItCanSetAndRetrieveServiceFactoryWithName()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var service1 = new TestService();
+ var service2 = new TestService();
+
+ // Act
+ services.SetService("foo", () => service1);
+ services.SetService("bar", () => service2);
+ var provider = services.Build();
+
+ // Assert
+ Assert.Same(service1, provider.GetService("foo"));
+ Assert.Same(service2, provider.GetService("bar"));
+ }
+
+ [Fact]
+ public void ItCanSetAndRetrieveServiceFactoryWithServiceProvider()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var service = new TestService();
+
+ // Act
+ services.SetService(() => service);
+ var provider = services.Build();
+
+ // Assert
+ Assert.Same(service, provider.GetService());
+ }
+
+ [Fact]
+ public void ItCanSetAndRetrieveServiceFactoryWithServiceProviderAndName()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var service1 = new TestService();
+ var service2 = new TestService();
+
+ // Act
+ services.SetService("foo", () => service1);
+ services.SetService("bar", () => service2);
+ var provider = services.Build();
+
+ // Assert
+ Assert.Same(service1, provider.GetService("foo"));
+ Assert.Same(service2, provider.GetService("bar"));
+ }
+
+ [Fact]
+ public void ItCanSetDefaultService()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var service1 = new TestService();
+ var service2 = new TestService();
+
+ // Act
+ services.SetService("foo", service1);
+ services.SetService("bar", service2, setAsDefault: true);
+ var provider = services.Build();
+
+ // Assert
+ Assert.Same(service2, provider.GetService());
+ }
+
+ [Fact]
+ public void ItCanSetDefaultServiceFactory()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var service1 = new TestService();
+ var service2 = new TestService();
+
+ // Act
+ services.SetService("foo", () => service1);
+ services.SetService("bar", () => service2, setAsDefault: true);
+ var provider = services.Build();
+
+ // Assert
+ Assert.Same(service2, provider.GetService());
+ }
+
+ [Fact]
+ public void ItCanSetDefaultServiceFactoryWithServiceProvider()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var service1 = new TestService();
+ var service2 = new TestService();
+
+ // Act
+ services.SetService("foo", () => service1);
+ services.SetService("bar", () => service2, setAsDefault: true);
+ var provider = services.Build();
+
+ // Assert
+ Assert.Same(service2, provider.GetService());
+ }
+
+ [Fact]
+ public void ItCanTryGetService()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var service = new TestService();
+ services.SetService(service);
+ var provider = services.Build();
+
+ // Act
+ var result = provider.TryGetService(out IAIService? retrieved);
+
+ // Assert
+ Assert.True(result);
+ Assert.Same(service, retrieved);
+ }
+
+ [Fact]
+ public void ItCanTryGetServiceWithName()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var service = new TestService();
+ services.SetService("foo", service);
+ var provider = services.Build();
+
+ // Act
+ var result = provider.TryGetService("foo", out IAIService? retrieved);
+
+ // Assert
+ Assert.True(result);
+ Assert.Same(service, retrieved);
+ }
+
+ [Fact]
+ public void ItReturnsFalseIfTryGetServiceWithInvalidName()
+ {
+ // Arrange
+ var services = new AIServiceCollection();
+ var service = new TestService();
+ services.SetService("foo", service);
+ var provider = services.Build();
+
+ // Act
+ var result = provider.TryGetService("bar", out IAIService? retrieved);
+
+ // Assert
+ Assert.False(result);
+ Assert.Null(retrieved);
+ }
+
+ // A test service implementation
+ private sealed class TestService : IAIService
+ {
+ }
+}
diff --git a/dotnet/src/SemanticKernel/Kernel.cs b/dotnet/src/SemanticKernel/Kernel.cs
index 07f1d5636e..d07a8b55b6 100644
--- a/dotnet/src/SemanticKernel/Kernel.cs
+++ b/dotnet/src/SemanticKernel/Kernel.cs
@@ -15,6 +15,7 @@ using Microsoft.SemanticKernel.Diagnostics;
using Microsoft.SemanticKernel.Memory;
using Microsoft.SemanticKernel.Orchestration;
using Microsoft.SemanticKernel.SemanticFunctions;
+using Microsoft.SemanticKernel.Services;
using Microsoft.SemanticKernel.SkillDefinition;
using Microsoft.SemanticKernel.TemplateEngine;
@@ -58,12 +59,14 @@ public sealed class Kernel : IKernel, IDisposable
/// Kernel constructor. See KernelBuilder for an easier and less error prone approach to create kernel instances.
///
///
+ ///
///
///
///
///
public Kernel(
ISkillCollection skillCollection,
+ IAIServiceProvider aiServiceProvider,
IPromptTemplateEngine promptTemplateEngine,
ISemanticTextMemory memory,
KernelConfig config,
@@ -73,6 +76,8 @@ public sealed class Kernel : IKernel, IDisposable
this.Config = config;
this.PromptTemplateEngine = promptTemplateEngine;
this._memory = memory;
+ this._aiServiceProvider = aiServiceProvider;
+ this._promptTemplateEngine = promptTemplateEngine;
this._skillCollection = skillCollection;
}
@@ -221,22 +226,26 @@ public sealed class Kernel : IKernel, IDisposable
}
///
- public T GetService(string? name = null)
+ public T GetService(string? name = null) where T : IAIService
{
- // TODO: use .NET ServiceCollection (will require a lot of changes)
- // TODO: support Connectors, IHttpFactory and IDelegatingHandlerFactory
+ var service = this._aiServiceProvider.GetService(name);
+ if (service != null)
+ {
+ return service;
+ }
if (typeof(T) == typeof(ITextCompletion))
{
name ??= this.Config.DefaultServiceId;
+#pragma warning disable CS0618 // Type or member is obsolete
if (!this.Config.TextCompletionServices.TryGetValue(name, out Func factory))
{
throw new KernelException(KernelException.ErrorCodes.ServiceNotFound, $"'{name}' text completion service not available");
}
- var service = factory.Invoke(this);
- return (T)service;
+ var serv = factory.Invoke(this);
+ return (T)serv;
}
if (typeof(T) == typeof(IEmbeddingGeneration))
@@ -248,8 +257,8 @@ public sealed class Kernel : IKernel, IDisposable
throw new KernelException(KernelException.ErrorCodes.ServiceNotFound, $"'{name}' text embedding service not available");
}
- var service = factory.Invoke(this);
- return (T)service;
+ var serv = factory.Invoke(this);
+ return (T)serv;
}
if (typeof(T) == typeof(IChatCompletion))
@@ -261,8 +270,8 @@ public sealed class Kernel : IKernel, IDisposable
throw new KernelException(KernelException.ErrorCodes.ServiceNotFound, $"'{name}' chat completion service not available");
}
- var service = factory.Invoke(this);
- return (T)service;
+ var serv = factory.Invoke(this);
+ return (T)serv;
}
if (typeof(T) == typeof(IImageGeneration))
@@ -274,11 +283,12 @@ public sealed class Kernel : IKernel, IDisposable
throw new KernelException(KernelException.ErrorCodes.ServiceNotFound, $"'{name}' image generation service not available");
}
- var service = factory.Invoke(this);
- return (T)service;
+ var serv = factory.Invoke(this);
+ return (T)serv;
}
+#pragma warning restore CS0618 // Type or member is obsolete
- throw new NotSupportedException("The kernel service collection doesn't support the type " + typeof(T).FullName);
+ throw new KernelException(KernelException.ErrorCodes.ServiceNotFound, $"Service of type {typeof(T)} and name {name ?? ""} not registered.");
}
///
@@ -297,6 +307,8 @@ public sealed class Kernel : IKernel, IDisposable
private readonly ISkillCollection _skillCollection;
private ISemanticTextMemory _memory;
+ private readonly IPromptTemplateEngine _promptTemplateEngine;
+ private readonly IAIServiceProvider _aiServiceProvider;
private ISKFunction CreateSemanticFunction(
string skillName,
diff --git a/dotnet/src/SemanticKernel/KernelBuilder.cs b/dotnet/src/SemanticKernel/KernelBuilder.cs
index 3e32ea0462..352b8a6f32 100644
--- a/dotnet/src/SemanticKernel/KernelBuilder.cs
+++ b/dotnet/src/SemanticKernel/KernelBuilder.cs
@@ -7,6 +7,7 @@ using Microsoft.SemanticKernel.AI.Embeddings;
using Microsoft.SemanticKernel.Diagnostics;
using Microsoft.SemanticKernel.Memory;
using Microsoft.SemanticKernel.Reliability;
+using Microsoft.SemanticKernel.Services;
using Microsoft.SemanticKernel.SkillDefinition;
using Microsoft.SemanticKernel.TemplateEngine;
@@ -20,9 +21,11 @@ public sealed class KernelBuilder
{
private KernelConfig _config = new();
private ISemanticTextMemory _memory = NullMemory.Instance;
- private ILogger _log = NullLogger.Instance;
+ private ILogger _logger = NullLogger.Instance;
private IMemoryStore? _memoryStorage = null;
private IDelegatingHandlerFactory? _httpHandlerFactory = null;
+ private IPromptTemplateEngine? _promptTemplateEngine;
+ private readonly AIServiceCollection _aiServices = new();
///
/// Create a new kernel instance
@@ -46,11 +49,12 @@ public sealed class KernelBuilder
}
var instance = new Kernel(
- new SkillCollection(this._log),
- new PromptTemplateEngine(this._log),
+ new SkillCollection(this._logger),
+ this._aiServices.Build(),
+ this._promptTemplateEngine ?? new PromptTemplateEngine(this._logger),
this._memory,
this._config,
- this._log
+ this._logger
);
// TODO: decouple this from 'UseMemory' kernel extension
@@ -70,7 +74,7 @@ public sealed class KernelBuilder
public KernelBuilder WithLogger(ILogger log)
{
Verify.NotNull(log);
- this._log = log;
+ this._logger = log;
return this;
}
@@ -98,6 +102,18 @@ public sealed class KernelBuilder
return this;
}
+ ///
+ /// Add prompt template engine to the kernel to be built.
+ ///
+ /// Prompt template engine to add.
+ /// Updated kernel builder including the prompt template engine.
+ public KernelBuilder WithPromptTemplateEngine(IPromptTemplateEngine promptTemplateEngine)
+ {
+ Verify.NotNull(promptTemplateEngine);
+ this._promptTemplateEngine = promptTemplateEngine;
+ return this;
+ }
+
///
/// Add memory storage and an embedding generator to the kernel to be built.
///
@@ -105,7 +121,7 @@ public sealed class KernelBuilder
/// Embedding generator to add.
/// Updated kernel builder including the memory storage and embedding generator.
public KernelBuilder WithMemoryStorageAndTextEmbeddingGeneration(
- IMemoryStore storage, IEmbeddingGeneration embeddingGenerator)
+ IMemoryStore storage, ITextEmbeddingGeneration embeddingGenerator)
{
Verify.NotNull(storage);
Verify.NotNull(embeddingGenerator);
@@ -148,4 +164,54 @@ public sealed class KernelBuilder
configure.Invoke(this._config);
return this;
}
+
+ ///
+ /// Adds a instance to the services collection
+ ///
+ /// The instance.
+ public KernelBuilder WithDefaultAIService(TService instance) where TService : IAIService
+ {
+ this._aiServices.SetService(instance);
+ return this;
+ }
+
+ ///
+ /// Adds a instance to the services collection
+ ///
+ /// The service ID
+ /// The instance.
+ /// Optional: set as the default AI service for type
+ public KernelBuilder WithAIService(
+ string? serviceId,
+ TService instance,
+ bool setAsDefault = false) where TService : IAIService
+ {
+ this._aiServices.SetService(serviceId, instance, setAsDefault);
+ return this;
+ }
+
+ ///
+ /// Adds a factory method to the services collection
+ ///
+ /// The factory method that creates the AI service instances of type .
+ public KernelBuilder WithDefaultAIService(Func factory) where TService : IAIService
+ {
+ this._aiServices.SetService(() => factory(this._logger));
+ return this;
+ }
+
+ ///
+ /// Adds a factory method to the services collection
+ ///
+ /// The service ID
+ /// The factory method that creates the AI service instances of type .
+ /// Optional: set as the default AI service for type
+ public KernelBuilder WithAIService(
+ string? serviceId,
+ Func<(ILogger Logger, KernelConfig Config), TService> factory,
+ bool setAsDefault = false) where TService : IAIService
+ {
+ this._aiServices.SetService(serviceId, () => factory((this._logger, this._config)), setAsDefault);
+ return this;
+ }
}
diff --git a/dotnet/src/SemanticKernel/Memory/MemoryConfiguration.cs b/dotnet/src/SemanticKernel/Memory/MemoryConfiguration.cs
index f3247c5a98..caa1776709 100644
--- a/dotnet/src/SemanticKernel/Memory/MemoryConfiguration.cs
+++ b/dotnet/src/SemanticKernel/Memory/MemoryConfiguration.cs
@@ -23,7 +23,7 @@ public static class MemoryConfiguration
/// Kernel service id for embedding generation
public static void UseMemory(this IKernel kernel, IMemoryStore storage, string? embeddingsServiceId = null)
{
- var embeddingGenerator = kernel.GetService>(embeddingsServiceId);
+ var embeddingGenerator = kernel.GetService(embeddingsServiceId);
UseMemory(kernel, embeddingGenerator, storage);
}
@@ -35,7 +35,7 @@ public static class MemoryConfiguration
/// Embedding generator
/// Memory storage
[SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "The embeddingGenerator object is disposed by the kernel")]
- public static void UseMemory(this IKernel kernel, IEmbeddingGeneration embeddingGenerator, IMemoryStore storage)
+ public static void UseMemory(this IKernel kernel, ITextEmbeddingGeneration embeddingGenerator, IMemoryStore storage)
{
Verify.NotNull(storage);
Verify.NotNull(embeddingGenerator);
diff --git a/dotnet/src/SemanticKernel/Memory/SemanticTextMemory.cs b/dotnet/src/SemanticKernel/Memory/SemanticTextMemory.cs
index 7535c285be..57b94fc826 100644
--- a/dotnet/src/SemanticKernel/Memory/SemanticTextMemory.cs
+++ b/dotnet/src/SemanticKernel/Memory/SemanticTextMemory.cs
@@ -15,12 +15,12 @@ namespace Microsoft.SemanticKernel.Memory;
///
public sealed class SemanticTextMemory : ISemanticTextMemory, IDisposable
{
- private readonly IEmbeddingGeneration _embeddingGenerator;
+ private readonly ITextEmbeddingGeneration _embeddingGenerator;
private readonly IMemoryStore _storage;
public SemanticTextMemory(
IMemoryStore storage,
- IEmbeddingGeneration embeddingGenerator)
+ ITextEmbeddingGeneration embeddingGenerator)
{
this._embeddingGenerator = embeddingGenerator;
this._storage = storage;
diff --git a/dotnet/src/SemanticKernel/Services/AIServiceCollection.cs b/dotnet/src/SemanticKernel/Services/AIServiceCollection.cs
new file mode 100644
index 0000000000..dc61b4bda1
--- /dev/null
+++ b/dotnet/src/SemanticKernel/Services/AIServiceCollection.cs
@@ -0,0 +1,116 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.SemanticKernel.Services;
+
+[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1711:Identifiers should not have incorrect suffix")]
+public class AIServiceCollection
+{
+ // A constant key for the default service
+ private const string DefaultKey = "__DEFAULT__";
+
+ // A dictionary that maps a service type to a nested dictionary of names and service instances or factories
+ //private readonly Dictionary> _services = new();
+ private readonly Dictionary>> _services = new();
+
+ // A dictionary that maps a service type to the name of the default service
+ private readonly Dictionary _defaultIds = new();
+
+ ///
+ /// Registers a singleton service instance with the default name.
+ ///
+ /// The type of the service.
+ /// The service instance.
+ /// The service instance is null.
+ public void SetService(T service) where T : IAIService
+ => this.SetService(DefaultKey, service, true);
+
+ ///
+ /// Registers a singleton service instance with an optional name and default flag.
+ ///
+ /// The type of the service.
+ /// The name of the service, or null for the default service.
+ /// The service instance.
+ /// Whether the service should be the default for its type.
+ /// The service instance is null.
+ /// The name is empty or whitespace.
+ public void SetService(string? name, T service, bool setAsDefault = false) where T : IAIService
+ => this.SetService(name, (() => service), setAsDefault);
+
+ ///
+ /// Registers a transient service factory with the default name.
+ ///
+ /// The type of the service.
+ /// The factory function to create the service instance.
+ /// The factory function is null.
+ public void SetService(Func factory) where T : IAIService
+ => this.SetService(DefaultKey, factory, true);
+
+ ///
+ /// Registers a transient service factory with an optional name and default flag.
+ ///
+ /// The type of the service.
+ /// The name of the service, or null for the default service.
+ /// The factory function to create the service instance.
+ /// Whether the service should be the default for its type.
+ /// The factory function is null.
+ /// The name is empty or whitespace.
+ public void SetService(string? name, Func factory, bool setAsDefault = false) where T : IAIService
+ {
+ // Validate the factory function
+ if (factory == null)
+ {
+ throw new ArgumentNullException(nameof(factory));
+ }
+
+ // Get or create the nested dictionary for the service type
+ var type = typeof(T);
+ if (!this._services.TryGetValue(type, out var namedServices))
+ {
+ namedServices = new();
+ this._services[type] = namedServices;
+ }
+
+ // Set as the default if the name is empty, or the default flag is true,
+ // or there is no default name for the service type.
+ if (name == null || setAsDefault || !this.HasDefault())
+ {
+ // Update the default name for the service type
+ this._defaultIds[type] = name ?? DefaultKey;
+ }
+
+ var objectFactory = factory as Func