mirror of
https://github.com/microsoft/semantic-kernel.git
synced 2026-10-12 02:19:06 +00:00
### 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]>
194 lines
8.3 KiB
C#
194 lines
8.3 KiB
C#
// Copyright (c) Microsoft. All rights reserved.
|
|
|
|
using System;
|
|
using System.Net;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.SemanticKernel;
|
|
using Microsoft.SemanticKernel.CoreSkills;
|
|
using Microsoft.SemanticKernel.Reliability;
|
|
using Reliability;
|
|
using RepoUtils;
|
|
|
|
// ReSharper disable once InconsistentNaming
|
|
public static class Example08_RetryHandler
|
|
{
|
|
public static async Task RunAsync()
|
|
{
|
|
var kernel = InitializeKernel();
|
|
var retryHandlerFactory = new RetryThreeTimesWithBackoffFactory();
|
|
InfoLogger.Log.LogInformation("============================== RetryThreeTimesWithBackoff ==============================");
|
|
await RunRetryPolicyAsync(kernel, retryHandlerFactory);
|
|
|
|
InfoLogger.Log.LogInformation("========================= RetryThreeTimesWithRetryAfterBackoff =========================");
|
|
await RunRetryPolicyBuilderAsync(typeof(RetryThreeTimesWithRetryAfterBackoffFactory));
|
|
|
|
InfoLogger.Log.LogInformation("==================================== NoRetryPolicy =====================================");
|
|
await RunRetryPolicyBuilderAsync(typeof(NullHttpRetryHandlerFactory));
|
|
|
|
InfoLogger.Log.LogInformation("=============================== DefaultHttpRetryHandler ================================");
|
|
await RunRetryHandlerConfigAsync(new HttpRetryConfig() { MaxRetryCount = 3, UseExponentialBackoff = true });
|
|
|
|
InfoLogger.Log.LogInformation("======= DefaultHttpRetryConfig [MaxRetryCount = 3, UseExponentialBackoff = true] =======");
|
|
await RunRetryHandlerConfigAsync(new HttpRetryConfig() { MaxRetryCount = 3, UseExponentialBackoff = true });
|
|
}
|
|
|
|
private static async Task RunRetryHandlerConfigAsync(HttpRetryConfig? config = null)
|
|
{
|
|
var kernelBuilder = Kernel.Builder.WithLogger(InfoLogger.Log);
|
|
if (config != null)
|
|
{
|
|
kernelBuilder = kernelBuilder.Configure(c => c.SetDefaultHttpRetryConfig(config));
|
|
}
|
|
|
|
// Add 401 to the list of retryable status codes
|
|
// Typically 401 would not be something we retry but for demonstration
|
|
// purposes we are doing so as it's easy to trigger when using an invalid key.
|
|
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.WithOpenAITextCompletionService("text-davinci-003", "BAD_KEY");
|
|
|
|
var kernel = kernelBuilder.Build();
|
|
|
|
await ImportAndExecuteSkillAsync(kernel);
|
|
}
|
|
|
|
private static IKernel InitializeKernel()
|
|
{
|
|
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;
|
|
}
|
|
|
|
private static async Task RunRetryPolicyAsync(IKernel kernel, IDelegatingHandlerFactory retryHandlerFactory)
|
|
{
|
|
kernel.Config.SetHttpRetryHandlerFactory(retryHandlerFactory);
|
|
await ImportAndExecuteSkillAsync(kernel);
|
|
}
|
|
|
|
private static async Task RunRetryPolicyBuilderAsync(Type retryHandlerFactoryType)
|
|
{
|
|
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);
|
|
}
|
|
|
|
private static async Task ImportAndExecuteSkillAsync(IKernel kernel)
|
|
{
|
|
// Load semantic skill defined with prompt templates
|
|
string folder = RepoFiles.SampleSkillsPath();
|
|
|
|
kernel.ImportSkill(new TimeSkill(), "time");
|
|
|
|
var qaSkill = kernel.ImportSemanticSkillFromDirectory(
|
|
folder,
|
|
"QASkill");
|
|
|
|
var question = "How popular is Polly library?";
|
|
|
|
InfoLogger.Log.LogInformation("Question: {0}", question);
|
|
// To see the retry policy in play, you can set the OPENAI_API_KEY to an invalid value
|
|
var answer = await kernel.RunAsync(question, qaSkill["Question"]);
|
|
InfoLogger.Log.LogInformation("Answer: {0}", answer);
|
|
}
|
|
|
|
private static class InfoLogger
|
|
{
|
|
internal static ILogger Log => LogFactory.CreateLogger<object>();
|
|
private static ILoggerFactory LogFactory => s_loggerFactory.Value;
|
|
private static readonly Lazy<ILoggerFactory> s_loggerFactory = new(LogBuilder);
|
|
|
|
private static ILoggerFactory LogBuilder()
|
|
{
|
|
return LoggerFactory.Create(builder =>
|
|
{
|
|
builder.SetMinimumLevel(LogLevel.Information);
|
|
builder.AddFilter("Microsoft", LogLevel.Information);
|
|
builder.AddFilter("System", LogLevel.Information);
|
|
|
|
builder.AddConsole();
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Output:
|
|
info: object[0]
|
|
============================== RetryThreeTimesWithBackoff ==============================
|
|
info: object[0]
|
|
Question: How popular is Polly library?
|
|
warn: object[0]
|
|
Error executing action [attempt 1 of 3], pausing 2000ms. Outcome: Unauthorized
|
|
warn: object[0]
|
|
Error executing action [attempt 2 of 3], pausing 4000ms. Outcome: Unauthorized
|
|
warn: object[0]
|
|
Error executing action [attempt 3 of 3], pausing 8000ms. Outcome: Unauthorized
|
|
fail: object[0]
|
|
Function call fail during pipeline step 0: QASkill.Question
|
|
info: object[0]
|
|
Answer: Error: AccessDenied: The request is not authorized, HTTP status: Unauthorized
|
|
info: object[0]
|
|
========================= RetryThreeTimesWithRetryAfterBackoff =========================
|
|
info: object[0]
|
|
Question: How popular is Polly library?
|
|
warn: object[0]
|
|
Error executing action [attempt 1 of 3], pausing 2000ms. Outcome: Unauthorized
|
|
warn: object[0]
|
|
Error executing action [attempt 2 of 3], pausing 2000ms. Outcome: Unauthorized
|
|
warn: object[0]
|
|
Error executing action [attempt 3 of 3], pausing 2000ms. Outcome: Unauthorized
|
|
fail: object[0]
|
|
Function call fail during pipeline step 0: QASkill.Question
|
|
info: object[0]
|
|
Answer: Error: AccessDenied: The request is not authorized, HTTP status: Unauthorized
|
|
info: object[0]
|
|
==================================== NoRetryPolicy =====================================
|
|
info: object[0]
|
|
Question: How popular is Polly library?
|
|
fail: object[0]
|
|
Function call fail during pipeline step 0: QASkill.Question
|
|
info: object[0]
|
|
Answer: Error: AccessDenied: The request is not authorized, HTTP status: Unauthorized
|
|
info: object[0]
|
|
=============================== DefaultHttpRetryHandler ================================
|
|
info: object[0]
|
|
Question: How popular is Polly library?
|
|
warn: object[0]
|
|
Error executing action [attempt 1 of 3]. Reason: Unauthorized. Will retry after 2000ms
|
|
warn: object[0]
|
|
Error executing action [attempt 2 of 3]. Reason: Unauthorized. Will retry after 4000ms
|
|
warn: object[0]
|
|
Error executing action [attempt 3 of 3]. Reason: Unauthorized. Will retry after 8000ms
|
|
fail: object[0]
|
|
Error executing request, max retry count reached. Reason: Unauthorized
|
|
fail: object[0]
|
|
Function call fail during pipeline step 0: QASkill.Question
|
|
info: object[0]
|
|
Answer: Error: AccessDenied: The request is not authorized, HTTP status: Unauthorized
|
|
info: object[0]
|
|
======= DefaultHttpRetryConfig [MaxRetryCount = 3, UseExponentialBackoff = true] =======
|
|
info: object[0]
|
|
Question: How popular is Polly library?
|
|
warn: object[0]
|
|
Error executing action [attempt 1 of 3]. Reason: Unauthorized. Will retry after 2000ms
|
|
warn: object[0]
|
|
Error executing action [attempt 2 of 3]. Reason: Unauthorized. Will retry after 4000ms
|
|
warn: object[0]
|
|
Error executing action [attempt 3 of 3]. Reason: Unauthorized. Will retry after 8000ms
|
|
fail: object[0]
|
|
Error executing request, max retry count reached. Reason: Unauthorized
|
|
fail: object[0]
|
|
Function call fail during pipeline step 0: QASkill.Question
|
|
info: object[0]
|
|
Answer: Error: AccessDenied: The request is not authorized, HTTP status: Unauthorized
|
|
*/
|