From 562354494939d8af53ea0fbfd63e3dee6394cc10 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 22 Nov 2023 17:24:06 -0500 Subject: [PATCH] .Net: Delete IPlanner and associated WithInstrumentation (#3601) Not all planner have the same shape, and there's no current demonstrated need to use them interchangeably via an abstraction. IPlanner existed temporarily to provide a hook for WithInstrumentation, but it's not desirable. Instead, all the planners just get the instrumentation abilities built in. Contributes to https://github.com/microsoft/semantic-kernel/issues/3490 Co-authored-by: Mark Wallace <127216156+markwallace-microsoft@users.noreply.github.com> --- .../ApplicationInsightsExample/Program.cs | 19 ++-- .../Example51_StepwisePlanner.cs | 2 +- .../StepwisePlanner/StepwisePlannerTests.cs | 10 +- .../Planners.Core/Action/ActionPlanner.cs | 20 +++- .../Handlebars/HandlebarsPlanner.cs | 27 ++++-- .../Planners.Core/PlannerInstrumentation.cs | 77 ++++++++++++++++ .../Sequential/SequentialPlanner.cs | 24 ++++- .../Planners.Core/Stepwise/StepwisePlanner.cs | 46 +++++++--- .../SemanticKernel.Core/Planning/IPlanner.cs | 23 ----- .../Planning/InstrumentedPlanner.cs | 92 ------------------- .../Planning/PlannerExtensions.cs | 21 ----- 11 files changed, 180 insertions(+), 181 deletions(-) create mode 100644 dotnet/src/Planners/Planners.Core/PlannerInstrumentation.cs delete mode 100644 dotnet/src/SemanticKernel.Core/Planning/IPlanner.cs delete mode 100644 dotnet/src/SemanticKernel.Core/Planning/InstrumentedPlanner.cs delete mode 100644 dotnet/src/SemanticKernel.Core/Planning/PlannerExtensions.cs diff --git a/dotnet/samples/ApplicationInsightsExample/Program.cs b/dotnet/samples/ApplicationInsightsExample/Program.cs index f77237a6a31b..272dbc7440cc 100644 --- a/dotnet/samples/ApplicationInsightsExample/Program.cs +++ b/dotnet/samples/ApplicationInsightsExample/Program.cs @@ -51,7 +51,7 @@ public static async Task Main() ConfigureTracing(activityListener, telemetryClient); var kernel = GetKernel(loggerFactory); - var planner = GetSequentialPlanner(kernel, loggerFactory); + var planner = GetSequentialPlanner(kernel); try { @@ -128,26 +128,23 @@ private static Kernel GetKernel(ILoggerFactory loggerFactory) return kernel; } - private static IPlanner GetSequentialPlanner( + private static SequentialPlanner GetSequentialPlanner( Kernel kernel, - ILoggerFactory loggerFactory, int maxTokens = 1024) { var plannerConfig = new SequentialPlannerConfig { MaxTokens = maxTokens }; - return new SequentialPlanner(kernel, plannerConfig).WithInstrumentation(loggerFactory); + return new SequentialPlanner(kernel, plannerConfig); } - private static IPlanner GetActionPlanner( - Kernel kernel, - ILoggerFactory loggerFactory) + private static ActionPlanner GetActionPlanner( + Kernel kernel) { - return new ActionPlanner(kernel).WithInstrumentation(loggerFactory); + return new ActionPlanner(kernel); } - private static IPlanner GetStepwisePlanner( + private static StepwisePlanner GetStepwisePlanner( Kernel kernel, - ILoggerFactory loggerFactory, int minIterationTimeMs = 1500, int maxTokens = 2000) { @@ -157,7 +154,7 @@ private static IPlanner GetStepwisePlanner( MaxTokens = maxTokens }; - return new StepwisePlanner(kernel, plannerConfig).WithInstrumentation(loggerFactory); + return new StepwisePlanner(kernel, plannerConfig); } /// diff --git a/dotnet/samples/KernelSyntaxExamples/Example51_StepwisePlanner.cs b/dotnet/samples/KernelSyntaxExamples/Example51_StepwisePlanner.cs index 8061a23bceaf..8ac7048e9b4f 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example51_StepwisePlanner.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example51_StepwisePlanner.cs @@ -139,7 +139,7 @@ private static async Task RunWithQuestionAsync(Kernel kernel, ExecutionResult cu try { StepwisePlanner planner = new(kernel: kernel, config: plannerConfig); - var plan = await planner.CreatePlanAsync(question); + var plan = planner.CreatePlan(question); var functionResult = await kernel.RunAsync(plan); diff --git a/dotnet/src/IntegrationTests/Planners/StepwisePlanner/StepwisePlannerTests.cs b/dotnet/src/IntegrationTests/Planners/StepwisePlanner/StepwisePlannerTests.cs index 349d740a0975..321a664f9d38 100644 --- a/dotnet/src/IntegrationTests/Planners/StepwisePlanner/StepwisePlannerTests.cs +++ b/dotnet/src/IntegrationTests/Planners/StepwisePlanner/StepwisePlannerTests.cs @@ -45,7 +45,7 @@ public StepwisePlannerTests(ITestOutputHelper output) [Theory] [InlineData(false, "Who is the current president of the United States? What is his current age divided by 2", "ExecutePlan", "StepwisePlanner")] [InlineData(true, "Who is the current president of the United States? What is his current age divided by 2", "ExecutePlan", "StepwisePlanner")] - public async Task CanCreateStepwisePlanAsync(bool useChatModel, string prompt, string expectedFunction, string expectedPlugin) + public void CanCreateStepwisePlanAsync(bool useChatModel, string prompt, string expectedFunction, string expectedPlugin) { // Arrange bool useEmbeddings = false; @@ -58,7 +58,7 @@ public async Task CanCreateStepwisePlanAsync(bool useChatModel, string prompt, s var planner = new StepwisePlanner(kernel, new() { MaxIterations = 10 }); // Act - var plan = await planner.CreatePlanAsync(prompt); + var plan = planner.CreatePlan(prompt); // Assert Assert.Empty(plan.Steps); @@ -84,7 +84,7 @@ public async Task CanExecuteStepwisePlanAsync(bool useChatModel, string prompt, var planner = new StepwisePlanner(kernel, new() { MaxIterations = 10 }); // Act - var plan = await planner.CreatePlanAsync(prompt); + var plan = planner.CreatePlan(prompt); var planResult = await plan.InvokeAsync(kernel); var result = planResult.GetValue(); @@ -113,7 +113,7 @@ public async Task ExecutePlanFailsWithTooManyFunctionsAsync() var planner = new StepwisePlanner(kernel, new() { MaxTokens = 1000 }); // Act - var plan = await planner.CreatePlanAsync("I need to buy a new brush for my cat. Can you show me options?"); + var plan = planner.CreatePlan("I need to buy a new brush for my cat. Can you show me options?"); // Assert var ex = await Assert.ThrowsAsync(async () => await kernel.RunAsync(plan)); @@ -131,7 +131,7 @@ public async Task ExecutePlanSucceedsWithAlmostTooManyFunctionsAsync() var planner = new StepwisePlanner(kernel); // Act - var plan = await planner.CreatePlanAsync("I need to buy a new brush for my cat. Can you show me options?"); + var plan = planner.CreatePlan("I need to buy a new brush for my cat. Can you show me options?"); var functionResult = await kernel.RunAsync(plan); var result = functionResult.GetValue(); diff --git a/dotnet/src/Planners/Planners.Core/Action/ActionPlanner.cs b/dotnet/src/Planners/Planners.Core/Action/ActionPlanner.cs index f105ef2b5e21..cc9aedefae8e 100644 --- a/dotnet/src/Planners/Planners.Core/Action/ActionPlanner.cs +++ b/dotnet/src/Planners/Planners.Core/Action/ActionPlanner.cs @@ -28,7 +28,7 @@ namespace Microsoft.SemanticKernel.Planning; /// The rationale is currently available only in the prompt, we might include it in /// the Plan object in future. /// -public sealed class ActionPlanner : IPlanner +public sealed class ActionPlanner { private const string StopSequence = "#END-OF-PLAN"; private const string PluginName = "this"; @@ -92,11 +92,25 @@ public ActionPlanner( this._logger = this._kernel.LoggerFactory.CreateLogger(this.GetType()); } - /// - public async Task CreatePlanAsync(string goal, CancellationToken cancellationToken = default) + /// Creates a plan for the specified goal. + /// The goal for which a plan should be created. + /// The to monitor for cancellation requests. The default is . + /// The created plan. + /// is null. + /// is empty or entirely composed of whitespace. + /// A plan could not be created. + public Task CreatePlanAsync(string goal, CancellationToken cancellationToken = default) { Verify.NotNullOrWhiteSpace(goal); + return PlannerInstrumentation.CreatePlanAsync( + static (ActionPlanner planner, string goal, CancellationToken cancellationToken) => planner.CreatePlanCoreAsync(goal, cancellationToken), + static (Plan plan) => plan.ToSafePlanString(), + this, goal, this._logger, cancellationToken); + } + + private async Task CreatePlanCoreAsync(string goal, CancellationToken cancellationToken) + { this._context.Variables.Update(goal); FunctionResult result = await this._plannerFunction.InvokeAsync(this._kernel, this._context, cancellationToken: cancellationToken).ConfigureAwait(false); diff --git a/dotnet/src/Planners/Planners.Core/Handlebars/HandlebarsPlanner.cs b/dotnet/src/Planners/Planners.Core/Handlebars/HandlebarsPlanner.cs index ab366779e8a1..d9ef41d84dd3 100644 --- a/dotnet/src/Planners/Planners.Core/Handlebars/HandlebarsPlanner.cs +++ b/dotnet/src/Planners/Planners.Core/Handlebars/HandlebarsPlanner.cs @@ -7,6 +7,7 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Microsoft.SemanticKernel.AI.ChatCompletion; namespace Microsoft.SemanticKernel.Planning.Handlebars; @@ -27,6 +28,7 @@ public sealed class HandlebarsPlanner public Stopwatch Stopwatch { get; } = new(); private readonly Kernel _kernel; + private readonly ILogger _logger; private readonly HandlebarsPlannerConfig _config; @@ -41,16 +43,27 @@ public HandlebarsPlanner(Kernel kernel, HandlebarsPlannerConfig? config = defaul { this._kernel = kernel; this._config = config ?? new HandlebarsPlannerConfig(); + this._logger = kernel.LoggerFactory.CreateLogger(this.GetType()); } - /// - /// Create a plan for a goal. - /// - /// The goal to create a plan for. + /// Creates a plan for the specified goal. + /// The goal for which a plan should be created. /// The to monitor for cancellation requests. The default is . - /// The plan. - /// Thrown when the plan cannot be created. - public async Task CreatePlanAsync(string goal, CancellationToken cancellationToken = default) + /// The created plan. + /// is null. + /// is empty or entirely composed of whitespace. + /// A plan could not be created. + public Task CreatePlanAsync(string goal, CancellationToken cancellationToken = default) + { + Verify.NotNullOrWhiteSpace(goal); + + return PlannerInstrumentation.CreatePlanAsync( + createPlanAsync: static (HandlebarsPlanner planner, string goal, CancellationToken cancellationToken) => planner.CreatePlanCoreAsync(goal, cancellationToken), + planToString: static (HandlebarsPlan plan) => plan.ToString(), + this, goal, this._logger, cancellationToken); + } + + private async Task CreatePlanCoreAsync(string goal, CancellationToken cancellationToken = default) { var availableFunctions = this.GetAvailableFunctionsManual(cancellationToken); var handlebarsTemplate = this.GetHandlebarsTemplate(this._kernel, goal, availableFunctions); diff --git a/dotnet/src/Planners/Planners.Core/PlannerInstrumentation.cs b/dotnet/src/Planners/Planners.Core/PlannerInstrumentation.cs new file mode 100644 index 000000000000..43fa2601cb96 --- /dev/null +++ b/dotnet/src/Planners/Planners.Core/PlannerInstrumentation.cs @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Diagnostics; +using System.Diagnostics.Metrics; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + +#pragma warning disable IDE0130 +// ReSharper disable once CheckNamespace - using planners namespace +namespace Microsoft.SemanticKernel.Planning; +#pragma warning restore IDE0130 + +/// Surrounds the invocation of a planner with logging and metrics. +internal static class PlannerInstrumentation +{ + /// for planning-related activities. + private static readonly ActivitySource s_activitySource = new("Microsoft.SemanticKernel.Planning"); + + /// for planner-related metrics. + private static readonly Meter s_meter = new("Microsoft.SemanticKernel.Planning"); + + /// to record plan creation duration. + private static readonly Histogram s_createPlanDuration = s_meter.CreateHistogram( + name: "sk.planning.create_plan.duration", + unit: "s", + description: "Duration time of plan creation."); + + /// Invokes the supplied delegate, surrounded by logging and metrics. + internal static async Task CreatePlanAsync( + Func> createPlanAsync, + Func planToString, + TPlanner planner, string goal, ILogger logger, CancellationToken cancellationToken) + where TPlanner : class + where TPlan : class + { + string plannerName = planner.GetType().FullName; + + using var _ = s_activitySource.StartActivity(plannerName); + + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace("Plan creation started. Goal: {Goal}", goal); // Sensitive data, logging as trace, disabled by default + } + else if (logger.IsEnabled(LogLevel.Information)) + { + logger.LogInformation("Plan creation started."); + } + + TagList tags = new() { { "sk.planner.name", plannerName } }; + long startingTimestamp = Stopwatch.GetTimestamp(); + try + { + var plan = await createPlanAsync(planner, goal, cancellationToken).ConfigureAwait(false); + + if (logger.IsEnabled(LogLevel.Information)) + { + logger.LogInformation("Plan created. Plan:\n{Plan}", planToString(plan)); + } + + return plan; + } + catch (Exception ex) + { + logger.LogError(ex, "Plan creation failed. Error: {Message}", ex.Message); + tags.Add("error.type", ex.GetType().FullName); + throw; + } + finally + { + TimeSpan duration = new((long)((Stopwatch.GetTimestamp() - startingTimestamp) * (10_000_000.0 / Stopwatch.Frequency))); + logger.LogInformation("Plan creation duration: {Duration}ms.", duration.TotalMilliseconds); + s_createPlanDuration.Record(duration.TotalSeconds, in tags); + } + } +} diff --git a/dotnet/src/Planners/Planners.Core/Sequential/SequentialPlanner.cs b/dotnet/src/Planners/Planners.Core/Sequential/SequentialPlanner.cs index 715e754f5980..938fd9786536 100644 --- a/dotnet/src/Planners/Planners.Core/Sequential/SequentialPlanner.cs +++ b/dotnet/src/Planners/Planners.Core/Sequential/SequentialPlanner.cs @@ -1,7 +1,9 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Microsoft.SemanticKernel.AI; using Microsoft.SemanticKernel.Orchestration; @@ -13,7 +15,7 @@ namespace Microsoft.SemanticKernel.Planning; /// /// A planner that uses semantic function to create a sequential plan. /// -public sealed class SequentialPlanner : IPlanner +public sealed class SequentialPlanner { private const string StopSequence = ""; private const string AvailableFunctionsKey = "available_functions"; @@ -51,13 +53,28 @@ public SequentialPlanner( }); this._kernel = kernel; + this._logger = this._kernel.LoggerFactory.CreateLogger(this.GetType()); } - /// - public async Task CreatePlanAsync(string goal, CancellationToken cancellationToken = default) + /// Creates a plan for the specified goal. + /// The goal for which a plan should be created. + /// The to monitor for cancellation requests. The default is . + /// The created plan. + /// is null. + /// is empty or entirely composed of whitespace. + /// A plan could not be created. + public Task CreatePlanAsync(string goal, CancellationToken cancellationToken = default) { Verify.NotNullOrWhiteSpace(goal); + return PlannerInstrumentation.CreatePlanAsync( + createPlanAsync: static (SequentialPlanner planner, string goal, CancellationToken cancellationToken) => planner.CreatePlanCoreAsync(goal, cancellationToken), + planToString: static (Plan plan) => plan.ToSafePlanString(), + this, goal, this._logger, cancellationToken); + } + + private async Task CreatePlanCoreAsync(string goal, CancellationToken cancellationToken) + { string relevantFunctionsManual = await this._kernel.Plugins.GetFunctionsManualAsync(this.Config, goal, null, cancellationToken).ConfigureAwait(false); ContextVariables vars = new(goal) @@ -99,6 +116,7 @@ public async Task CreatePlanAsync(string goal, CancellationToken cancellat private SequentialPlannerConfig Config { get; } private readonly Kernel _kernel; + private readonly ILogger _logger; /// /// the function flow semantic function, which takes a goal and creates an xml plan that can be executed diff --git a/dotnet/src/Planners/Planners.Core/Stepwise/StepwisePlanner.cs b/dotnet/src/Planners/Planners.Core/Stepwise/StepwisePlanner.cs index 6f8a4efa81f9..860c13758b4e 100644 --- a/dotnet/src/Planners/Planners.Core/Stepwise/StepwisePlanner.cs +++ b/dotnet/src/Planners/Planners.Core/Stepwise/StepwisePlanner.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; @@ -28,7 +29,7 @@ namespace Microsoft.SemanticKernel.Planning; /// /// An implementation of a Mrkl system as described in https://arxiv.org/pdf/2205.00445.pdf /// -public class StepwisePlanner : IPlanner +public class StepwisePlanner { /// /// Initialize a new instance of the class. @@ -67,22 +68,37 @@ public StepwisePlanner( this._logger = this._kernel.LoggerFactory.CreateLogger(this.GetType()); } - /// - public Task CreatePlanAsync(string goal, CancellationToken cancellationToken = default) + /// Creates a plan for the specified goal. + /// The goal for which a plan should be created. + /// The created plan. + /// is null. + /// is empty or entirely composed of whitespace. + /// A plan could not be created. + public Plan CreatePlan(string goal) { Verify.NotNullOrWhiteSpace(goal); - Plan plan = new(this._nativeFunctions["ExecutePlan"]); - plan.PluginName = RestrictedPluginName; - - plan.Parameters.Set("question", goal); - - plan.Outputs.Add("stepCount"); - plan.Outputs.Add("functionCount"); - plan.Outputs.Add("stepsTaken"); - plan.Outputs.Add("iterations"); - - return Task.FromResult(plan); + Task task = PlannerInstrumentation.CreatePlanAsync( + static (StepwisePlanner planner, string goal, CancellationToken _) => + { + Plan plan = new(planner._nativeFunctions["ExecutePlan"]) + { + PluginName = RestrictedPluginName, + Outputs = { "stepCount", "functionCount", "stepsTaken", "iterations" }, + }; + plan.Parameters.Set("question", goal); + return Task.FromResult(plan); + }, + static (Plan plan) => plan.ToSafePlanString(), + this, goal, this._logger, CancellationToken.None); + + // The instrumentation doesn't do any asynchronous work other than invoke the supplied callback, + // which we know will complete synchronously, so we can safely use GetResult without incurring + // blocking as the operation will have already completed by the time the call returns. + Debug.Assert(task.IsCompleted); +#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits + return task.GetAwaiter().GetResult(); +#pragma warning restore VSTHRD002 } /// @@ -622,7 +638,7 @@ private static void AddExecutionStatsToContext(List stepsTaken, SKCo // Context used to access the list of functions in the kernel private readonly Kernel _kernel; - private readonly ILogger? _logger; + private readonly ILogger _logger; /// /// Planner native functions diff --git a/dotnet/src/SemanticKernel.Core/Planning/IPlanner.cs b/dotnet/src/SemanticKernel.Core/Planning/IPlanner.cs deleted file mode 100644 index 89184b36b4e2..000000000000 --- a/dotnet/src/SemanticKernel.Core/Planning/IPlanner.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using System.Threading; -using System.Threading.Tasks; - -#pragma warning disable IDE0130 -// ReSharper disable once CheckNamespace - Using NS of Plan -namespace Microsoft.SemanticKernel.Planning; -#pragma warning restore IDE0130 - -/// Represents a planner that creates a plan to achieve a goal. -public interface IPlanner -{ - /// Creates a plan for the specified goal. - /// The goal for which a plan should be created. - /// The to monitor for cancellation requests. The default is . - /// The created plan. - /// is null. - /// is empty or entirely composed of whitespace. - /// A plan could not be created. - Task CreatePlanAsync(string goal, CancellationToken cancellationToken = default); -} diff --git a/dotnet/src/SemanticKernel.Core/Planning/InstrumentedPlanner.cs b/dotnet/src/SemanticKernel.Core/Planning/InstrumentedPlanner.cs deleted file mode 100644 index 8f276eb0b02e..000000000000 --- a/dotnet/src/SemanticKernel.Core/Planning/InstrumentedPlanner.cs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using System.Diagnostics; -using System.Diagnostics.Metrics; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; - -#pragma warning disable IDE0130 -// ReSharper disable once CheckNamespace - using planners namespace -namespace Microsoft.SemanticKernel.Planning; -#pragma warning restore IDE0130 - -/// Instrumented planner that surrounds the invocation of another planner with logging and metrics. -internal sealed class InstrumentedPlanner : IPlanner -{ - /// for planning-related activities. - private static readonly ActivitySource s_activitySource = new("Microsoft.SemanticKernel.Planning"); - - /// for planner-related metrics. - private static readonly Meter s_meter = new("Microsoft.SemanticKernel.Planning"); - - /// to record plan creation duration. - private static readonly Histogram s_createPlanDuration = s_meter.CreateHistogram( - name: "sk.planning.create_plan.duration", - unit: "s", - description: "Duration time of plan creation."); - - /// Wrapped planner instance. - private readonly IPlanner _planner; - - /// Logger to use for logging activities. - private readonly ILogger _logger; - - /// Creates a new instance of the class. - /// Instance of to decorate. - /// The to use for logging. If null, no logging will be performed. - public InstrumentedPlanner(IPlanner planner, ILoggerFactory? loggerFactory = null) - { - this._planner = planner; - this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(planner.GetType()) : NullLogger.Instance; - } - - /// - async Task IPlanner.CreatePlanAsync(string goal, CancellationToken cancellationToken) - { - string plannerName = this._planner.GetType().FullName; - - using var _ = s_activitySource.StartActivity(plannerName); - - if (this._logger.IsEnabled(LogLevel.Trace)) - { - this._logger.LogTrace("Plan creation started. Goal: {Goal}", goal); // Sensitive data, logging as trace, disabled by default - } - else if (this._logger.IsEnabled(LogLevel.Information)) - { - this._logger.LogInformation("Plan creation started."); - } - - TagList tags = new() { { "sk.planner.name", plannerName } }; - long startingTimestamp = Stopwatch.GetTimestamp(); - try - { - var plan = await this._planner.CreatePlanAsync(goal, cancellationToken).ConfigureAwait(false); - - if (this._logger.IsEnabled(LogLevel.Trace)) - { - this._logger.LogTrace("Plan created:\n{Plan}", plan.ToPlanString()); // Sensitive data, logging as trace, disabled by default - } - else if (this._logger.IsEnabled(LogLevel.Information)) - { - this._logger.LogInformation("Plan created:\n{Plan}", plan.ToSafePlanString()); - } - - return plan; - } - catch (Exception ex) - { - this._logger.LogError(ex, "Plan creation failed: {Message}", ex.Message); - tags.Add("error.type", ex.GetType().FullName); - throw; - } - finally - { - TimeSpan duration = new((long)((Stopwatch.GetTimestamp() - startingTimestamp) * (10_000_000.0 / Stopwatch.Frequency))); - this._logger.LogInformation("Plan creation duration: {Duration}ms.", duration.TotalMilliseconds); - s_createPlanDuration.Record(duration.TotalSeconds, in tags); - } - } -} diff --git a/dotnet/src/SemanticKernel.Core/Planning/PlannerExtensions.cs b/dotnet/src/SemanticKernel.Core/Planning/PlannerExtensions.cs deleted file mode 100644 index 2004f2992fbc..000000000000 --- a/dotnet/src/SemanticKernel.Core/Planning/PlannerExtensions.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using Microsoft.Extensions.Logging; - -#pragma warning disable IDE0130 -// ReSharper disable once CheckNamespace - Using NS of Plan -namespace Microsoft.SemanticKernel.Planning; -#pragma warning restore IDE0130 - -/// Provides extension methods for instances. -public static class PlannerExtensions -{ - /// Gets an that surrounds the invocation of another planner with logging and metrics. - /// Instance of to decorate. - /// The to use for logging. If null, no additional logging will be performed. - public static IPlanner WithInstrumentation(this IPlanner planner, ILoggerFactory? loggerFactory = null) - { - Verify.NotNull(planner); - return planner as InstrumentedPlanner ?? new InstrumentedPlanner(planner, loggerFactory); - } -}