diff --git a/webapi/Controllers/ChatController.cs b/webapi/Controllers/ChatController.cs index a661ebb0d..be735bfad 100644 --- a/webapi/Controllers/ChatController.cs +++ b/webapi/Controllers/ChatController.cs @@ -27,6 +27,7 @@ using Microsoft.Graph; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.AI; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Orchestration; using Microsoft.SemanticKernel.SkillDefinition; using Microsoft.SemanticKernel.Skills.MsGraph; @@ -127,9 +128,9 @@ public async Task ChatAsync( { function = kernel.Skills.GetFunction(ChatSkillName, ChatFunctionName); } - catch (KernelException ke) + catch (SKException ex) { - this._logger.LogError("Failed to find {0}/{1} on server: {2}", ChatSkillName, ChatFunctionName, ke); + this._logger.LogError("Failed to find {0}/{1} on server: {2}", ChatSkillName, ChatFunctionName, ex); return this.NotFound($"Failed to find {ChatSkillName}/{ChatFunctionName} on server"); } diff --git a/webapi/CopilotChatWebApi.csproj b/webapi/CopilotChatWebApi.csproj index d299e57ea..2daa6c0c3 100644 --- a/webapi/CopilotChatWebApi.csproj +++ b/webapi/CopilotChatWebApi.csproj @@ -12,16 +12,16 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/webapi/Extensions/SemanticKernelExtensions.cs b/webapi/Extensions/SemanticKernelExtensions.cs index 376aa39ec..acdcf391f 100644 --- a/webapi/Extensions/SemanticKernelExtensions.cs +++ b/webapi/Extensions/SemanticKernelExtensions.cs @@ -21,10 +21,10 @@ using Microsoft.SemanticKernel.Connectors.Memory.Chroma; using Microsoft.SemanticKernel.Connectors.Memory.Postgres; using Microsoft.SemanticKernel.Connectors.Memory.Qdrant; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory; using Microsoft.SemanticKernel.Orchestration; using Microsoft.SemanticKernel.Skills.Core; -using Microsoft.SemanticKernel.TemplateEngine; using Npgsql; using Pgvector.Npgsql; using static CopilotChat.WebApi.Options.MemoryStoreOptions; @@ -50,7 +50,7 @@ internal static IServiceCollection AddSemanticKernelServices(this IServiceCollec services.AddScoped(sp => { IKernel kernel = Kernel.Builder - .WithLogger(sp.GetRequiredService>()) + .WithLoggerFactory(sp.GetRequiredService()) .WithMemory(sp.GetRequiredService()) .WithCompletionBackend(sp.GetRequiredService>().Value) .WithEmbeddingBackend(sp.GetRequiredService>().Value) @@ -81,12 +81,12 @@ public static IServiceCollection AddPlannerServices(this IServiceCollection serv services.AddScoped(sp => { IKernel plannerKernel = Kernel.Builder - .WithLogger(sp.GetRequiredService>()) + .WithLoggerFactory(sp.GetRequiredService()) .WithMemory(sp.GetRequiredService()) // TODO: [sk Issue #2046] verify planner has AI service configured .WithPlannerBackend(sp.GetRequiredService>().Value) .Build(); - return new CopilotChatPlanner(plannerKernel, plannerOptions?.Value); + return new CopilotChatPlanner(plannerKernel, plannerOptions?.Value, sp.GetRequiredService>()); }); // Register Planner skills (AI plugins) here. @@ -123,7 +123,8 @@ public static void ThrowIfFailed(this SKContext context) { if (context.ErrorOccurred) { - context.Logger.LogError(context.LastException, "{0}", context.LastException?.Message); + var logger = context.LoggerFactory.CreateLogger(nameof(SKContext)); + logger.LogError(context.LastException, "{0}", context.LastException?.Message); throw context.LastException!; } } @@ -149,9 +150,10 @@ private static Task RegisterSkillsAsync(IServiceProvider sp, IKernel kernel) { kernel.ImportSemanticSkillFromDirectory(options.SemanticSkillsDirectory, Path.GetFileName(subDir)!); } - catch (TemplateException e) + catch (SKException ex) { - kernel.Logger.LogError("Could not load skill from {Directory}: {Message}", subDir, e.Message); + var logger = kernel.LoggerFactory.CreateLogger(nameof(Kernel)); + logger.LogError("Could not load skill from {Directory}: {Message}", subDir, ex.Message); } } } @@ -193,7 +195,7 @@ private static void AddSemanticTextMemory(this IServiceCollection services) httpClient: httpClient, config.Qdrant.VectorSize, endPointBuilder.ToString(), - logger: sp.GetRequiredService>() + loggerFactory: sp.GetRequiredService() ); }); break; @@ -225,7 +227,7 @@ private static void AddSemanticTextMemory(this IServiceCollection services) return new ChromaMemoryStore( httpClient: httpClient, endpoint: endPointBuilder.ToString(), - logger: sp.GetRequiredService>() + loggerFactory: sp.GetRequiredService() ); }); break; @@ -256,7 +258,7 @@ private static void AddSemanticTextMemory(this IServiceCollection services) services.AddScoped(sp => new SemanticTextMemory( sp.GetRequiredService(), sp.GetRequiredService>().Value - .ToTextEmbeddingsService(logger: sp.GetRequiredService>()))); + .ToTextEmbeddingsService(loggerFactory: sp.GetRequiredService()))); } /// @@ -323,17 +325,17 @@ private static KernelBuilder WithPlannerBackend(this KernelBuilder kernelBuilder /// /// The service configuration /// Custom for HTTP requests. - /// Application logger + /// Custom for logging. private static ITextEmbeddingGeneration ToTextEmbeddingsService(this AIServiceOptions options, HttpClient? httpClient = null, - ILogger? logger = null) + ILoggerFactory? loggerFactory = null) { return options.Type switch { AIServiceOptions.AIServiceType.AzureOpenAI - => new AzureTextEmbeddingGeneration(options.Models.Embedding, options.Endpoint, options.Key, httpClient: httpClient, logger: logger), + => new AzureTextEmbeddingGeneration(options.Models.Embedding, options.Endpoint, options.Key, httpClient: httpClient, loggerFactory: loggerFactory), AIServiceOptions.AIServiceType.OpenAI - => new OpenAITextEmbeddingGeneration(options.Models.Embedding, options.Key, httpClient: httpClient, logger: logger), + => new OpenAITextEmbeddingGeneration(options.Models.Embedding, options.Key, httpClient: httpClient, loggerFactory: loggerFactory), _ => throw new ArgumentException("Invalid AIService value in embeddings backend settings"), }; diff --git a/webapi/Options/PlannerOptions.cs b/webapi/Options/PlannerOptions.cs index 7d9c79d3f..b1deb81fd 100644 --- a/webapi/Options/PlannerOptions.cs +++ b/webapi/Options/PlannerOptions.cs @@ -11,20 +11,24 @@ namespace CopilotChat.WebApi.Options; public class PlannerOptions { /// - /// Whether to allow missing functions in plan on creation. If allowed, proposed plan will be sanitized of no-op functions. - /// Functions are considered missing if they're not available in the planner's kernel's context. + /// Options to handle planner errors. /// - public class MissingFunctionErrorOptions + public class ErrorOptions { /// - /// Flag to indicate if skips are allowed on MissingFunction error - /// If set to true, the plan will be created with missing functions as no-op steps that are filtered from the final proposed plan. - /// If this is set to false, the plan creation will fail if any functions are missing. + /// Whether to allow retries on planner errors. /// public bool AllowRetries { get; set; } = true; + // + // Whether to allow missing functions in the sequential plan on creation. If set to true, the + // plan will be created with missing functions as no-op steps. If set to false (default), + // the plan creation will fail if any functions are missing. + // + public bool AllowMissingFunctions { get; set; } = true; + /// - /// Max retries allowed on MissingFunctionsError. + /// Max retries allowed. /// [Range(1, 5)] public int MaxRetriesAllowed { get; set; } = 3; @@ -45,14 +49,9 @@ public class MissingFunctionErrorOptions public double? RelevancyThreshold { get; set; } = 0; /// - /// Options on how to handle missing functions in plan on creation. - /// - public MissingFunctionErrorOptions MissingFunctionError { get; set; } = new MissingFunctionErrorOptions(); - - /// - /// Whether to retry plan creation if LLM returned response that doesn't contain valid plan (e.g., invalid XML or JSON, contains missing function, etc.). + /// Options on how to handle planner errors. /// - public bool AllowRetriesOnInvalidPlan { get; set; } = true; + public ErrorOptions ErrorHandling { get; set; } = new ErrorOptions(); /// /// The configuration for the stepwise planner. diff --git a/webapi/Skills/ChatSkills/ChatSkill.cs b/webapi/Skills/ChatSkills/ChatSkill.cs index 9fbd70748..f4314796b 100644 --- a/webapi/Skills/ChatSkills/ChatSkill.cs +++ b/webapi/Skills/ChatSkills/ChatSkill.cs @@ -109,14 +109,16 @@ public ChatSkill( this._semanticChatMemorySkill = new SemanticChatMemorySkill( promptOptions, - chatSessionRepository, logger); + chatSessionRepository, + logger); this._documentMemorySkill = new DocumentMemorySkill( promptOptions, documentImportOptions, logger); this._externalInformationSkill = new ExternalInformationSkill( promptOptions, - planner); + planner, + logger); this._contentSafety = contentSafety; } @@ -159,7 +161,7 @@ public async Task ExtractUserIntentAsync(SKContext context, Cancellation ); // Get token usage from ChatCompletion result and add to context - TokenUtilities.GetFunctionTokenUsage(result, context, "SystemIntentExtraction"); + TokenUtilities.GetFunctionTokenUsage(result, context, this._logger, "SystemIntentExtraction"); result.ThrowIfFailed(); @@ -203,7 +205,7 @@ public async Task ExtractAudienceAsync(SKContext context, CancellationTo ); // Get token usage from ChatCompletion result and add to context - TokenUtilities.GetFunctionTokenUsage(result, context, "SystemAudienceExtraction"); + TokenUtilities.GetFunctionTokenUsage(result, context, this._logger, "SystemAudienceExtraction"); result.ThrowIfFailed(); @@ -323,7 +325,7 @@ public async Task ChatAsync( } else { - context.Logger.LogWarning("ChatSkill token usage unknown. Ensure token management has been implemented correctly."); + this._logger.LogWarning("ChatSkill token usage unknown. Ensure token management has been implemented correctly."); } return context; @@ -443,7 +445,7 @@ private async Task GetChatResponseAsync(string chatId, string userI var promptView = new BotResponsePrompt(renderedPrompt, this._promptOptions.SystemDescription, this._promptOptions.SystemResponse, audience, userIntent, chatMemories, documentMemories, plannerDetails, chatHistory, systemChatContinuation); // Calculate token usage of prompt template - chatContext.Variables.Set(TokenUtilities.GetFunctionKey(chatContext.Logger, "SystemMetaPrompt")!, TokenUtilities.TokenCount(renderedPrompt).ToString(CultureInfo.InvariantCulture)); + chatContext.Variables.Set(TokenUtilities.GetFunctionKey(this._logger, "SystemMetaPrompt")!, TokenUtilities.TokenCount(renderedPrompt).ToString(CultureInfo.InvariantCulture)); chatContext.ThrowIfFailed(); @@ -485,7 +487,7 @@ private async Task GetAudienceAsync(SKContext context, CancellationToken var audience = await this.ExtractAudienceAsync(audienceContext, cancellationToken); // Copy token usage into original chat context - var functionKey = TokenUtilities.GetFunctionKey(context.Logger, "SystemAudienceExtraction")!; + var functionKey = TokenUtilities.GetFunctionKey(this._logger, "SystemAudienceExtraction")!; if (audienceContext.Variables.TryGetValue(functionKey, out string? tokenUsage)) { context.Variables.Set(functionKey, tokenUsage); @@ -512,7 +514,7 @@ private async Task GetUserIntentAsync(SKContext context, CancellationTok userIntent = await this.ExtractUserIntentAsync(intentContext, cancellationToken); // Copy token usage into original chat context - var functionKey = TokenUtilities.GetFunctionKey(context.Logger, "SystemIntentExtraction")!; + var functionKey = TokenUtilities.GetFunctionKey(this._logger, "SystemIntentExtraction")!; if (intentContext.Variables.TryGetValue(functionKey!, out string? tokenUsage)) { context.Variables.Set(functionKey!, tokenUsage); diff --git a/webapi/Skills/ChatSkills/CopilotChatPlanner.cs b/webapi/Skills/ChatSkills/CopilotChatPlanner.cs index 937aae04e..bf754d73a 100644 --- a/webapi/Skills/ChatSkills/CopilotChatPlanner.cs +++ b/webapi/Skills/ChatSkills/CopilotChatPlanner.cs @@ -11,6 +11,7 @@ using CopilotChat.WebApi.Options; using Microsoft.Extensions.Logging; using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Orchestration; using Microsoft.SemanticKernel.Planning; using Microsoft.SemanticKernel.Planning.Sequential; @@ -23,6 +24,11 @@ namespace CopilotChat.WebApi.Skills.ChatSkills; /// public class CopilotChatPlanner { + /// + /// High level logger. + /// + private readonly ILogger _logger; + /// /// The planner's kernel. /// @@ -62,10 +68,11 @@ public class CopilotChatPlanner /// Initializes a new instance of the class. /// /// The planner's kernel. - public CopilotChatPlanner(IKernel plannerKernel, PlannerOptions? plannerOptions) + public CopilotChatPlanner(IKernel plannerKernel, PlannerOptions? plannerOptions, ILogger logger) { this.Kernel = plannerKernel; this._plannerOptions = plannerOptions; + this._logger = logger; } /// @@ -97,7 +104,7 @@ public async Task CreatePlanAsync(string goal, ILogger logger, Cancellatio { RelevancyThreshold = this._plannerOptions?.RelevancyThreshold, // Allow plan to be created with missing functions - AllowMissingFunctions = this._plannerOptions?.MissingFunctionError.AllowRetries ?? false + AllowMissingFunctions = this._plannerOptions?.ErrorHandling.AllowMissingFunctions ?? false } ).CreatePlanAsync(goal, cancellationToken); break; @@ -106,13 +113,13 @@ public async Task CreatePlanAsync(string goal, ILogger logger, Cancellatio break; } } - catch (PlanningException e) when (e.ErrorCode == PlanningException.ErrorCodes.CreatePlanError && e.Message.Contains("Not possible to create plan for goal with available functions", StringComparison.InvariantCulture)) + catch (SKException) { // No relevant functions are available - return an empty plan. return new Plan(goal); } - return this._plannerOptions!.MissingFunctionError.AllowRetries ? this.SanitizePlan(plan, plannerFunctionsView, logger) : plan; + return this._plannerOptions!.ErrorHandling.AllowMissingFunctions ? this.SanitizePlan(plan, plannerFunctionsView, logger) : plan; } /// @@ -147,7 +154,7 @@ public async Task RunStepwisePlannerAsync(string goal, SKContext cont } catch (Exception e) { - context.Logger.LogError(e, "Error running stepwise planner"); + this._logger.LogError(e, "Error running stepwise planner"); throw; } } diff --git a/webapi/Skills/ChatSkills/DocumentMemorySkill.cs b/webapi/Skills/ChatSkills/DocumentMemorySkill.cs index 268a5d832..c041167c8 100644 --- a/webapi/Skills/ChatSkills/DocumentMemorySkill.cs +++ b/webapi/Skills/ChatSkills/DocumentMemorySkill.cs @@ -40,7 +40,7 @@ public class DocumentMemorySkill public DocumentMemorySkill( IOptions promptOptions, IOptions documentImportOptions, - Microsoft.Extensions.Logging.ILogger logger) + ILogger logger) { this._logger = logger; this._promptOptions = promptOptions.Value; diff --git a/webapi/Skills/ChatSkills/ExternalInformationSkill.cs b/webapi/Skills/ChatSkills/ExternalInformationSkill.cs index 543a61d46..f37e2587e 100644 --- a/webapi/Skills/ChatSkills/ExternalInformationSkill.cs +++ b/webapi/Skills/ChatSkills/ExternalInformationSkill.cs @@ -16,7 +16,6 @@ using CopilotChat.WebApi.Skills.OpenApiPlugins.JiraPlugin.Model; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Orchestration; using Microsoft.SemanticKernel.Planning; using Microsoft.SemanticKernel.SkillDefinition; @@ -28,6 +27,11 @@ namespace CopilotChat.WebApi.Skills.ChatSkills; /// public class ExternalInformationSkill { + /// + /// High level logger. + /// + private readonly ILogger _logger; + /// /// Prompt settings. /// @@ -73,10 +77,12 @@ public class ExternalInformationSkill /// public ExternalInformationSkill( IOptions promptOptions, - CopilotChatPlanner planner) + CopilotChatPlanner planner, + ILogger logger) { this._promptOptions = promptOptions.Value; this._planner = planner; + this._logger = logger; } /// @@ -121,7 +127,7 @@ public async Task AcquireExternalInformationAsync( string planJson = JsonSerializer.Serialize(deserializedPlan.Plan); // Reload the plan with the planner's kernel so // it has full context to be executed - var newPlanContext = new SKContext(null, this._planner.Kernel.Skills, this._planner.Kernel.Logger); + var newPlanContext = new SKContext(null, this._planner.Kernel.Skills, this._planner.Kernel.LoggerFactory); var plan = Plan.FromJson(planJson, newPlanContext); // Invoke plan @@ -156,25 +162,20 @@ public async Task AcquireExternalInformationAsync( Plan? plan = null; // Use default planner options if planner options are null. var plannerOptions = this._planner.PlannerOptions ?? new PlannerOptions(); - int retriesAvail = plannerOptions.MissingFunctionError.AllowRetries - ? plannerOptions.MissingFunctionError.MaxRetriesAllowed // Will always be at least 1 - : plannerOptions.AllowRetriesOnInvalidPlan ? 1 : 0; + int retriesAvail = plannerOptions.ErrorHandling.AllowRetries + ? plannerOptions.ErrorHandling.MaxRetriesAllowed : 0; do { // TODO: [Issue #2256] Remove InvalidPlan retry logic once Core team stabilizes planner try { - plan = await this._planner.CreatePlanAsync(goal, context.Logger, cancellationToken); + plan = await this._planner.CreatePlanAsync(goal, this._logger, cancellationToken); } - catch (Exception e) when (this.IsRetriableError(e)) + catch (Exception e) { - if (retriesAvail > 0) + if (--retriesAvail >= 0) { - // PlanningExceptions are limited to one (1) pass as built-in stabilization. Retry limit of MissingFunctionErrors is user-configured. - retriesAvail = e is PlanningException ? 0 : retriesAvail--; - - // Retry plan creation if LLM returned response that doesn't contain valid plan (invalid XML or JSON). - context.Logger.LogWarning("Retrying CreatePlan on error: {0}", e.Message); + this._logger.LogWarning("Retrying CreatePlan on error: {0}", e.Message); continue; } throw; @@ -207,25 +208,6 @@ public async Task AcquireExternalInformationAsync( #region Private - /// - /// Retry on plan creation error if: - /// 1. PlannerOptions.AllowRetriesOnInvalidPlan is true and exception contains error code InvalidPlan. - /// 2. PlannerOptions.MissingFunctionError.AllowRetries is true and exception contains error code FunctionNotAvailable. - /// - private bool IsRetriableError(Exception e) - { - var retryOnInvalidPlanError = e is PlanningException - && ((e as PlanningException)!.ErrorCode == PlanningException.ErrorCodes.InvalidPlan - || (e.InnerException as PlanningException)!.ErrorCode == PlanningException.ErrorCodes.InvalidPlan) - && this._planner.PlannerOptions!.AllowRetriesOnInvalidPlan; - - var retryOnMissingFunctionError = e is KernelException - && (e as KernelException)!.ErrorCode == KernelException.ErrorCodes.FunctionNotAvailable - && this._planner.PlannerOptions!.MissingFunctionError.AllowRetries; - - return retryOnMissingFunctionError || retryOnInvalidPlanError; - } - /// /// Merge any variables from context into plan parameters. /// @@ -266,11 +248,11 @@ private bool TryExtractJsonFromOpenApiPlanResult(SKContext context, string openA } catch (JsonException) { - context.Logger.LogDebug("Unable to extract JSON from planner response, it is likely not from an OpenAPI skill."); + this._logger.LogDebug("Unable to extract JSON from planner response, it is likely not from an OpenAPI skill."); } catch (InvalidOperationException) { - context.Logger.LogDebug("Unable to extract JSON from planner response, it may already be proper JSON."); + this._logger.LogDebug("Unable to extract JSON from planner response, it may already be proper JSON."); } json = string.Empty; diff --git a/webapi/Skills/ChatSkills/SemanticChatMemoryExtractor.cs b/webapi/Skills/ChatSkills/SemanticChatMemoryExtractor.cs index ee0cc2c11..8e37ea8c6 100644 --- a/webapi/Skills/ChatSkills/SemanticChatMemoryExtractor.cs +++ b/webapi/Skills/ChatSkills/SemanticChatMemoryExtractor.cs @@ -53,7 +53,8 @@ internal static async Task ExtractSemanticChatMemoryAsync( memoryName, kernel, context, - options + options, + logger ); foreach (var item in semanticMemory.Items) { @@ -64,7 +65,7 @@ internal static async Task ExtractSemanticChatMemoryAsync( { // Skip semantic memory extraction for this item if it fails. // We cannot rely on the model to response with perfect Json each time. - context.Logger.LogInformation("Unable to extract semantic memory for {0}: {1}. Continuing...", memoryName, ex.Message); + logger.LogInformation("Unable to extract semantic memory for {0}: {1}. Continuing...", memoryName, ex.Message); continue; } } @@ -77,12 +78,14 @@ internal static async Task ExtractSemanticChatMemoryAsync( /// The semantic kernel. /// The SKContext /// The prompts options. + /// The logger. /// A SemanticChatMemory object. internal static async Task ExtractCognitiveMemoryAsync( string memoryName, IKernel kernel, SKContext context, - PromptsOptions options) + PromptsOptions options, + ILogger logger) { if (!options.MemoryMap.TryGetValue(memoryName, out var memoryPrompt)) { @@ -110,7 +113,7 @@ internal static async Task ExtractCognitiveMemoryAsync( // Get token usage from ChatCompletion result and add to context // Since there are multiple memory types, total token usage is calculated by cumulating the token usage of each memory type. - TokenUtilities.GetFunctionTokenUsage(result, context, $"SystemCognitive_{memoryName}"); + TokenUtilities.GetFunctionTokenUsage(result, context, logger, $"SystemCognitive_{memoryName}"); SemanticChatMemory memory = SemanticChatMemory.FromJson(result.ToString()); return memory; diff --git a/webapi/Skills/TokenUtilities.cs b/webapi/Skills/TokenUtilities.cs index 52fff0478..041e7b7be 100644 --- a/webapi/Skills/TokenUtilities.cs +++ b/webapi/Skills/TokenUtilities.cs @@ -62,11 +62,12 @@ internal static Dictionary EmptyTokenUsages() /// /// Result context from chat model /// Context maintained during response generation. + /// The logger instance to use for logging errors. /// Name of the function that invoked the chat completion. /// true if token usage is found in result context; otherwise, false. - internal static void GetFunctionTokenUsage(SKContext result, SKContext chatContext, string? functionName = null) + internal static void GetFunctionTokenUsage(SKContext result, SKContext chatContext, ILogger logger, string? functionName = null) { - var functionKey = GetFunctionKey(chatContext.Logger, functionName); + var functionKey = GetFunctionKey(logger, functionName); if (functionKey == null) { return; @@ -74,7 +75,7 @@ internal static void GetFunctionTokenUsage(SKContext result, SKContext chatConte if (result.ModelResults == null || result.ModelResults.Count == 0) { - chatContext.Logger.LogError("Unable to determine token usage for {0}", functionKey); + logger.LogError("Unable to determine token usage for {0}", functionKey); return; } diff --git a/webapi/appsettings.json b/webapi/appsettings.json index a874cda08..ccd486a7b 100644 --- a/webapi/appsettings.json +++ b/webapi/appsettings.json @@ -63,15 +63,16 @@ // Set RelevancyThreshold to a value between 0 and 1 if using the SequentialPlanner or Stepwise planner with gpt-3.5-turbo. // Ignored when Planner:Type is "Action" "RelevancyThreshold": "0.25", - // Whether to allow missing functions in the plan on creation then sanitize output. Functions are considered missing if they're not available in the planner's kernel's context. - // If set to true, the plan will be created with missing functions as no-op steps that are filtered from the final proposed plan. - // If this is set to false, the plan creation will fail if any functions are missing. - "MissingFunctionsError": { - "AllowSkips": "true", - "MaxRetriesAllowed": "3" // Max retries allowed on MissingFunctionsError. If set to 0, no retries will be attempted. + // Configuration for the error handling and retry logic. + // - Set AllowRetries to "true" to enable retries. + // - Set AllowMissingFunctions to "true" to allow missing functions in the sequential plan on creation. + // The plan will be created with missing functions as no-op steps. + // - Set MaxRetriesAllowed to the maximum number of retries allowed. If set to 0, no retries will be attempted. + "ErrorHandling": { + "AllowRetries": "true", + "AllowMissingFunctions": "true", + "MaxRetriesAllowed": "3" // Max retries allowed. If set to 0, no retries will be attempted. }, - // Whether to retry plan creation if LLM returned response with invalid plan. - "AllowRetriesOnInvalidPlan": "true", "StepwisePlannerConfig": { "MaxTokens": "2048", "MaxIterations": "15",