Feature serviceregistry (#993)

### Motivation, Context and Description

This PR introduces several changes aimed to simplify the kernel and
enhancing its extensibility for AI services management. The changes
include the following:

1. Kernel simplification and extensibility: The code responsible for AI
services management, such as registration and resolution, has been moved
out of the Kernel and KernelConfig classes into a new class called
AIServiceProvider. This restructuring promotes the principle of Single
Responsibility, ensuring that each class has a clear and focused
purpose.

2. Hosting apps customization: With the introduction of the
AIServiceProvider, hosting applications now have the flexibility to
provide their own implementation of the provider if needed. This
promotes extensibility, allowing the hosting apps to tailor the AI
service management according to their specific requirements.

3. AI services registration improvements: A new method called
WithAiService has been added, which enables the registration of any new
AI service that is not currently supported.

4. Improved kernel builder interface: The Add* methods for AI services
registration have been moved from the KernelConfig class to the
KernelBuilder. This change aligns the interface with other KernelBilder
methods, such as WithLogger and WithMemory, creating a more cohesive and
intuitive kernel builder interface.

---------

Co-authored-by: Shawn Callegari <[email protected]>
Co-authored-by: Lee Miller <[email protected]>
This commit is contained in:
SergeyMenshykh
2023-05-22 15:21:48 -07:00
committed by GitHub
co-authored by Shawn Callegari Lee Miller
parent c0ab238f58
commit af03e00a79
86 changed files with 2452 additions and 459 deletions
-1
View File
@@ -3,7 +3,6 @@
<!-- using Central Package Management feature -->
<!-- https://learn.microsoft.com/en-us/nuget/consume-packages/Central-Package-Management -->
<Sdk Name="Microsoft.Build.CentralPackageVersions" Version="2.1.3" />
<!-- Only run 'dotnet format' on dev machines, Release builds. Skip on GitHub Actions -->
<!-- as this runs in its own Actions job. -->
<Target Name="DotnetFormatOnBuild" BeforeTargets="Build"
@@ -16,7 +16,7 @@ namespace Microsoft.SemanticKernel.Connectors.AI.HuggingFace.TextEmbedding;
/// <summary>
/// HuggingFace embedding generation service.
/// </summary>
public sealed class HuggingFaceTextEmbeddingGeneration : IEmbeddingGeneration<string, float>, IDisposable
public sealed class HuggingFaceTextEmbeddingGeneration : ITextEmbeddingGeneration, IDisposable
{
private const string HttpUserAgent = "Microsoft Semantic Kernel";
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- THIS PROPERTY GROUP MUST COME FIRST -->
@@ -20,7 +20,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\SemanticKernel.Abstractions\SemanticKernel.Abstractions.csproj" />
<ProjectReference Include="..\..\SemanticKernel\SemanticKernel.csproj" />
</ItemGroup>
<ItemGroup>
@@ -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
/// <param name="httpClient">Custom <see cref="HttpClient"/> for HTTP requests.</param>
/// <param name="logger">Application logger</param>
/// <returns>Self instance</returns>
[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
/// <param name="httpClient">Custom <see cref="HttpClient"/> for HTTP requests.</param>
/// <param name="logger">Application logger</param>
/// <returns>Self instance</returns>
[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
/// <param name="httpClient">Custom <see cref="HttpClient"/> for HTTP requests.</param>
/// <param name="logger">Application logger</param>
/// <returns>Self instance</returns>
[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
/// <param name="httpClient">Custom <see cref="HttpClient"/> for HTTP requests.</param>
/// <param name="logger">Application logger</param>
/// <returns>Self instance</returns>
[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
/// <param name="httpClient">Custom <see cref="HttpClient"/> for HTTP requests.</param>
/// <param name="logger">Application logger</param>
/// <returns>Self instance</returns>
[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
/// <param name="httpClient">Custom <see cref="HttpClient"/> for HTTP requests.</param>
/// <param name="logger">Application logger</param>
/// <returns>Self instance</returns>
[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
/// <param name="httpClient">Custom <see cref="HttpClient"/> for HTTP requests.</param>
/// <param name="logger">Application logger</param>
/// <returns>Self instance</returns>
[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
/// <param name="httpClient">Custom <see cref="HttpClient"/> for HTTP requests.</param>
/// <param name="logger">Application logger</param>
/// <returns>Self instance</returns>
[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
/// <param name="httpClient">Custom <see cref="HttpClient"/> for HTTP requests.</param>
/// <param name="logger">Application logger</param>
/// <returns>Self instance</returns>
[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
/// <param name="httpClient">Custom <see cref="HttpClient"/> for HTTP requests.</param>
/// <param name="logger">Application logger</param>
/// <returns>Self instance</returns>
[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,
@@ -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
/// <summary>
/// Adds an Azure OpenAI text completion service to the list.
/// See https://learn.microsoft.com/azure/cognitive-services/openai for service details.
/// </summary>
/// <param name="builder">The <see cref="KernelBuilder"/> instance</param>
/// <param name="deploymentName">Azure OpenAI deployment name, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource</param>
/// <param name="endpoint">Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart</param>
/// <param name="apiKey">Azure OpenAI API key, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart</param>
/// <param name="serviceId">A local identifier for the given AI service</param>
/// <param name="setAsDefault">Whether the service should be the default for its type.</param>
/// <param name="httpClient">Custom <see cref="HttpClient"/> for HTTP requests.</param>
/// <returns>Self instance</returns>
public static KernelBuilder WithAzureTextCompletionService(this KernelBuilder builder,
string deploymentName,
string endpoint,
string apiKey,
string? serviceId = null,
bool setAsDefault = false,
HttpClient? httpClient = null)
{
builder.WithAIService<ITextCompletion>(serviceId, (parameters) =>
new AzureTextCompletion(
deploymentName,
endpoint,
apiKey,
httpClient ?? parameters.Config.HttpHandlerFactory.CreateHttpClient(parameters.Logger),
parameters.Logger),
setAsDefault);
return builder;
}
/// <summary>
/// Adds an Azure OpenAI text completion service to the list.
/// See https://learn.microsoft.com/azure/cognitive-services/openai for service details.
/// </summary>
/// <param name="builder">The <see cref="KernelBuilder"/> instance</param>
/// <param name="deploymentName">Azure OpenAI deployment name, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource</param>
/// <param name="endpoint">Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart</param>
/// <param name="credentials">Token credentials, e.g. DefaultAzureCredential, ManagedIdentityCredential, EnvironmentCredential, etc.</param>
/// <param name="serviceId">A local identifier for the given AI service</param>
/// <param name="setAsDefault">Whether the service should be the default for its type.</param>
/// <param name="httpClient">Custom <see cref="HttpClient"/> for HTTP requests.</param>
/// <returns>Self instance</returns>
public static KernelBuilder WithAzureTextCompletionService(this KernelBuilder builder,
string deploymentName,
string endpoint,
TokenCredential credentials,
string? serviceId = null,
bool setAsDefault = false,
HttpClient? httpClient = null)
{
builder.WithAIService<ITextCompletion>(serviceId, (parameters) =>
new AzureTextCompletion(
deploymentName,
endpoint,
credentials,
httpClient ?? parameters.Config.HttpHandlerFactory.CreateHttpClient(parameters.Logger),
parameters.Logger),
setAsDefault);
return builder;
}
/// <summary>
/// Adds the OpenAI text completion service to the list.
/// See https://platform.openai.com/docs for service details.
/// </summary>
/// <param name="builder">The <see cref="KernelBuilder"/> instance</param>
/// <param name="modelId">OpenAI model name, see https://platform.openai.com/docs/models</param>
/// <param name="apiKey">OpenAI API key, see https://platform.openai.com/account/api-keys</param>
/// <param name="orgId">OpenAI organization id. This is usually optional unless your account belongs to multiple organizations.</param>
/// <param name="serviceId">A local identifier for the given AI service</param>
/// <param name="setAsDefault">Whether the service should be the default for its type.</param>
/// <param name="httpClient">Custom <see cref="HttpClient"/> for HTTP requests.</param>
/// <returns>Self instance</returns>
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<ITextCompletion>(serviceId, (parameters) =>
new OpenAITextCompletion(
modelId,
apiKey,
orgId,
httpClient ?? parameters.Config.HttpHandlerFactory.CreateHttpClient(parameters.Logger),
parameters.Logger),
setAsDefault);
return builder;
}
#endregion
#region Text Embedding
/// <summary>
/// Adds an Azure OpenAI text embeddings service to the list.
/// See https://learn.microsoft.com/azure/cognitive-services/openai for service details.
/// </summary>
/// <param name="builder">The <see cref="KernelBuilder"/> instance</param>
/// <param name="deploymentName">Azure OpenAI deployment name, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource</param>
/// <param name="endpoint">Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart</param>
/// <param name="apiKey">Azure OpenAI API key, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart</param>
/// <param name="serviceId">A local identifier for the given AI service</param>
/// <param name="setAsDefault">Whether the service should be the default for its type.</param>
/// <param name="httpClient">Custom <see cref="HttpClient"/> for HTTP requests.</param>
/// <returns>Self instance</returns>
public static KernelBuilder WithAzureTextEmbeddingGenerationService(this KernelBuilder builder,
string deploymentName,
string endpoint,
string apiKey,
string? serviceId = null,
bool setAsDefault = false,
HttpClient? httpClient = null)
{
builder.WithAIService<ITextEmbeddingGeneration>(serviceId, (parameters) =>
new AzureTextEmbeddingGeneration(
deploymentName,
endpoint,
apiKey,
httpClient ?? parameters.Config.HttpHandlerFactory.CreateHttpClient(parameters.Logger),
parameters.Logger),
setAsDefault);
return builder;
}
/// <summary>
/// Adds an Azure OpenAI text embeddings service to the list.
/// See https://learn.microsoft.com/azure/cognitive-services/openai for service details.
/// </summary>
/// <param name="builder">The <see cref="KernelBuilder"/> instance</param>
/// <param name="deploymentName">Azure OpenAI deployment name, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource</param>
/// <param name="endpoint">Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart</param>
/// <param name="credential">Token credentials, e.g. DefaultAzureCredential, ManagedIdentityCredential, EnvironmentCredential, etc.</param>
/// <param name="serviceId">A local identifier for the given AI service</param>
/// <param name="setAsDefault">Whether the service should be the default for its type.</param>
/// <param name="httpClient">Custom <see cref="HttpClient"/> for HTTP requests.</param>
/// <returns>Self instance</returns>
public static KernelBuilder WithAzureTextEmbeddingGenerationService(this KernelBuilder builder,
string deploymentName,
string endpoint,
TokenCredential credential,
string? serviceId = null,
bool setAsDefault = false,
HttpClient? httpClient = null)
{
builder.WithAIService<ITextEmbeddingGeneration>(serviceId, (parameters) =>
new AzureTextEmbeddingGeneration(
deploymentName,
endpoint,
credential,
httpClient ?? parameters.Config.HttpHandlerFactory.CreateHttpClient(parameters.Logger),
parameters.Logger),
setAsDefault);
return builder;
}
/// <summary>
/// Adds the OpenAI text embeddings service to the list.
/// See https://platform.openai.com/docs for service details.
/// </summary>
/// <param name="builder">The <see cref="KernelBuilder"/> instance</param>
/// <param name="modelId">OpenAI model name, see https://platform.openai.com/docs/models</param>
/// <param name="apiKey">OpenAI API key, see https://platform.openai.com/account/api-keys</param>
/// <param name="orgId">OpenAI organization id. This is usually optional unless your account belongs to multiple organizations.</param>
/// <param name="serviceId">A local identifier for the given AI service</param>
/// <param name="setAsDefault">Whether the service should be the default for its type.</param>
/// <param name="httpClient">Custom <see cref="HttpClient"/> for HTTP requests.</param>
/// <returns>Self instance</returns>
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<ITextEmbeddingGeneration>(serviceId, (parameters) =>
new OpenAITextEmbeddingGeneration(
modelId,
apiKey,
orgId,
httpClient ?? parameters.Config.HttpHandlerFactory.CreateHttpClient(parameters.Logger),
parameters.Logger),
setAsDefault);
return builder;
}
#endregion
#region Chat Completion
/// <summary>
/// Adds the Azure OpenAI ChatGPT completion service to the list.
/// See https://platform.openai.com/docs for service details.
/// </summary>
/// <param name="builder">The <see cref="KernelBuilder"/> instance</param>
/// <param name="deploymentName">Azure OpenAI deployment name, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource</param>
/// <param name="endpoint">Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart</param>
/// <param name="apiKey">Azure OpenAI API key, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart</param>
/// <param name="alsoAsTextCompletion">Whether to use the service also for text completion, if supported</param>
/// <param name="serviceId">A local identifier for the given AI service</param>
/// <param name="setAsDefault">Whether the service should be the default for its type.</param>
/// <param name="httpClient">Custom <see cref="HttpClient"/> for HTTP requests.</param>
/// <returns>Self instance</returns>
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<IChatCompletion>(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<ITextCompletion>(serviceId, Factory, setAsDefault);
}
return builder;
}
/// <summary>
/// Adds the Azure OpenAI ChatGPT completion service to the list.
/// See https://platform.openai.com/docs for service details.
/// </summary>
/// <param name="builder">The <see cref="KernelBuilder"/> instance</param>
/// <param name="deploymentName">Azure OpenAI deployment name, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource</param>
/// <param name="endpoint">Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart</param>
/// <param name="credentials">Token credentials, e.g. DefaultAzureCredential, ManagedIdentityCredential, EnvironmentCredential, etc.</param>
/// <param name="alsoAsTextCompletion">Whether to use the service also for text completion, if supported</param>
/// <param name="serviceId">A local identifier for the given AI service</param>
/// <param name="setAsDefault">Whether the service should be the default for its type.</param>
/// <param name="httpClient">Custom <see cref="HttpClient"/> for HTTP requests.</param>
/// <returns>Self instance</returns>
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<IChatCompletion>(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<ITextCompletion>(serviceId, Factory, setAsDefault);
}
return builder;
}
/// <summary>
/// Adds the OpenAI ChatGPT completion service to the list.
/// See https://platform.openai.com/docs for service details.
/// </summary>
/// <param name="builder">The <see cref="KernelBuilder"/> instance</param>
/// <param name="modelId">OpenAI model name, see https://platform.openai.com/docs/models</param>
/// <param name="apiKey">OpenAI API key, see https://platform.openai.com/account/api-keys</param>
/// <param name="orgId">OpenAI organization id. This is usually optional unless your account belongs to multiple organizations.</param>
/// <param name="serviceId">A local identifier for the given AI service</param>
/// <param name="alsoAsTextCompletion">Whether to use the service also for text completion, if supported</param>
/// <param name="setAsDefault">Whether the service should be the default for its type.</param>
/// <param name="httpClient">Custom <see cref="HttpClient"/> for HTTP requests.</param>
/// <returns>Self instance</returns>
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<IChatCompletion>(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<ITextCompletion>(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
/// <summary>
/// Add the OpenAI DallE image generation service to the list
/// </summary>
/// <param name="builder">The <see cref="KernelBuilder"/> instance</param>
/// <param name="apiKey">OpenAI API key, see https://platform.openai.com/account/api-keys</param>
/// <param name="orgId">OpenAI organization id. This is usually optional unless your account belongs to multiple organizations.</param>
/// <param name="serviceId">A local identifier for the given AI service</param>
/// <param name="setAsDefault">Whether the service should be the default for its type.</param>
/// <param name="httpClient">Custom <see cref="HttpClient"/> for HTTP requests.</param>
/// <returns>Self instance</returns>
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
}
@@ -14,7 +14,7 @@ namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextEmbedding;
/// <summary>
/// Azure OpenAI text embedding service.
/// </summary>
public sealed class AzureTextEmbeddingGeneration : AzureOpenAIClientBase, IEmbeddingGeneration<string, float>
public sealed class AzureTextEmbeddingGeneration : AzureOpenAIClientBase, ITextEmbeddingGeneration
{
/// <summary>
/// Creates a new AzureTextCompletion client instance using API Key auth
@@ -13,7 +13,7 @@ namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextEmbedding;
/// <summary>
/// OpenAI text embedding service.
/// </summary>
public sealed class OpenAITextEmbeddingGeneration : OpenAIClientBase, IEmbeddingGeneration<string, float>
public sealed class OpenAITextEmbeddingGeneration : OpenAIClientBase, ITextEmbeddingGeneration
{
/// <summary>
/// Create an instance of the OpenAI text embedding connector
@@ -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;
/// <summary>
/// Unit tests of <see cref="OpenAIKernelBuilderExtensions"/>.
/// </summary>
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<ITextCompletion>("azure"));
Assert.NotNull(targetKernel.GetService<ITextEmbeddingGeneration>("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<ITextCompletion>("azure"));
Assert.NotNull(targetKernel.GetService<ITextCompletion>("oai"));
Assert.NotNull(targetKernel.GetService<ITextEmbeddingGeneration>("azure"));
Assert.NotNull(targetKernel.GetService<ITextCompletion>("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<ITextCompletion>("one", new OpenAITextCompletion("model", "key"));
targetBuilder.WithAIService<ITextCompletion>("one", new OpenAITextCompletion("model", "key"));
targetBuilder.WithAIService<ITextCompletion>("one", (_) => new OpenAITextCompletion("model", "key"));
targetBuilder.WithAIService<ITextCompletion>("one", (_) => new OpenAITextCompletion("model", "key"));
}
}
@@ -8,6 +8,7 @@ namespace SemanticKernel.Connectors.UnitTests.OpenAI;
/// <summary>
/// Unit tests of <see cref="KernelConfigOpenAIExtensions"/>.
/// </summary>
[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]
@@ -36,9 +36,6 @@ public sealed class OpenAICompletionTests : IDisposable
.AddEnvironmentVariables()
.AddUserSecrets<OpenAICompletionTests>()
.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<OpenAIConfiguration>();
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<string, ISKFunction> 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<string, ISKFunction> 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<AzureOpenAIConfiguration>();
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<string, ISKFunction> 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<OpenAIConfiguration>();
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<string, ISKFunction> 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<AzureOpenAIConfiguration>();
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<string, ISKFunction> 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<OpenAIConfiguration>();
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<string, ISKFunction> 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<AzureOpenAIConfiguration>();
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<string, ISKFunction> 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<AzureOpenAIConfiguration>();
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<string, ISKFunction> 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<OpenAIConfiguration>();
@@ -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<OpenAIConfiguration>();
@@ -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<AzureOpenAIConfiguration>();
@@ -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<AzureOpenAIConfiguration>();
@@ -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
@@ -478,37 +478,31 @@ public sealed class PlanTests : IDisposable
AzureOpenAIConfiguration? azureOpenAIEmbeddingsConfiguration = this._configuration.GetSection("AzureOpenAIEmbeddings").Get<AzureOpenAIConfiguration>();
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();
@@ -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");
@@ -109,37 +109,30 @@ public sealed class SequentialPlannerTests : IDisposable
AzureOpenAIConfiguration? azureOpenAIEmbeddingsConfiguration = this._configuration.GetSection("AzureOpenAIEmbeddings").Get<AzureOpenAIConfiguration>();
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();
@@ -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
{
/// <summary>
/// Get the <see cref="IChatCompletion"/> matching the given <paramref name="serviceId"/>, or
/// the default if <paramref name="serviceId"/> is not provided or not found.
/// </summary>
/// <param name="services">The service provider.</param>
/// <param name="serviceId">Optional identifier of the desired service.</param>
/// <returns>The completion service id matching the given id or the default.</returns>
/// <exception cref="KernelException">Thrown when no suitable service is found.</exception>
public static IChatCompletion GetChatCompletionService(
this IAIServiceProvider services,
string? serviceId = null) => services.GetService<IChatCompletion>(serviceId)
?? throw new KernelException(KernelException.ErrorCodes.ServiceNotFound, "Chat completion service not found");
/// <summary>
/// Returns true if a <see cref="IChatCompletion"/> exist with the specified ID.
/// </summary>
/// <param name="services">The service provider.</param>
/// <param name="serviceId">The service ID to search for. If null, it will look for a default service.</param>
/// <returns>True if the service ID is registered, false otherwise.</returns>
public static bool HasChatCompletionService(
this IAIServiceProvider services,
string? serviceId = null)
=> services.TryGetService<IChatCompletion>(serviceId, out _);
}
@@ -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
{
/// <summary>
/// Create a new empty chat instance
@@ -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;
/// <summary>
/// Provides a collection of static methods for operating on <see cref="IEmbeddingGeneration{TValue,TEmbedding}"/> objects.
/// </summary>
public static class EmbeddingGenerationExtensions
{
/// <summary>
/// Generates an embedding from the given <paramref name="value"/>.
/// </summary>
/// <typeparam name="TValue">The type from which embeddings will be generated.</typeparam>
/// <typeparam name="TEmbedding">The numeric type of the embedding data.</typeparam>
/// <param name="generator">The embedding generator.</param>
/// <param name="value">A value from which an <see cref="Embedding{TEmbedding}"/> will be generated.</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>A list of <see cref="Embedding{TEmbedding}"/> structs representing the input <paramref name="value"/>.</returns>
public static async Task<Embedding<TEmbedding>> GenerateEmbeddingAsync<TValue, TEmbedding>
(this IEmbeddingGeneration<TValue, TEmbedding> generator, TValue value, CancellationToken cancellationToken = default)
where TEmbedding : unmanaged
{
Verify.NotNull(generator);
return (await generator.GenerateEmbeddingsAsync(new[] { value }, cancellationToken).ConfigureAwait(false)).FirstOrDefault();
}
}
@@ -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<TValue, TEmbedding>
/// <returns>List of embeddings</returns>
Task<IList<Embedding<TEmbedding>>> GenerateEmbeddingsAsync(IList<TValue> data, CancellationToken cancellationToken = default);
}
/// <summary>
/// Provides a collection of static methods for operating on <see cref="IEmbeddingGeneration{TValue,TEmbedding}"/> objects.
/// </summary>
public static class EmbeddingGenerationExtensions
{
/// <summary>
/// Generates an embedding from the given <paramref name="value"/>.
/// </summary>
/// <typeparam name="TValue">The type from which embeddings will be generated.</typeparam>
/// <typeparam name="TEmbedding">The numeric type of the embedding data.</typeparam>
/// <param name="generator">The embedding generator.</param>
/// <param name="value">A value from which an <see cref="Embedding{TEmbedding}"/> will be generated.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A list of <see cref="Embedding{TEmbedding}"/> structs representing the input <paramref name="value"/>.</returns>
public static async Task<Embedding<TEmbedding>> GenerateEmbeddingAsync<TValue, TEmbedding>
(this IEmbeddingGeneration<TValue, TEmbedding> generator, TValue value, CancellationToken cancellationToken = default)
where TEmbedding : unmanaged
{
Verify.NotNull(generator);
return (await generator.GenerateEmbeddingsAsync(new[] { value }, cancellationToken).ConfigureAwait(false)).FirstOrDefault();
}
}
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.SemanticKernel.Services;
namespace Microsoft.SemanticKernel.AI.Embeddings;
/// <summary>
/// Represents a generator of text embeddings of type <c>float</c>.
/// </summary>
public interface ITextEmbeddingGeneration : IEmbeddingGeneration<string, float>, IAIService
{
}
@@ -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
{
/// <summary>
/// Get the <see cref="ITextEmbeddingGeneration"/> matching the given <paramref name="serviceId"/>, or the default
/// if the <paramref name="serviceId"/> is not provided or not found.
/// </summary>
/// <param name="services">The service provider.</param>
/// <param name="serviceId">Optional identifier of the desired service.</param>
/// <returns>The embedding service matching the given id or the default service.</returns>
/// <exception cref="KernelException">Thrown when no suitable service is found.</exception>
public static ITextEmbeddingGeneration GetTextEmbeddingService(
this IAIServiceProvider services,
string? serviceId = null)
=> services.GetService<ITextEmbeddingGeneration>(serviceId)
?? throw new KernelException(KernelException.ErrorCodes.ServiceNotFound, "Text embedding service not available");
/// <summary>
/// Returns true if a <see cref="ITextEmbeddingGeneration"/> exist with the specified ID.
/// </summary>
/// <param name="services">The service provider.</param>
/// <param name="serviceId">The service ID to search for. If null, it will look for a default service.</param>
/// <returns>True if the service ID is registered, false otherwise.</returns>
public static bool HasTextEmbeddingService(
this IAIServiceProvider services,
string? serviceId = null)
=> services.TryGetService<ITextEmbeddingGeneration>(serviceId, out _);
}
@@ -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
{
/// <summary>
/// Generate an image matching the given description
@@ -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
{
/// <summary>
/// Get the <see cref="IImageGeneration"/> matching the given <paramref name="serviceId"/>, or the default
/// if the <paramref name="serviceId"/> is not provided or not found.
/// </summary>
/// <param name="services">The service provider.</param>
/// <param name="serviceId">Optional identifier of the desired service.</param>
/// <returns>The <see cref="IImageGeneration"/> id matching the given id or the default.</returns>
/// <exception cref="KernelException">Thrown when no suitable service is found.</exception>
public static IImageGeneration GetImageGenerationService(
this IAIServiceProvider services,
string? serviceId = null) => services.GetService<IImageGeneration>(serviceId)
?? throw new KernelException(KernelException.ErrorCodes.ServiceNotFound, "Image generation service not found");
/// <summary>
/// Returns true if a <see cref="IImageGeneration"/> exist with the specified ID.
/// </summary>
/// <param name="services">The service provider.</param>
/// <param name="serviceId">The service ID to search for. If null, it will look for a default service.</param>
/// <returns>True if the service ID is registered, false otherwise.</returns>
public static bool HasImageGenerationService(
this IAIServiceProvider services,
string? serviceId = null)
=> services.TryGetService<IImageGeneration>(serviceId, out _);
}
@@ -3,13 +3,14 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SemanticKernel.Services;
namespace Microsoft.SemanticKernel.AI.TextCompletion;
/// <summary>
/// Interface for text completion services
/// </summary>
public interface ITextCompletion
public interface ITextCompletion : IAIService
{
/// <summary>
/// Get completion results for the prompt and settings.
@@ -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
{
/// <summary>
/// Get the <see cref="ITextCompletion"/> matching the given <paramref name="serviceId"/>, or the default
/// if the <paramref name="serviceId"/> is not provided or not found.
/// </summary>
/// <param name="services">The service provider.</param>
/// <param name="serviceId">Optional identifier of the desired service.</param>
/// <returns>The text completion service id matching the given ID or the default.</returns>
/// <exception cref="KernelException">Thrown when no suitable service is found.</exception>
public static ITextCompletion GetTextCompletionServiceOrDefault(
this IAIServiceProvider services,
string? serviceId = null) => services.GetService<ITextCompletion>()
?? throw new KernelException(KernelException.ErrorCodes.ServiceNotFound, "Text completion service not found");
/// <summary>
/// Returns true if a <see cref="ITextCompletion"/> exist with the specified ID.
/// </summary>
/// <param name="services">The service provider.</param>
/// <param name="serviceId">The service ID to search for. If null, it will look for a default service.</param>
/// <returns>True if the service ID is registered, false otherwise.</returns>
public static bool HasTextCompletionService(
this IAIServiceProvider services,
string? serviceId = null)
=> services.TryGetService<ITextCompletion>(serviceId, out _);
}
@@ -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
/// <param name="name">Optional name. If the name is not provided, returns the default T available</param>
/// <typeparam name="T">Service type</typeparam>
/// <returns>Instance of T</returns>
T GetService<T>(string? name = null);
T GetService<T>(string? name = null) where T : IAIService;
}
@@ -29,21 +29,25 @@ public sealed class KernelConfig
/// <summary>
/// Text completion service factories
/// </summary>
[Obsolete("This property is deprecated and will be removed in one of the next SK SDK versions.")]
public Dictionary<string, Func<IKernel, ITextCompletion>> TextCompletionServices { get; } = new();
/// <summary>
/// Chat completion service factories
/// </summary>
[Obsolete("This property is deprecated and will be removed in one of the next SK SDK versions.")]
public Dictionary<string, Func<IKernel, IChatCompletion>> ChatCompletionServices { get; } = new();
/// <summary>
/// Text embedding generation service factories
/// </summary>
[Obsolete("This property is deprecated and will be removed in one of the next SK SDK versions.")]
public Dictionary<string, Func<IKernel, IEmbeddingGeneration<string, float>>> TextEmbeddingGenerationServices { get; } = new();
/// <summary>
/// Image generation service factories
/// </summary>
[Obsolete("This property is deprecated and will be removed in one of the next SK SDK versions.")]
public Dictionary<string, Func<IKernel, IImageGeneration>> ImageGenerationServices { get; } = new();
/// <summary>
@@ -58,6 +62,7 @@ public sealed class KernelConfig
/// <param name="serviceId">Id used to identify the service</param>
/// <returns>Current object instance</returns>
/// <exception cref="KernelException">Failure if a service with the same id already exists</exception>
[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<IKernel, ITextCompletion> serviceFactory,
string? serviceId = null)
@@ -87,6 +92,7 @@ public sealed class KernelConfig
/// <param name="serviceId">Id used to identify the service</param>
/// <returns>Current object instance</returns>
/// <exception cref="KernelException">Failure if a service with the same id already exists</exception>
[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<IKernel, IChatCompletion> serviceFactory,
string? serviceId = null)
@@ -116,6 +122,7 @@ public sealed class KernelConfig
/// <param name="serviceId">Id used to identify the service</param>
/// <returns>Current object instance</returns>
/// <exception cref="KernelException">Failure if a service with the same id already exists</exception>
[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<IKernel, IEmbeddingGeneration<string, float>> serviceFactory,
string? serviceId = null)
@@ -145,6 +152,7 @@ public sealed class KernelConfig
/// <param name="serviceId">Id used to identify the service</param>
/// <returns>Current object instance</returns>
/// <exception cref="KernelException">Failure if a service with the same id already exists</exception>
[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<IKernel, IImageGeneration> serviceFactory,
string? serviceId = null)
@@ -201,6 +209,7 @@ public sealed class KernelConfig
/// <param name="serviceId">Identifier of completion service to use.</param>
/// <returns>The updated kernel configuration.</returns>
/// <exception cref="KernelException">Thrown if the requested service doesn't exist.</exception>
[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
/// <param name="serviceId">Identifier of text embedding service to use.</param>
/// <returns>The updated kernel configuration.</returns>
/// <exception cref="KernelException">Thrown if the requested service doesn't exist.</exception>
[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.
/// </summary>
/// <returns>The updated kernel configuration.</returns>
[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.
/// </summary>
/// <returns>The updated kernel configuration.</returns>
[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.
/// </summary>
/// <returns>The updated kernel configuration.</returns>
[Obsolete("This method is deprecated and will be removed in one of the next SK SDK versions.")]
public KernelConfig RemoveAllTextEmbeddingGenerationServices()
{
this.TextEmbeddingGenerationServices.Clear();
@@ -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
{
}
@@ -0,0 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
namespace Microsoft.SemanticKernel.Services;
public interface IAIServiceProvider : INamedServiceProvider<IAIService>
{
}
@@ -0,0 +1,14 @@
// Copyright (c) Microsoft. All rights reserved.
namespace Microsoft.SemanticKernel.Services;
public interface INamedServiceProvider<TService>
{
/// <summary>
/// Gets the service of the specified type and name, or null if not found.
/// </summary>
/// <typeparam name="T">The type of the service.</typeparam>
/// <param name="name">The name of the service, or null for the default service.</param>
/// <returns>The service instance, or null if not found.</returns>
T? GetService<T>(string? name = null) where T : TService;
}
@@ -0,0 +1,36 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Diagnostics.CodeAnalysis;
namespace Microsoft.SemanticKernel.Services;
internal static class AIServiceProviderExtensions
{
/// <summary>
/// Tries to get the service of the specified type and name, and returns a value indicating whether the operation succeeded.
/// </summary>
/// <typeparam name="T">The type of the service.</typeparam>
/// <param name="serviceProvider">The service provider.</param>
/// <param name="service">The output parameter to receive the service instance, or null if not found.</param>
/// <returns>True if the service was found, false otherwise.</returns>
public static bool TryGetService<T>(this IAIServiceProvider serviceProvider,
[NotNullWhen(true)] out T? service) where T : IAIService
{
service = serviceProvider.GetService<T>();
return service != null;
}
/// <summary>
/// Tries to get the service of the specified type and name, and returns a value indicating whether the operation succeeded.
/// </summary>
/// <typeparam name="T">The type of the service.</typeparam>
/// <param name="serviceProvider">The service provider.</param>
/// <param name="name">The name of the service, or null for the default service.</param>
/// <param name="service">The output parameter to receive the service instance, or null if not found.</param>
/// <returns>True if the service was found, false otherwise.</returns>
public static bool TryGetService<T>(this IAIServiceProvider serviceProvider,
string? name, [NotNullWhen(true)] out T? service) where T : IAIService
{
service = serviceProvider.GetService<T>(name);
return service != null;
}
}
@@ -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;
/// <summary>
/// Unit tests of <see cref="ChatCompletionServiceExtensions"/>.
/// </summary>
public class ChatCompletionServiceExtensionsTests
{
[Fact]
public void ItCanAddChatCompletionServiceInstance()
{
// Arrange
var services = new AIServiceCollection();
var serviceId = "test";
var instance = Mock.Of<IChatCompletion>();
// Act
services.SetService<IChatCompletion>(serviceId, instance);
var provider = services.Build();
// Assert
Assert.True(provider.TryGetService<IChatCompletion>(serviceId, out var instanceRetrieved));
Assert.Same(instance, instanceRetrieved);
}
[Fact]
public void ItCanAddChatCompletionServiceFactory()
{
// Arrange
var services = new AIServiceCollection();
var serviceId = "test";
var instance = Mock.Of<IChatCompletion>();
var factory = new Func<IChatCompletion>(() => instance);
// Act
services.SetService<IChatCompletion>(serviceId, factory);
var provider = services.Build();
// Assert
Assert.True(provider.TryGetService<IChatCompletion>(serviceId, out _));
}
[Fact]
public void ItCanSetDefaultChatCompletionService()
{
// Arrange
var services = new AIServiceCollection();
var serviceId1 = "test1";
var serviceId2 = "test2";
var instance1 = Mock.Of<IChatCompletion>();
var instance2 = Mock.Of<IChatCompletion>();
services.SetService<IChatCompletion>(serviceId1, instance1);
// Act
services.SetService<IChatCompletion>(serviceId2, instance2, true);
var provider = services.Build();
// Assert
Assert.True(provider.TryGetService<IChatCompletion>(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<IChatCompletion>(out var instanceRetrieved));
}
[Fact]
public void ItReturnsTrueIfHasChatCompletionServiceWithValidId()
{
// Arrange
var services = new AIServiceCollection();
var serviceId = "test";
var instance = Mock.Of<IChatCompletion>();
services.SetService<IChatCompletion>(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<IChatCompletion>();
services.SetService<IChatCompletion>(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<IChatCompletion>();
services.SetService<IChatCompletion>(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);
}
}
@@ -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;
/// <summary>
/// Unit tests of <see cref="TextEmbeddingServiceExtensions"/>.
/// </summary>
public class TextEmbeddingServiceExtensionsTests
{
[Fact]
public void ItCanAddTextEmbeddingServiceInstance()
{
// Arrange
var services = new AIServiceCollection();
var serviceId = "test";
var instance = Mock.Of<ITextEmbeddingGeneration>();
// Act
services.SetService<ITextEmbeddingGeneration>(serviceId, instance);
var provider = services.Build();
// Assert
Assert.True(provider.TryGetService<ITextEmbeddingGeneration>(serviceId, out var instanceRetrieved));
Assert.Same(instance, instanceRetrieved);
}
[Fact]
public void ItCanAddTextEmbeddingServiceFactory()
{
// Arrange
var services = new AIServiceCollection();
var serviceId = "test";
var instance = Mock.Of<ITextEmbeddingGeneration>();
var factory = new Func<ITextEmbeddingGeneration>(() => instance);
// Act
services.SetService<ITextEmbeddingGeneration>(serviceId, factory);
var provider = services.Build();
// Assert
Assert.True(provider.TryGetService<ITextEmbeddingGeneration>(serviceId, out _));
}
[Fact]
public void ItCanSetDefaultTextEmbeddingService()
{
// Arrange
var services = new AIServiceCollection();
var serviceId1 = "test1";
var serviceId2 = "test2";
var instance1 = Mock.Of<ITextEmbeddingGeneration>();
var instance2 = Mock.Of<ITextEmbeddingGeneration>();
services.SetService<ITextEmbeddingGeneration>(serviceId1, instance1);
// Act
services.SetService<ITextEmbeddingGeneration>(serviceId2, instance2, setAsDefault: true);
var provider = services.Build();
// Assert
Assert.True(provider.TryGetService<ITextEmbeddingGeneration>(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<ITextEmbeddingGeneration>(out var instanceRetrieved));
}
[Fact]
public void ItReturnsTrueIfHasTextEmbeddingServiceWithValidId()
{
// Arrange
var services = new AIServiceCollection();
var serviceId = "test";
var instance = Mock.Of<ITextEmbeddingGeneration>();
services.SetService<ITextEmbeddingGeneration>(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<ITextEmbeddingGeneration>();
services.SetService<ITextEmbeddingGeneration>(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<ITextEmbeddingGeneration>();
services.SetService<ITextEmbeddingGeneration>(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);
}
}
@@ -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;
/// <summary>
/// Unit tests of <see cref="ImageGenerationServiceExtensions"/>.
/// </summary>
public class ImageGenerationServiceExtensionsTests
{
[Fact]
public void ItCanSetServiceImageGenerationInstance()
{
// Arrange
var services = new AIServiceCollection();
var serviceId = "test";
var instance = Mock.Of<IImageGeneration>();
// Act
services.SetService<IImageGeneration>(serviceId, instance);
var provider = services.Build();
// Assert
Assert.True(provider.TryGetService<IImageGeneration>(serviceId, out var instanceRetrieved));
Assert.Same(instance, instanceRetrieved);
}
[Fact]
public void ItCanSetServiceImageGenerationFactory()
{
// Arrange
var services = new AIServiceCollection();
var serviceId = "test";
var instance = Mock.Of<IImageGeneration>();
var factory = new Func<IImageGeneration>(() => instance);
// Act
services.SetService<IImageGeneration>(serviceId, factory);
var provider = services.Build();
// Assert
Assert.True(provider.TryGetService<IImageGeneration>(out var instanceRetrieved));
}
[Fact]
public void ItCanSetDefaultImageGenerationService()
{
// Arrange
var services = new AIServiceCollection();
var serviceId1 = "test1";
var serviceId2 = "test2";
var instance1 = Mock.Of<IImageGeneration>();
var instance2 = Mock.Of<IImageGeneration>();
services.SetService<IImageGeneration>(serviceId1, instance1);
// Act
services.SetService<IImageGeneration>(serviceId2, instance2, setAsDefault: true);
var provider = services.Build();
// Assert
Assert.True(provider.TryGetService<IImageGeneration>(out var instanceRetrieved));
Assert.Same(instance2, instanceRetrieved);
}
[Fact]
public void ItReturnsFalseIfNoDefaultImageGenerationServiceIsSet()
{
// Arrange
var services = new AIServiceCollection();
var provider = services.Build();
Assert.False(provider.TryGetService<IImageGeneration>(out var instanceRetrieved));
}
[Fact]
public void ItReturnsTrueIfHasImageGenerationServiceWithValidId()
{
// Arrange
var services = new AIServiceCollection();
var serviceId = "test";
var instance = Mock.Of<IImageGeneration>();
services.SetService<IImageGeneration>(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<IImageGeneration>();
services.SetService<IImageGeneration>(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<IImageGeneration>();
services.SetService<IImageGeneration>(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);
}
}
@@ -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;
/// <summary>
/// Unit tests of <see cref="TextCompletionServiceExtensions"/>.
/// </summary>
public class TextCompletionServiceExtensionsTests
{
[Fact]
public void ItCanAddTextCompletionServiceInstance()
{
// Arrange
var services = new AIServiceCollection();
var serviceId = "test";
var instance = Mock.Of<ITextCompletion>();
// Act
services.SetService<ITextCompletion>(serviceId, instance);
var provider = services.Build();
// Assert
Assert.True(provider.TryGetService<ITextCompletion>(serviceId, out var instanceRetrieved));
Assert.Same(instance, instanceRetrieved);
}
[Fact]
public void ItCanAddTextCompletionServiceFactory()
{
// Arrange
var services = new AIServiceCollection();
var serviceId = "test";
var instance = Mock.Of<ITextCompletion>();
var factory = new Func<ITextCompletion>(() => instance);
// Act
services.SetService<ITextCompletion>(serviceId, factory);
var provider = services.Build();
// Assert
Assert.True(provider.TryGetService<ITextCompletion>(serviceId, out var _));
}
[Fact]
public void ItCanSetDefaultTextCompletionService()
{
// Arrange
var services = new AIServiceCollection();
var serviceId1 = "test1";
var serviceId2 = "test2";
var instance1 = Mock.Of<ITextCompletion>();
var instance2 = Mock.Of<ITextCompletion>();
services.SetService<ITextCompletion>(serviceId1, instance1);
// Act
services.SetService<ITextCompletion>(serviceId2, instance2, setAsDefault: true);
var provider = services.Build();
// Assert
Assert.True(provider.TryGetService<ITextCompletion>(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<ITextCompletion>(out var _));
}
[Fact]
public void ItReturnsTrueIfHasTextCompletionServiceWithValidId()
{
// Arrange
var services = new AIServiceCollection();
var serviceId = "test";
var instance = Mock.Of<ITextCompletion>();
services.SetService<ITextCompletion>(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<ITextCompletion>();
services.SetService<ITextCompletion>(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<ITextCompletion>();
services.SetService<ITextCompletion>(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);
}
}
@@ -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");
@@ -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");
@@ -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");
@@ -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");
@@ -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");
@@ -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();
@@ -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<Func<ILogger, ITextCompletion>>();
var kernel = Kernel.Builder
.WithDefaultAIService<ITextCompletion>(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<Func<(ILogger, KernelConfig), ITextCompletion>>();
var kernel = Kernel.Builder
.WithAIService<ITextCompletion>("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<string, ISKFunction> skill = KernelBuilder.Create().ImportSkill(new MySkill(), "test");
IDictionary<string, ISKFunction> 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<string, ISKFunction> 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<KernelException>(
() => kernel.CreateSemanticFunction(promptTemplate: "Tell me a joke", functionName: "joker", skillName: "jk", description: "Nice fun"));
}
public class MySkill
{
[SKFunction("Return any value.")]
@@ -27,6 +27,7 @@
<ItemGroup>
<ProjectReference Include="..\Connectors\Connectors.AI.OpenAI\Connectors.AI.OpenAI.csproj" />
<ProjectReference Include="..\SemanticKernel.Abstractions\SemanticKernel.Abstractions.csproj" />
<ProjectReference Include="..\SemanticKernel\SemanticKernel.csproj" />
</ItemGroup>
@@ -0,0 +1,219 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.SemanticKernel.Services;
using Xunit;
namespace SemanticKernel.UnitTests.Services;
/// <summary>
/// Unit tests of <see cref="AIServiceCollection"/>.
/// </summary>
public class ServiceRegistryTests
{
[Fact]
public void ItCanSetAndRetrieveServiceInstance()
{
// Arrange
var services = new AIServiceCollection();
var service = new TestService();
// Act
services.SetService<IAIService>(service);
var provider = services.Build();
var result = provider.GetService<IAIService>();
// 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<IAIService>("foo", service1);
services.SetService<IAIService>("bar", service2);
var provider = services.Build();
// Assert
Assert.Same(service1, provider.GetService<IAIService>("foo"));
Assert.Same(service2, provider.GetService<IAIService>("bar"));
}
[Fact]
public void ItCanSetAndRetrieveServiceFactory()
{
// Arrange
var services = new AIServiceCollection();
var service = new TestService();
// Act
services.SetService<IAIService>(() => service);
var provider = services.Build();
// Assert
Assert.Same(service, provider.GetService<IAIService>());
}
[Fact]
public void ItCanSetAndRetrieveServiceFactoryWithName()
{
// Arrange
var services = new AIServiceCollection();
var service1 = new TestService();
var service2 = new TestService();
// Act
services.SetService<IAIService>("foo", () => service1);
services.SetService<IAIService>("bar", () => service2);
var provider = services.Build();
// Assert
Assert.Same(service1, provider.GetService<IAIService>("foo"));
Assert.Same(service2, provider.GetService<IAIService>("bar"));
}
[Fact]
public void ItCanSetAndRetrieveServiceFactoryWithServiceProvider()
{
// Arrange
var services = new AIServiceCollection();
var service = new TestService();
// Act
services.SetService<IAIService>(() => service);
var provider = services.Build();
// Assert
Assert.Same(service, provider.GetService<IAIService>());
}
[Fact]
public void ItCanSetAndRetrieveServiceFactoryWithServiceProviderAndName()
{
// Arrange
var services = new AIServiceCollection();
var service1 = new TestService();
var service2 = new TestService();
// Act
services.SetService<IAIService>("foo", () => service1);
services.SetService<IAIService>("bar", () => service2);
var provider = services.Build();
// Assert
Assert.Same(service1, provider.GetService<IAIService>("foo"));
Assert.Same(service2, provider.GetService<IAIService>("bar"));
}
[Fact]
public void ItCanSetDefaultService()
{
// Arrange
var services = new AIServiceCollection();
var service1 = new TestService();
var service2 = new TestService();
// Act
services.SetService<IAIService>("foo", service1);
services.SetService<IAIService>("bar", service2, setAsDefault: true);
var provider = services.Build();
// Assert
Assert.Same(service2, provider.GetService<IAIService>());
}
[Fact]
public void ItCanSetDefaultServiceFactory()
{
// Arrange
var services = new AIServiceCollection();
var service1 = new TestService();
var service2 = new TestService();
// Act
services.SetService<IAIService>("foo", () => service1);
services.SetService<IAIService>("bar", () => service2, setAsDefault: true);
var provider = services.Build();
// Assert
Assert.Same(service2, provider.GetService<IAIService>());
}
[Fact]
public void ItCanSetDefaultServiceFactoryWithServiceProvider()
{
// Arrange
var services = new AIServiceCollection();
var service1 = new TestService();
var service2 = new TestService();
// Act
services.SetService<IAIService>("foo", () => service1);
services.SetService<IAIService>("bar", () => service2, setAsDefault: true);
var provider = services.Build();
// Assert
Assert.Same(service2, provider.GetService<IAIService>());
}
[Fact]
public void ItCanTryGetService()
{
// Arrange
var services = new AIServiceCollection();
var service = new TestService();
services.SetService<IAIService>(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<IAIService>("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<IAIService>("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
{
}
}
+24 -12
View File
@@ -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.
/// </summary>
/// <param name="skillCollection"></param>
/// <param name="aiServiceProvider"></param>
/// <param name="promptTemplateEngine"></param>
/// <param name="memory"></param>
/// <param name="config"></param>
/// <param name="log"></param>
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
}
/// <inheritdoc/>
public T GetService<T>(string? name = null)
public T GetService<T>(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<T>(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<IKernel, ITextCompletion> 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<string, float>))
@@ -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 ?? "<NONE>"} not registered.");
}
/// <summary>
@@ -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,
+72 -6
View File
@@ -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();
/// <summary>
/// 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;
}
/// <summary>
/// Add prompt template engine to the kernel to be built.
/// </summary>
/// <param name="promptTemplateEngine">Prompt template engine to add.</param>
/// <returns>Updated kernel builder including the prompt template engine.</returns>
public KernelBuilder WithPromptTemplateEngine(IPromptTemplateEngine promptTemplateEngine)
{
Verify.NotNull(promptTemplateEngine);
this._promptTemplateEngine = promptTemplateEngine;
return this;
}
/// <summary>
/// Add memory storage and an embedding generator to the kernel to be built.
/// </summary>
@@ -105,7 +121,7 @@ public sealed class KernelBuilder
/// <param name="embeddingGenerator">Embedding generator to add.</param>
/// <returns>Updated kernel builder including the memory storage and embedding generator.</returns>
public KernelBuilder WithMemoryStorageAndTextEmbeddingGeneration(
IMemoryStore storage, IEmbeddingGeneration<string, float> 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;
}
/// <summary>
/// Adds a <typeparamref name="TService"/> instance to the services collection
/// </summary>
/// <param name="instance">The <typeparamref name="TService"/> instance.</param>
public KernelBuilder WithDefaultAIService<TService>(TService instance) where TService : IAIService
{
this._aiServices.SetService<TService>(instance);
return this;
}
/// <summary>
/// Adds a <typeparamref name="TService"/> instance to the services collection
/// </summary>
/// <param name="serviceId">The service ID</param>
/// <param name="instance">The <typeparamref name="TService"/> instance.</param>
/// <param name="setAsDefault">Optional: set as the default AI service for type <typeparamref name="TService"/></param>
public KernelBuilder WithAIService<TService>(
string? serviceId,
TService instance,
bool setAsDefault = false) where TService : IAIService
{
this._aiServices.SetService<TService>(serviceId, instance, setAsDefault);
return this;
}
/// <summary>
/// Adds a <typeparamref name="TService"/> factory method to the services collection
/// </summary>
/// <param name="factory">The factory method that creates the AI service instances of type <typeparamref name="TService"/>.</param>
public KernelBuilder WithDefaultAIService<TService>(Func<ILogger, TService> factory) where TService : IAIService
{
this._aiServices.SetService<TService>(() => factory(this._logger));
return this;
}
/// <summary>
/// Adds a <typeparamref name="TService"/> factory method to the services collection
/// </summary>
/// <param name="serviceId">The service ID</param>
/// <param name="factory">The factory method that creates the AI service instances of type <typeparamref name="TService"/>.</param>
/// <param name="setAsDefault">Optional: set as the default AI service for type <typeparamref name="TService"/></param>
public KernelBuilder WithAIService<TService>(
string? serviceId,
Func<(ILogger Logger, KernelConfig Config), TService> factory,
bool setAsDefault = false) where TService : IAIService
{
this._aiServices.SetService<TService>(serviceId, () => factory((this._logger, this._config)), setAsDefault);
return this;
}
}
@@ -23,7 +23,7 @@ public static class MemoryConfiguration
/// <param name="embeddingsServiceId">Kernel service id for embedding generation</param>
public static void UseMemory(this IKernel kernel, IMemoryStore storage, string? embeddingsServiceId = null)
{
var embeddingGenerator = kernel.GetService<IEmbeddingGeneration<string, float>>(embeddingsServiceId);
var embeddingGenerator = kernel.GetService<ITextEmbeddingGeneration>(embeddingsServiceId);
UseMemory(kernel, embeddingGenerator, storage);
}
@@ -35,7 +35,7 @@ public static class MemoryConfiguration
/// <param name="embeddingGenerator">Embedding generator</param>
/// <param name="storage">Memory storage</param>
[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<string, float> embeddingGenerator, IMemoryStore storage)
public static void UseMemory(this IKernel kernel, ITextEmbeddingGeneration embeddingGenerator, IMemoryStore storage)
{
Verify.NotNull(storage);
Verify.NotNull(embeddingGenerator);
@@ -15,12 +15,12 @@ namespace Microsoft.SemanticKernel.Memory;
/// </summary>
public sealed class SemanticTextMemory : ISemanticTextMemory, IDisposable
{
private readonly IEmbeddingGeneration<string, float> _embeddingGenerator;
private readonly ITextEmbeddingGeneration _embeddingGenerator;
private readonly IMemoryStore _storage;
public SemanticTextMemory(
IMemoryStore storage,
IEmbeddingGeneration<string, float> embeddingGenerator)
ITextEmbeddingGeneration embeddingGenerator)
{
this._embeddingGenerator = embeddingGenerator;
this._storage = storage;
@@ -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<Type, Dictionary<string, object>> _services = new();
private readonly Dictionary<Type, Dictionary<string, Func<object>>> _services = new();
// A dictionary that maps a service type to the name of the default service
private readonly Dictionary<Type, string> _defaultIds = new();
/// <summary>
/// Registers a singleton service instance with the default name.
/// </summary>
/// <typeparam name="T">The type of the service.</typeparam>
/// <param name="service">The service instance.</param>
/// <exception cref="ArgumentNullException">The service instance is null.</exception>
public void SetService<T>(T service) where T : IAIService
=> this.SetService<T>(DefaultKey, service, true);
/// <summary>
/// Registers a singleton service instance with an optional name and default flag.
/// </summary>
/// <typeparam name="T">The type of the service.</typeparam>
/// <param name="name">The name of the service, or null for the default service.</param>
/// <param name="service">The service instance.</param>
/// <param name="setAsDefault">Whether the service should be the default for its type.</param>
/// <exception cref="ArgumentNullException">The service instance is null.</exception>
/// <exception cref="ArgumentException">The name is empty or whitespace.</exception>
public void SetService<T>(string? name, T service, bool setAsDefault = false) where T : IAIService
=> this.SetService<T>(name, (() => service), setAsDefault);
/// <summary>
/// Registers a transient service factory with the default name.
/// </summary>
/// <typeparam name="T">The type of the service.</typeparam>
/// <param name="factory">The factory function to create the service instance.</param>
/// <exception cref="ArgumentNullException">The factory function is null.</exception>
public void SetService<T>(Func<T> factory) where T : IAIService
=> this.SetService<T>(DefaultKey, factory, true);
/// <summary>
/// Registers a transient service factory with an optional name and default flag.
/// </summary>
/// <typeparam name="T">The type of the service.</typeparam>
/// <param name="name">The name of the service, or null for the default service.</param>
/// <param name="factory">The factory function to create the service instance.</param>
/// <param name="setAsDefault">Whether the service should be the default for its type.</param>
/// <exception cref="ArgumentNullException">The factory function is null.</exception>
/// <exception cref="ArgumentException">The name is empty or whitespace.</exception>
public void SetService<T>(string? name, Func<T> 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<T>())
{
// Update the default name for the service type
this._defaultIds[type] = name ?? DefaultKey;
}
var objectFactory = factory as Func<object>;
// Register the factory with the given name
namedServices[name ?? DefaultKey] = objectFactory
?? throw new InvalidOperationException("Service factory is an invalid format");
}
/// <summary>
/// Builds an <see cref="INamedServiceProvider{TService}"/> from the registered services and default names.
/// </summary>
/// <returns></returns>
public IAIServiceProvider Build()
{
// Create a clone of the services and defaults Dictionaries to prevent further changes
// by the services provider.
var servicesClone = this._services.ToDictionary(
typeCollection => typeCollection.Key,
typeCollection => typeCollection.Value.ToDictionary(
service => service.Key,
service => service.Value));
var defaultsClone = this._defaultIds.ToDictionary(
typeDefault => typeDefault.Key,
typeDefault => typeDefault.Value);
return new AIServiceProvider(servicesClone, defaultsClone);
}
private bool HasDefault<T>() where T : IAIService
=> this._defaultIds.TryGetValue(typeof(T), out var defaultName)
&& !string.IsNullOrEmpty(defaultName);
}
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
namespace Microsoft.SemanticKernel.Services;
public class AIServiceProvider : NamedServiceProvider<IAIService>, IAIServiceProvider
{
public AIServiceProvider(Dictionary<Type, Dictionary<string, Func<object>>> services, Dictionary<Type, string> defaultIds)
: base(services, defaultIds)
{
}
}
@@ -0,0 +1,70 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
namespace Microsoft.SemanticKernel.Services;
public class NamedServiceProvider<TService> : INamedServiceProvider<TService>
{
// A dictionary that maps a service type to a nested dictionary of names and service instances or factories
//private readonly Dictionary<Type, Dictionary<string, object>> _services = new();
private readonly Dictionary<Type, Dictionary<string, Func<object>>> _services;
// A dictionary that maps a service type to the name of the default service
private readonly Dictionary<Type, string> _defaultIds;
public NamedServiceProvider(
Dictionary<Type, Dictionary<string, Func<object>>> services,
Dictionary<Type, string> defaultIds)
{
this._services = services;
this._defaultIds = defaultIds;
}
/// <inheritdoc/>
public T? GetService<T>(string? name = null) where T : TService
{
// Return the service, casting or invoking the factory if needed
var factory = this.GetServiceFactory<T>(name);
if (factory is Func<T>)
{
return factory.Invoke();
}
return default;
}
/// <inheritdoc/>
private string? GetDefaultServiceName<T>() where T : TService
{
// Returns the name of the default service for the given type, or null if none
var type = typeof(T);
if (this._defaultIds.TryGetValue(type, out var name))
{
return name;
}
return null;
}
private Func<T>? GetServiceFactory<T>(string? name = null) where T : TService
{
// Get the nested dictionary for the service type
if (this._services.TryGetValue(typeof(T), out var namedServices))
{
Func<object>? serviceFactory = null;
// If the name is not specified, try to load the default factory
name ??= this.GetDefaultServiceName<T>();
if (name != null)
{
// Check if there is a service registered with the given name
namedServices.TryGetValue(name, out serviceFactory);
}
return serviceFactory as Func<T>;
}
return null;
}
}
@@ -229,7 +229,7 @@ public sealed class SKFunction : ISKFunction, IDisposable
{
Verify.NotNull(serviceFactory);
this.VerifyIsSemantic();
this._aiService = serviceFactory.Invoke();
this._aiService = new Lazy<ITextCompletion>(serviceFactory);
return this;
}
@@ -279,7 +279,7 @@ public sealed class SKFunction : ISKFunction, IDisposable
private readonly Delegate _function;
private readonly ILogger _log;
private IReadOnlySkillCollection? _skillCollection;
private ITextCompletion? _aiService = null;
private Lazy<ITextCompletion>? _aiService = null;
private CompleteRequestSettings _aiRequestSettings = new();
private struct MethodDetails
@@ -345,9 +345,15 @@ public sealed class SKFunction : ISKFunction, IDisposable
private void ReleaseUnmanagedResources()
{
if (this._aiService is not IDisposable disposable) { return; }
if (this._aiService == null || !this._aiService.IsValueCreated)
{
return;
}
disposable.Dispose();
if (this._aiService.Value is IDisposable disposable)
{
disposable.Dispose();
}
}
/// <summary>
@@ -374,7 +380,7 @@ public sealed class SKFunction : ISKFunction, IDisposable
settings ??= this._aiRequestSettings;
var callable = (Func<ITextCompletion?, CompleteRequestSettings?, SKContext, Task<SKContext>>)this._function;
context.Variables.Update((await callable(this._aiService, settings, context).ConfigureAwait(false)).Variables);
context.Variables.Update((await callable(this._aiService?.Value, settings, context).ConfigureAwait(false)).Variables);
return context;
}
@@ -23,7 +23,7 @@ public class SearchUrlSkillTests
public void ItCanBeImported()
{
// Arrange
IKernel kernel = KernelBuilder.Create();
IKernel kernel = Kernel.Builder.Build();
// Act - Assert no exception occurs e.g. due to reflection
kernel.ImportSkill(new SearchUrlSkill(), "search");
+15 -31
View File
@@ -10,8 +10,11 @@
using Microsoft.SemanticKernel.AI.TextCompletion;
using Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextCompletion;
using Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextEmbedding;
using Microsoft.SemanticKernel.Services;
IKernel kernel1 = KernelBuilder.Create();
#pragma warning disable CA1852 // Seal internal types
IKernel kernel1 = Kernel.Builder.Build();
#pragma warning restore CA1852 // Seal internal types
IKernel kernel2 = Kernel.Builder.Build();
@@ -49,13 +52,16 @@ var memory = new SemanticTextMemory(memoryStorage, textEmbeddingGenerator);
var skills = new SkillCollection();
var templateEngine = new PromptTemplateEngine(logger);
var config = new KernelConfig();
var httpHandler = new DefaultHttpRetryHandler(new HttpRetryConfig(), logger);
var httpClient = new HttpClient(httpHandler);
ITextCompletion Factory(IKernel kernel) => new AzureTextCompletion("deploymentName", "https://...", "apiKey", httpClient, logger);
config.AddTextCompletionService(Factory);
var aiServices = new AIServiceCollection();
ITextCompletion Factory() => new AzureTextCompletion("deploymentName", "https://...", "apiKey", httpClient, logger);
aiServices.SetService("foo", Factory);
IAIServiceProvider aiServiceProvider = aiServices.Build();
// Create kernel manually injecting all the dependencies
var kernel3 = new Kernel(skills, templateEngine, memory, config, logger);
var kernel3 = new Kernel(skills, aiServiceProvider, templateEngine, memory, config, logger);
// ==========================================================================================================
// The kernel builder purpose is to simplify this process, automating how dependencies
@@ -65,10 +71,7 @@ var kernel3 = new Kernel(skills, templateEngine, memory, config, logger);
var kernel4 = Kernel.Builder
.WithLogger(NullLogger.Instance)
.WithMemory(memory)
.Configure(c =>
{
c.AddAzureTextCompletionService("deploymentName", "https://...", "apiKey");
})
.WithAzureTextCompletionService("deploymentName", "https://...", "apiKey")
.Build();
// Example: how to use a custom memory storage and custom embedding generator
@@ -81,36 +84,17 @@ var kernel5 = Kernel.Builder
var kernel6 = Kernel.Builder
.WithLogger(NullLogger.Instance)
.WithMemoryStorage(memoryStorage) // Custom memory storage
.Configure(c =>
{
// This will be used when using AI completions
c.AddAzureTextCompletionService("completionDeploymentName", "https://...", "apiKey");
// This will be used when indexing memory records
c.AddAzureTextEmbeddingGenerationService("embeddingsDeploymentName", "https://...", "apiKey", serviceId: "myName3");
})
.WithAzureTextCompletionService("myName1", "completionDeploymentName", "https://...", "apiKey") // This will be used when using AI completions
.WithAzureTextEmbeddingGenerationService("myName2", "embeddingsDeploymentName", "https://...", "apiKey") // This will be used when indexing memory records
.Build();
// ==========================================================================================================
// The kernel configuration can be defined with the builder, but can also be managed
// when the kernel instance is already created.
// The AI services are defined with the builder
var kernel7 = Kernel.Builder
.Configure(c =>
{
c.AddAzureTextCompletionService("completionDeploymentName", "https://...", "apiKey");
})
.Configure(c =>
{
c.SetDefaultTextEmbeddingGenerationService("myName3");
})
.WithAzureTextCompletionService("myName1", "completionDeploymentName", "https://...", "apiKey", true)
.Build();
kernel7.Config
.AddAzureTextEmbeddingGenerationService("embeddingsDeploymentName1", "https://...", "apiKey", serviceId: "myName2")
.AddAzureTextEmbeddingGenerationService("embeddingsDeploymentName2", "https://...", "apiKey", serviceId: "myName3")
.AddOpenAITextCompletionService("text-davinci-003", "sk-...");
// ==========================================================================================================
// When invoking AI, by default the kernel will retry on transient errors, such as throttling and timeouts.
// The default behavior can be configured or a custom retry handler can be injected that will apply to all
@@ -43,53 +43,50 @@ internal static class SemanticKernelFactory
private static KernelBuilder _ConfigureKernelBuilder(ApiKeyConfig config, KernelBuilder builder, IMemoryStore? memoryStore)
{
return builder.Configure(c =>
switch (config.CompletionConfig.AIService)
{
switch (config.CompletionConfig.AIService)
case AIService.OpenAI:
builder.WithOpenAITextCompletionService(
config.CompletionConfig.DeploymentOrModelId,
config.CompletionConfig.Key,
config.CompletionConfig.ServiceId);
break;
case AIService.AzureOpenAI:
builder.WithAzureTextCompletionService(
config.CompletionConfig.DeploymentOrModelId,
config.CompletionConfig.Endpoint,
config.CompletionConfig.Key,
serviceId: config.CompletionConfig.ServiceId);
break;
default:
break;
}
if (memoryStore != null && config.EmbeddingConfig.IsValid())
{
switch (config.EmbeddingConfig.AIService)
{
case AIService.OpenAI:
c.AddOpenAIChatCompletionService(
modelId: config.CompletionConfig.DeploymentOrModelId,
apiKey: config.CompletionConfig.Key,
serviceId: config.CompletionConfig.ServiceId,
alsoAsTextCompletion: true);
builder.WithOpenAITextEmbeddingGenerationService(
config.EmbeddingConfig.DeploymentOrModelId,
config.EmbeddingConfig.Key,
serviceId: config.EmbeddingConfig.ServiceId);
break;
case AIService.AzureOpenAI:
c.AddAzureChatCompletionService(
deploymentName: config.CompletionConfig.DeploymentOrModelId,
endpoint: config.CompletionConfig.Endpoint,
apiKey: config.CompletionConfig.Key,
serviceId: config.CompletionConfig.ServiceId,
alsoAsTextCompletion: true);
builder.WithAzureTextEmbeddingGenerationService(
config.EmbeddingConfig.DeploymentOrModelId,
config.EmbeddingConfig.Endpoint,
config.EmbeddingConfig.Key,
serviceId: config.EmbeddingConfig.ServiceId);
break;
default:
break;
}
if (memoryStore != null && config.EmbeddingConfig.IsValid())
{
switch (config.EmbeddingConfig.AIService)
{
case AIService.OpenAI:
c.AddOpenAITextEmbeddingGenerationService(
config.EmbeddingConfig.DeploymentOrModelId,
config.EmbeddingConfig.Key,
serviceId: config.EmbeddingConfig.ServiceId);
break;
case AIService.AzureOpenAI:
c.AddAzureTextEmbeddingGenerationService(
config.EmbeddingConfig.DeploymentOrModelId,
config.EmbeddingConfig.Endpoint,
config.EmbeddingConfig.Key,
serviceId: config.EmbeddingConfig.ServiceId);
break;
default:
break;
}
builder.WithMemoryStorage(memoryStore);
}
builder.WithMemoryStorage(memoryStore);
}
});
return builder;
}
private static IKernel _CompleteKernelSetup(HttpRequestData req, KernelBuilder builder, ILogger logger, IEnumerable<string>? skillsToLoad = null)
@@ -27,6 +27,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\dotnet\src\Connectors\Connectors.AI.OpenAI\Connectors.AI.OpenAI.csproj" />
<ProjectReference Include="..\..\..\dotnet\src\SemanticKernel.Abstractions\SemanticKernel.Abstractions.csproj" />
<ProjectReference Include="..\..\..\dotnet\src\Skills\Skills.MsGraph\Skills.MsGraph.csproj" />
<ProjectReference Include="..\..\..\dotnet\src\SemanticKernel\SemanticKernel.csproj" />
</ItemGroup>
+25 -21
View File
@@ -65,6 +65,18 @@ public sealed class Program
MsGraphConfiguration graphApiConfiguration = candidateGraphApiConfig;
string? defaultCompletionServiceId = configuration["DefaultCompletionServiceId"];
if (string.IsNullOrWhiteSpace(defaultCompletionServiceId))
{
throw new InvalidOperationException("'DefaultCompletionServiceId' is not set in configuration.");
}
string? currentAssemblyDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (string.IsNullOrWhiteSpace(currentAssemblyDirectory))
{
throw new InvalidOperationException("Unable to determine current assembly directory.");
}
#endregion
// Initialize the Graph API client with interactive authentication and local caching.
@@ -95,22 +107,20 @@ public sealed class Program
EmailSkill outlookSkill = new(new OutlookMailConnector(graphServiceClient), loggerFactory.CreateLogger<EmailSkill>());
// Initialize the Semantic Kernel and and register connections with OpenAI/Azure OpenAI instances.
IKernel sk = Kernel.Builder.WithLogger(loggerFactory.CreateLogger<IKernel>()).Build();
var onedrive = sk.ImportSkill(oneDriveSkill, "onedrive");
var todo = sk.ImportSkill(todoSkill, "todo");
var outlook = sk.ImportSkill(outlookSkill, "outlook");
KernelBuilder builder = Kernel.Builder
.WithLogger(loggerFactory.CreateLogger<IKernel>());
if (configuration.GetSection("AzureOpenAI:ServiceId").Value != null)
{
AzureOpenAIConfiguration? azureOpenAIConfiguration = configuration.GetSection("AzureOpenAI").Get<AzureOpenAIConfiguration>();
if (azureOpenAIConfiguration != null)
{
sk.Config.AddAzureTextCompletionService(
builder.WithAzureTextCompletionService(
deploymentName: azureOpenAIConfiguration.DeploymentName,
endpoint: azureOpenAIConfiguration.Endpoint,
apiKey: azureOpenAIConfiguration.ApiKey,
serviceId: azureOpenAIConfiguration.ServiceId);
serviceId: azureOpenAIConfiguration.ServiceId,
setAsDefault: azureOpenAIConfiguration.ServiceId == defaultCompletionServiceId);
}
}
@@ -119,25 +129,19 @@ public sealed class Program
OpenAIConfiguration? openAIConfiguration = configuration.GetSection("OpenAI").Get<OpenAIConfiguration>();
if (openAIConfiguration != null)
{
sk.Config.AddOpenAITextCompletionService(
builder.WithOpenAITextCompletionService(
modelId: openAIConfiguration.ModelId,
apiKey: openAIConfiguration.ApiKey);
apiKey: openAIConfiguration.ApiKey,
serviceId: openAIConfiguration.ServiceId,
setAsDefault: openAIConfiguration.ServiceId == defaultCompletionServiceId);
}
}
string? defaultCompletionServiceId = configuration["DefaultCompletionServiceId"];
if (string.IsNullOrWhiteSpace(defaultCompletionServiceId))
{
throw new InvalidOperationException("'DefaultCompletionServiceId' is not set in configuration.");
}
IKernel sk = builder.Build();
sk.Config.SetDefaultTextCompletionService(defaultCompletionServiceId);
string? currentAssemblyDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (string.IsNullOrWhiteSpace(currentAssemblyDirectory))
{
throw new InvalidOperationException("Unable to determine current assembly directory.");
}
var onedrive = sk.ImportSkill(oneDriveSkill, "onedrive");
var todo = sk.ImportSkill(todoSkill, "todo");
var outlook = sk.ImportSkill(outlookSkill, "outlook");
string skillParentDirectory = RepoUtils.RepoFiles.SampleSkillsPath();
@@ -14,12 +14,11 @@ public static class Example04_CombineLLMPromptsAndNativeCode
{
Console.WriteLine("======== LLMPrompts ========");
IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
// OpenAI settings
kernel.Config.AddOpenAITextCompletionService("text-davinci-002", Env.Var("OPENAI_API_KEY"), serviceId: "text-davinci-002");
kernel.Config.AddOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"), serviceId: "text-davinci-003");
kernel.Config.SetDefaultTextCompletionService("text-davinci-003");
IKernel kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
.WithOpenAITextCompletionService("text-davinci-002", Env.Var("OPENAI_API_KEY"), serviceId: "text-davinci-002")
.WithOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"))
.Build();
// Load native skill
using var bingConnector = new BingConnector(Env.Var("BING_API_KEY"));
@@ -18,10 +18,10 @@ public static class Example05_InlineFunctionDefinition
* function inline if you like.
*/
IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
// OpenAI settings
kernel.Config.AddOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"));
IKernel kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
.WithOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"))
.Build();
// Function defined using few-shot design pattern
const string FunctionDefinition = @"
@@ -18,8 +18,10 @@ public static class Example06_TemplateLanguage
{
Console.WriteLine("======== TemplateLanguage ========");
IKernel kernel = Kernel.Builder.WithLogger(ConsoleLogger.Log).Build();
kernel.Config.AddOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"));
IKernel kernel = Kernel.Builder
.WithLogger(ConsoleLogger.Log)
.WithOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"))
.Build();
// Load native skill into the kernel skill collection, sharing its functions with prompt templates
// Functions loaded here are available as "time.*"
@@ -21,9 +21,10 @@ public static class Example07_BingAndGoogleSkills
{
public static async Task RunAsync()
{
IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
kernel.Config.AddOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"));
IKernel kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
.WithOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"))
.Build();
// Load Bing skill
using var bingConnector = new BingConnector(Env.Var("BING_API_KEY"));
@@ -47,7 +47,7 @@ public static class Example08_RetryHandler
kernelBuilder = kernelBuilder.Configure(c => c.DefaultHttpRetryConfig.RetryableStatusCodes.Add(HttpStatusCode.Unauthorized));
// OpenAI settings - you can set the OPENAI_API_KEY to an invalid value to see the retry policy in play
kernelBuilder = kernelBuilder.Configure(c => c.AddOpenAITextCompletionService("text-davinci-003", "BAD_KEY"));
kernelBuilder = kernelBuilder.WithOpenAITextCompletionService("text-davinci-003", "BAD_KEY");
var kernel = kernelBuilder.Build();
@@ -56,9 +56,11 @@ public static class Example08_RetryHandler
private static IKernel InitializeKernel()
{
var kernel = Kernel.Builder.WithLogger(InfoLogger.Log).Build();
// OpenAI settings - you can set the OPENAI_API_KEY to an invalid value to see the retry policy in play
kernel.Config.AddOpenAITextCompletionService("text-davinci-003", "BAD_KEY");
var kernel = Kernel.Builder
.WithLogger(InfoLogger.Log)
// OpenAI settings - you can set the OPENAI_API_KEY to an invalid value to see the retry policy in play
.WithOpenAITextCompletionService("text-davinci-003", "BAD_KEY")
.Build();
return kernel;
}
@@ -71,13 +73,11 @@ public static class Example08_RetryHandler
private static async Task RunRetryPolicyBuilderAsync(Type retryHandlerFactoryType)
{
var kernelBuilder = Kernel.Builder.WithLogger(InfoLogger.Log)
.WithRetryHandlerFactory((Activator.CreateInstance(retryHandlerFactoryType) as IDelegatingHandlerFactory)!);
// OpenAI settings - you can set the OPENAI_API_KEY to an invalid value to see the retry policy in play
kernelBuilder = kernelBuilder.Configure(c => c.AddOpenAITextCompletionService("text-davinci-003", "BAD_KEY"));
var kernel = kernelBuilder.Build();
var kernel = Kernel.Builder.WithLogger(InfoLogger.Log)
.WithRetryHandlerFactory((Activator.CreateInstance(retryHandlerFactoryType) as IDelegatingHandlerFactory)!)
// OpenAI settings - you can set the OPENAI_API_KEY to an invalid value to see the retry policy in play
.WithOpenAITextCompletionService("text-davinci-003", "BAD_KEY")
.Build();
await ImportAndExecuteSkillAsync(kernel);
}
@@ -18,8 +18,10 @@ public static class Example09_FunctionTypes
var fakeContext = new SKContext(logger: ConsoleLogger.Log);
var kernel = Kernel.Builder.WithLogger(ConsoleLogger.Log).Build();
kernel.Config.AddOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"));
var kernel = Kernel.Builder
.WithLogger(ConsoleLogger.Log)
.WithOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"))
.Build();
// Load native skill into the kernel skill collection, sharing its functions with prompt templates
var test = kernel.ImportSkill(new LocalExampleSkill(), "test");
@@ -20,8 +20,9 @@ public static class Example10_DescribeAllSkillsAndFunctions
{
Console.WriteLine("======== Describe all skills and functions ========");
var kernel = KernelBuilder.Create();
kernel.Config.AddOpenAITextCompletionService("text-davinci-003", "none");
var kernel = Kernel.Builder
.WithOpenAITextCompletionService("text-davinci-003", "none")
.Build();
// Import a native skill
var skill1 = new StaticTextSkill();
@@ -25,11 +25,14 @@ internal static class Example12_SequentialPlanner
private static async Task PoetrySamplesAsync()
{
Console.WriteLine("======== Sequential Planner - Create and Execute Poetry Plan ========");
var kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
kernel.Config.AddAzureTextCompletionService(
Env.Var("AZURE_OPENAI_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_ENDPOINT"),
Env.Var("AZURE_OPENAI_KEY"));
var kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
.WithAzureTextCompletionService(
Env.Var("AZURE_OPENAI_SERVICE_ID"),
Env.Var("AZURE_OPENAI_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_ENDPOINT"),
Env.Var("AZURE_OPENAI_KEY"))
.Build();
string folder = RepoFiles.SampleSkillsPath();
kernel.ImportSemanticSkillFromDirectory(folder,
@@ -135,19 +138,14 @@ internal static class Example12_SequentialPlanner
var kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
.Configure(
config =>
{
config.AddAzureTextCompletionService(
.WithAzureTextCompletionService(
Env.Var("AZURE_OPENAI_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_ENDPOINT"),
Env.Var("AZURE_OPENAI_KEY"));
config.AddAzureTextEmbeddingGenerationService(
Env.Var("AZURE_OPENAI_KEY"))
.WithAzureTextEmbeddingGenerationService(
Env.Var("AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_EMBEDDINGS_ENDPOINT"),
Env.Var("AZURE_OPENAI_EMBEDDINGS_KEY"));
})
Env.Var("AZURE_OPENAI_EMBEDDINGS_KEY"))
.WithMemoryStorage(new VolatileMemoryStore())
.Build();
@@ -182,11 +180,13 @@ internal static class Example12_SequentialPlanner
private static IKernel InitializeKernelAndPlanner(out SequentialPlanner planner, int maxTokens = 1024)
{
var kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
kernel.Config.AddAzureTextCompletionService(
Env.Var("AZURE_OPENAI_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_ENDPOINT"),
Env.Var("AZURE_OPENAI_KEY"));
var kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
.WithAzureTextCompletionService(
Env.Var("AZURE_OPENAI_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_ENDPOINT"),
Env.Var("AZURE_OPENAI_KEY"))
.Build();
planner = new SequentialPlanner(kernel, new SequentialPlannerConfig { MaxTokens = maxTokens });
@@ -178,11 +178,13 @@ Jane: Goodbye!
private static IKernel InitializeKernel()
{
IKernel kernel = Kernel.Builder.WithLogger(ConsoleLogger.Log).Build();
kernel.Config.AddAzureTextCompletionService(
Env.Var("AZURE_OPENAI_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_ENDPOINT"),
Env.Var("AZURE_OPENAI_KEY"));
IKernel kernel = Kernel.Builder
.WithLogger(ConsoleLogger.Log)
.WithAzureTextCompletionService(
Env.Var("AZURE_OPENAI_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_ENDPOINT"),
Env.Var("AZURE_OPENAI_KEY"))
.Build();
return kernel;
}
@@ -56,7 +56,7 @@ public static class Example14_SemanticMemory
var kernelWithCustomDb = Kernel.Builder
.WithLogger(ConsoleLogger.Log)
.Configure(c => c.AddOpenAITextEmbeddingGenerationService("ada", "text-embedding-ada-002", Env.Var("OPENAI_API_KEY")))
.WithOpenAITextEmbeddingGenerationService("ada", "text-embedding-ada-002", Env.Var("OPENAI_API_KEY"))
.WithMemoryStorage(new VolatileMemoryStore())
.Build();
@@ -17,11 +17,8 @@ public static class Example15_MemorySkill
{
var kernel = Kernel.Builder
.WithLogger(ConsoleLogger.Log)
.Configure(c =>
{
c.AddOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"));
c.AddOpenAITextEmbeddingGenerationService("text-embedding-ada-002", Env.Var("OPENAI_API_KEY"));
})
.WithOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"))
.WithOpenAITextEmbeddingGenerationService("text-embedding-ada-002", Env.Var("OPENAI_API_KEY"))
.WithMemoryStorage(new VolatileMemoryStore())
.Build();
@@ -85,12 +85,13 @@ public static class Example16_CustomLLM
{
Console.WriteLine("======== Custom LLM - Text Completion - SKFunction ========");
IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
ITextCompletion Factory(IKernel k) => new MyTextCompletionService();
// Add your text completion service
kernel.Config.AddTextCompletionService(Factory);
IKernel kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
// Add your text completion service as a singleton instance
.WithAIService<ITextCompletion>("myService1", new MyTextCompletionService())
// Add your text completion service as a factory method
.WithAIService<ITextCompletion>("myService2", (_) => new MyTextCompletionService())
.Build();
const string FunctionDefinition = "Does the text contain grammar errors (Y/N)? Text: {{$input}}";
@@ -52,10 +52,11 @@ public static class Example17_ChatGPT
{
Console.WriteLine("======== Open AI - ChatGPT ========");
IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
// Add your chat completion service
kernel.Config.AddOpenAIChatCompletionService("gpt-3.5-turbo", Env.Var("OPENAI_API_KEY"));
IKernel kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
.WithOpenAIChatCompletionService("gpt-3.5-turbo", Env.Var("OPENAI_API_KEY"))
.Build();
IChatCompletion chatGPT = kernel.GetService<IChatCompletion>();
@@ -66,13 +67,13 @@ public static class Example17_ChatGPT
{
Console.WriteLine("======== Azure Open AI - ChatGPT ========");
IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
// Add your chat completion service
kernel.Config.AddAzureChatCompletionService(
Env.Var("AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_ENDPOINT"),
Env.Var("AZURE_OPENAI_KEY"));
IKernel kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
.WithAzureChatCompletionService(
Env.Var("AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_ENDPOINT"),
Env.Var("AZURE_OPENAI_KEY")) // Add your chat completion service
.Build();
IChatCompletion chatGPT = kernel.GetService<IChatCompletion>();
@@ -19,10 +19,13 @@ public static class Example18_DallE
{
Console.WriteLine("======== Dall-E 2 Image Generation ========");
IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
// Add your image generation service
kernel.Config.AddOpenAIImageGenerationService(Env.Var("OPENAI_API_KEY"));
IKernel kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
// Add your image generation service
.WithOpenAIImageGenerationService("dallE", Env.Var("OPENAI_API_KEY"))
// Add your chat completion service
.WithOpenAIChatCompletionService("gpt-3.5-turbo", Env.Var("OPENAI_API_KEY"))
.Build();
IImageGeneration dallE = kernel.GetService<IImageGeneration>();
@@ -41,9 +44,6 @@ public static class Example18_DallE
Console.WriteLine("======== Chat with images ========");
// Add your chat completion service
kernel.Config.AddOpenAIChatCompletionService("gpt-3.5-turbo", Env.Var("OPENAI_API_KEY"));
IChatCompletion chatGPT = kernel.GetService<IChatCompletion>();
var chatHistory = (OpenAIChatHistory)chatGPT.CreateNewChat(
"You're chatting with a user. Instead of replying directly to the user" +
@@ -19,11 +19,8 @@ public static class Example19_Qdrant
QdrantMemoryStore memoryStore = new(Env.Var("QDRANT_ENDPOINT"), qdrantPort, vectorSize: 1536, ConsoleLogger.Log);
IKernel kernel = Kernel.Builder
.WithLogger(ConsoleLogger.Log)
.Configure(c =>
{
c.AddOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"));
c.AddOpenAITextEmbeddingGenerationService("text-embedding-ada-002", Env.Var("OPENAI_API_KEY"));
})
.WithOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"))
.WithOpenAITextEmbeddingGenerationService("text-embedding-ada-002", Env.Var("OPENAI_API_KEY"))
.WithMemoryStorage(memoryStore)
.Build();
@@ -17,10 +17,11 @@ public static class Example20_HuggingFace
{
Console.WriteLine("======== HuggingFace text completion AI ========");
IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
// Add HuggingFace text completion service
kernel.Config.AddTextCompletionService(_ => new HuggingFaceTextCompletion(Env.Var("HF_API_KEY"), model: "gpt2"));
IKernel kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
// Add HuggingFace text completion service as a factory methods
.WithDefaultAIService((_) => new HuggingFaceTextCompletion(Env.Var("HF_API_KEY"), "gpt2"))
.Build();
const string FunctionDefinition = "Question: {{$input}}; Answer:";
@@ -28,8 +28,6 @@ public static class Example26_AADAuth
{
Console.WriteLine("======== SK with AAD Auth ========");
IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
// Optional: choose which authentication to support
var authOptions = new DefaultAzureCredentialOptions
{
@@ -42,11 +40,14 @@ public static class Example26_AADAuth
ExcludeInteractiveBrowserCredential = true,
};
// Add Azure chat completion service using DefaultAzureCredential AAD auth
kernel.Config.AddAzureChatCompletionService(
"gpt-35-turbo",
"https://....openai.azure.com/",
new DefaultAzureCredential(authOptions));
IKernel kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
// Add Azure chat completion service using DefaultAzureCredential AAD auth
.WithAzureChatCompletionService(
"gpt-35-turbo",
"https://....openai.azure.com/",
new DefaultAzureCredential(authOptions))
.Build();
IChatCompletion chatGPT = kernel.GetService<IChatCompletion>();
var chatHistory = (OpenAIChatHistory)chatGPT.CreateNewChat();
@@ -16,10 +16,11 @@ public static class Example27_SemanticFunctionsUsingChatGPT
{
Console.WriteLine("======== Using Chat GPT model for text completion ========");
IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
// Note: we use Chat Completion and GPT 3.5 Turbo
kernel.Config.AddAzureChatCompletionService("gpt-35-turbo", "https://....openai.azure.com/", "...API KEY...");
IKernel kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
// Note: we use Chat Completion and GPT 3.5 Turbo
.WithAzureChatCompletionService("gpt-35-turbo", "https://....openai.azure.com/", "...API KEY...")
.Build();
var func = kernel.CreateSemanticFunction(
"List the two planets closest to '{{$input}}', excluding moons, using bullet points.");
@@ -13,10 +13,10 @@ public static class Example28_ActionPlanner
public static async Task RunAsync()
{
Console.WriteLine("======== Action Planner ========");
var kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
// Note: Action Planner works with old models like text-davinci-002
kernel.Config.AddOpenAITextCompletionService("text-davinci-002", Env.Var("OPENAI_API_KEY"));
var kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
.WithOpenAITextCompletionService("text-davinci-002", Env.Var("OPENAI_API_KEY"))// Note: Action Planner works with old models like text-davinci-002
.Build();
string folder = RepoFiles.SampleSkillsPath();
kernel.ImportSemanticSkillFromDirectory(folder, "SummarizeSkill");
@@ -63,8 +63,10 @@ public static class Example30_ChatWithPrompts
var userPromptTemplate = EmbeddedResource.Read("30-user-prompt.txt");
// Usual kernel initialization, with GPT 3.5 Turbo
IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
kernel.Config.AddOpenAIChatCompletionService("gpt-3.5-turbo", Env.Var("OPENAI_API_KEY"));
IKernel kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
.WithOpenAIChatCompletionService("gpt-3.5-turbo", Env.Var("OPENAI_API_KEY"), serviceId: "chat")
.Build();
// As an example, we import the time skill, which is used in system prompt to read the current date.
// We could also use a variable, this is just to show that the prompt can invoke functions.
@@ -120,19 +120,14 @@ internal static class Example31_CustomPlanner
{
return new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
.Configure(
config =>
{
config.AddAzureTextCompletionService(
Env.Var("AZURE_OPENAI_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_ENDPOINT"),
Env.Var("AZURE_OPENAI_KEY"));
config.AddAzureTextEmbeddingGenerationService(
Env.Var("AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_EMBEDDINGS_ENDPOINT"),
Env.Var("AZURE_OPENAI_EMBEDDINGS_KEY"));
})
.WithAzureTextCompletionService(
Env.Var("AZURE_OPENAI_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_ENDPOINT"),
Env.Var("AZURE_OPENAI_KEY"))
.WithAzureTextEmbeddingGenerationService(
Env.Var("AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_EMBEDDINGS_ENDPOINT"),
Env.Var("AZURE_OPENAI_EMBEDDINGS_KEY"))
.WithMemoryStorage(new VolatileMemoryStore())
.Build();
}
@@ -22,11 +22,13 @@ public static class Example32_StreamingCompletion
{
Console.WriteLine("======== Azure OpenAI - Text Completion - Raw Streaming ========");
IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
kernel.Config.AddAzureTextCompletionService(
Env.Var("AZURE_OPENAI_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_ENDPOINT"),
Env.Var("AZURE_OPENAI_KEY"));
IKernel kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
.WithAzureTextCompletionService(
Env.Var("AZURE_OPENAI_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_ENDPOINT"),
Env.Var("AZURE_OPENAI_KEY"))
.Build();
ITextCompletion textCompletion = kernel.GetService<ITextCompletion>();
@@ -37,8 +39,10 @@ public static class Example32_StreamingCompletion
{
Console.WriteLine("======== Open AI - Text Completion - Raw Streaming ========");
IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
kernel.Config.AddOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"), serviceId: "text-davinci-003");
IKernel kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
.WithOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"), serviceId: "text-davinci-003")
.Build();
ITextCompletion textCompletion = kernel.GetService<ITextCompletion>();
@@ -24,10 +24,10 @@ public static class Example33_StreamingChat
{
Console.WriteLine("======== Open AI - ChatGPT Streaming ========");
IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
// Add your chat completion service
kernel.Config.AddOpenAIChatCompletionService("gpt-3.5-turbo", Env.Var("OPENAI_API_KEY"));
IKernel kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
.WithOpenAIChatCompletionService("gpt-3.5-turbo", Env.Var("OPENAI_API_KEY")) // Add your chat completion service
.Build();
IChatCompletion chatGPT = kernel.GetService<IChatCompletion>();
@@ -38,13 +38,10 @@ public static class Example33_StreamingChat
{
Console.WriteLine("======== Azure Open AI - ChatGPT Streaming ========");
IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
// Add your chat completion service
kernel.Config.AddAzureChatCompletionService(
Env.Var("AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_ENDPOINT"),
Env.Var("AZURE_OPENAI_KEY"));
IKernel kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
.WithAzureChatCompletionService(Env.Var("AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"), Env.Var("AZURE_OPENAI_ENDPOINT"), Env.Var("OPENAI_API_KEY")) // Add your chat completion service
.Build();
IChatCompletion chatGPT = kernel.GetService<IChatCompletion>();
@@ -6,6 +6,7 @@ using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.AI.ChatCompletion;
using RepoUtils;
@@ -81,11 +82,12 @@ public static class Example34_CustomChatModel
{
Console.WriteLine("======== Custom LLM - Chat Completion ========");
IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
IChatCompletion Factory(ILogger l) => new MyChatCompletionService();
IChatCompletion Factory(IKernel k) => new MyChatCompletionService();
// Add your chat completion service
kernel.Config.AddChatCompletionService(Factory);
IKernel kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
.WithDefaultAIService<IChatCompletion>(Factory)
.Build();
IChatCompletion customChat = kernel.GetService<IChatCompletion>();
@@ -113,11 +115,12 @@ public static class Example34_CustomChatModel
{
Console.WriteLine("======== Custom LLM - Chat Completion Streaming ========");
IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
IChatCompletion Factory(ILogger l) => new MyChatCompletionService();
IChatCompletion Factory(IKernel k) => new MyChatCompletionService();
// Add your chat completion service
kernel.Config.AddChatCompletionService(Factory);
IKernel kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
.WithDefaultAIService<IChatCompletion>(Factory)
.Build();
IChatCompletion customChat = kernel.GetService<IChatCompletion>();
@@ -22,11 +22,12 @@ public static class Example36_MultiCompletion
{
Console.WriteLine("======== Azure OpenAI - Multiple Text Completion ========");
IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
kernel.Config.AddAzureTextCompletionService(
Env.Var("AZURE_OPENAI_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_ENDPOINT"),
Env.Var("AZURE_OPENAI_KEY"));
IKernel kernel = new KernelBuilder()
.WithAzureTextCompletionService(
Env.Var("AZURE_OPENAI_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_ENDPOINT"),
Env.Var("AZURE_OPENAI_KEY"))
.WithLogger(ConsoleLogger.Log).Build();
ITextCompletion textCompletion = kernel.GetService<ITextCompletion>();
@@ -37,8 +38,9 @@ public static class Example36_MultiCompletion
{
Console.WriteLine("======== Open AI - Multiple Text Completion ========");
IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
kernel.Config.AddOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"), serviceId: "text-davinci-003");
IKernel kernel = new KernelBuilder()
.WithOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"), serviceId: "text-davinci-003")
.WithLogger(ConsoleLogger.Log).Build();
ITextCompletion textCompletion = kernel.GetService<ITextCompletion>();
@@ -25,11 +25,12 @@ public static class Example37_MultiStreamingCompletion
{
Console.WriteLine("======== Azure OpenAI - Multiple Text Completion - Raw Streaming ========");
IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
kernel.Config.AddAzureTextCompletionService(
Env.Var("AZURE_OPENAI_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_ENDPOINT"),
Env.Var("AZURE_OPENAI_KEY"));
IKernel kernel = new KernelBuilder()
.WithAzureTextCompletionService(
Env.Var("AZURE_OPENAI_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_ENDPOINT"),
Env.Var("AZURE_OPENAI_KEY"))
.WithLogger(ConsoleLogger.Log).Build();
ITextCompletion textCompletion = kernel.GetService<ITextCompletion>();
@@ -40,8 +41,9 @@ public static class Example37_MultiStreamingCompletion
{
Console.WriteLine("======== Open AI - Multiple Text Completion - Raw Streaming ========");
IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build();
kernel.Config.AddOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"), serviceId: "text-davinci-003");
IKernel kernel = new KernelBuilder()
.WithOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"), serviceId: "text-davinci-003")
.WithLogger(ConsoleLogger.Log).Build();
ITextCompletion textCompletion = kernel.GetService<ITextCompletion>();
@@ -29,11 +29,8 @@ public static class Example38_Pinecone
IKernel kernel = Kernel.Builder
.WithLogger(ConsoleLogger.Log)
.Configure(c =>
{
c.AddOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"));
c.AddOpenAITextEmbeddingGenerationService("text-embedding-ada-002", Env.Var("OPENAI_API_KEY"));
})
.WithOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"))
.WithOpenAITextEmbeddingGenerationService("text-embedding-ada-002", Env.Var("OPENAI_API_KEY"))
.WithMemoryStorage(memoryStore)
.Build();
@@ -18,11 +18,8 @@ public static class Example39_Postgres
using PostgresMemoryStore memoryStore = await PostgresMemoryStore.ConnectAsync(connectionString, vectorSize: 1536);
IKernel kernel = Kernel.Builder
.WithLogger(ConsoleLogger.Log)
.Configure(c =>
{
c.AddOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"));
c.AddOpenAITextEmbeddingGenerationService("text-embedding-ada-002", Env.Var("OPENAI_API_KEY"));
})
.WithOpenAITextCompletionService("text-davinci-003", Env.Var("OPENAI_API_KEY"))
.WithOpenAITextEmbeddingGenerationService("text-embedding-ada-002", Env.Var("OPENAI_API_KEY"))
.WithMemoryStorage(memoryStore)
.Build();
@@ -0,0 +1,130 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.AI.TextCompletion;
using Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextCompletion;
using Microsoft.SemanticKernel.Memory;
using Microsoft.SemanticKernel.Services;
using Microsoft.SemanticKernel.SkillDefinition;
using Microsoft.SemanticKernel.TemplateEngine;
using RepoUtils;
/**
* The following examples show how to use SK SDK in applications using DI/IoC containers.
*/
public static class Example40_DIContainer
{
public static async Task RunAsync()
{
await UseKernelInDIPowerAppAsync();
await UseKernelInDIPowerApp_AdvancedScenarioAsync();
}
/// <summary>
/// This example shows how to register a Kernel in a DI container using KernelBuilder instead of
/// registering its dependencies.
/// </summary>
private static async Task UseKernelInDIPowerAppAsync()
{
//Bootstrapping code that initializes the modules, components, and classes that applications use.
//For regular .NET applications, the bootstrapping code usually resides either in the Main method or very close to it.
//In ASP.NET Core applications, the bootstrapping code is typically located in the ConfigureServices method of the Startup class.
//Registering Kernel dependencies
var collection = new ServiceCollection();
collection.AddTransient<ILogger>((_) => ConsoleLogger.Log);
//Registering Kernel
collection.AddTransient<IKernel>((serviceProvider) =>
{
return Kernel.Builder
.WithLogger(serviceProvider.GetRequiredService<ILogger>())
.WithOpenAITextCompletionService("text-davinci-002", Env.Var("OPENAI_API_KEY"))
.Build();
});
//Registering class that uses Kernel to execute a skill
collection.AddTransient<KernelClient>();
//Creating a service provider for resolving registered services
var serviceProvider = collection.BuildServiceProvider();
//If an application follows DI guidelines, the following line is unnecessary because DI will inject an instance of the KernelClient class to a class that references it.
//DI container guidelines - https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines#recommendations
var kernelClient = serviceProvider.GetRequiredService<KernelClient>();
//Execute the function
await kernelClient.SummarizeAsync("What's the tallest building in South America?");
}
/// <summary>
/// This example shows how to registered Kernel and all its dependencies in DI container.
/// </summary>
private static async Task UseKernelInDIPowerApp_AdvancedScenarioAsync()
{
//Bootstrapping code that initializes the modules, components, and classes that applications use.
//For regular .NET applications, the bootstrapping code usually resides either in the Main method or very close to it.
//In ASP.NET Core applications, the bootstrapping code is typically located in the ConfigureServices method of the Startup class.
//Registering AI services Kernel is going to use
var aiServicesCollection = new AIServiceCollection();
aiServicesCollection.SetService<ITextCompletion>(() => new OpenAITextCompletion("text-davinci-002", Env.Var("OPENAI_API_KEY")));
//Registering Kernel dependencies
var collection = new ServiceCollection();
collection.AddTransient<ILogger>((_) => ConsoleLogger.Log);
collection.AddTransient<KernelConfig>();
collection.AddTransient<ISkillCollection, SkillCollection>();
collection.AddTransient<IPromptTemplateEngine, PromptTemplateEngine>();
collection.AddTransient<ISemanticTextMemory>((_) => NullMemory.Instance);
collection.AddTransient<IAIServiceProvider>((_) => aiServicesCollection.Build()); //Registering AI service provider that is used by Kernel to resolve AI services runtime
//Registering Kernel
collection.AddTransient<IKernel, Kernel>();
//Registering class that uses Kernel to execute a skill
collection.AddTransient<KernelClient>();
//Creating a service provider for resolving registered services
var serviceProvider = collection.BuildServiceProvider();
//If an application follows DI guidelines, the following line is unnecessary because DI will inject an instance of the KernelClient class to a class that references it.
//DI container guidelines - https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines#recommendations
var kernelClient = serviceProvider.GetRequiredService<KernelClient>();
//Execute the function
await kernelClient.SummarizeAsync("What's the tallest building in South America?");
}
/// <summary>
/// Class that uses/references Kernel.
/// </summary>
#pragma warning disable CA1812 // Avoid uninstantiated internal classes
private sealed class KernelClient
#pragma warning restore CA1812 // Avoid uninstantiated internal classes
{
private readonly IKernel _kernel;
private readonly ILogger _logger;
public KernelClient(IKernel kernel, ILogger logger)
{
this._kernel = kernel;
this._logger = logger;
}
public async Task SummarizeAsync(string ask)
{
string folder = RepoFiles.SampleSkillsPath();
var sumSkill = this._kernel.ImportSemanticSkillFromDirectory(folder, "SummarizeSkill");
var result = await this._kernel.RunAsync(ask, sumSkill["Summarize"]);
this._logger.LogWarning("Result - {0}", result);
}
}
}
@@ -20,6 +20,7 @@
<PackageReference Include="Azure.Identity" Version="1.9.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0-preview.3.23174.8" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Polly" Version="7.2.3" />
@@ -124,5 +124,8 @@ public static class Program
await Example39_Postgres.RunAsync();
Console.WriteLine("== DONE ==");
await Example40_DIContainer.RunAsync();
Console.WriteLine("== DONE ==");
}
}