diff --git a/dotnet/samples/Concepts/Filtering/Legacy_KernelHooks.cs b/dotnet/samples/Concepts/Filtering/Legacy_KernelHooks.cs deleted file mode 100644 index 73e80c0f8c04..000000000000 --- a/dotnet/samples/Concepts/Filtering/Legacy_KernelHooks.cs +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System.Text.RegularExpressions; -using Microsoft.SemanticKernel; -using Microsoft.SemanticKernel.Connectors.OpenAI; - -namespace Filtering; - -#pragma warning disable CS0618 // Events are deprecated - -public class Legacy_KernelHooks : BaseTest -{ - /// - /// Demonstrate using kernel invocation-hooks to monitor usage: - /// - /// - /// - [Fact] - public async Task GetUsageAsync() - { - Console.WriteLine("\n======== Get Usage Data ========\n"); - - // Create kernel instance - Kernel kernel = Kernel.CreateBuilder() - .AddOpenAIChatCompletion( - modelId: _openAIModelId!, - apiKey: _openAIApiKey!) - .Build(); - - // Initialize prompt - const string FunctionPrompt = "Write a random paragraph about: {{$input}}."; - - var excuseFunction = kernel.CreateFunctionFromPrompt( - FunctionPrompt, - functionName: "Excuse", - executionSettings: new OpenAIPromptExecutionSettings() { MaxTokens = 100, Temperature = 0.4, TopP = 1 }); - - // Define hooks - void MyPreHandler(object? sender, FunctionInvokingEventArgs e) - { - Console.WriteLine($"{e.Function.Name} : Pre Execution Handler - Triggered"); - } - - void MyRemovedPreExecutionHandler(object? sender, FunctionInvokingEventArgs e) - { - Console.WriteLine($"{e.Function.Name} : Pre Execution Handler - Should not trigger"); - e.Cancel = true; - } - - void MyPostExecutionHandler(object? sender, FunctionInvokedEventArgs e) - { - Console.WriteLine($"{e.Function.Name} : Post Execution Handler - Usage: {e.Result.Metadata?["Usage"]?.AsJson()}"); - } - - kernel.FunctionInvoking += MyPreHandler; - kernel.FunctionInvoked += MyPostExecutionHandler; - - // Demonstrate pattern for removing a handler. - // Note: MyRemovedPreExecutionHandler will cancel execution if not removed. - kernel.FunctionInvoking += MyRemovedPreExecutionHandler; - kernel.FunctionInvoking -= MyRemovedPreExecutionHandler; - - // Invoke prompt to trigger execution hooks. - const string Input = "I missed the F1 final race"; - var result = await kernel.InvokeAsync(excuseFunction, new() { ["input"] = Input }); - Console.WriteLine($"Function Result: {result}"); - } - - /// - /// Demonstrate using kernel-hooks to around prompt rendering: - /// - /// - /// - [Fact] - public async Task GetRenderedPromptAsync() - { - Console.WriteLine("\n======== Get Rendered Prompt ========\n"); - - // Create kernel instance - Kernel kernel = Kernel.CreateBuilder() - .AddOpenAIChatCompletion( - modelId: _openAIModelId!, - apiKey: _openAIApiKey!) - .Build(); - - // Initialize prompt - const string FunctionPrompt = "Write a random paragraph about: {{$input}} in the style of {{$style}}."; - - var excuseFunction = kernel.CreateFunctionFromPrompt( - FunctionPrompt, - functionName: "Excuse", - executionSettings: new OpenAIPromptExecutionSettings() { MaxTokens = 100, Temperature = 0.4, TopP = 1 }); - - // Define hooks - void MyRenderingHandler(object? sender, PromptRenderingEventArgs e) - { - Console.WriteLine($"{e.Function.Name} : Prompt Rendering Handler - Triggered"); - e.Arguments["style"] = "Seinfeld"; - } - - void MyRenderedHandler(object? sender, PromptRenderedEventArgs e) - { - Console.WriteLine($"{e.Function.Name} : Prompt Rendered Handler - Triggered"); - e.RenderedPrompt += " USE SHORT, CLEAR, COMPLETE SENTENCES."; - - Console.WriteLine(e.RenderedPrompt); - } - - kernel.PromptRendering += MyRenderingHandler; - kernel.PromptRendered += MyRenderedHandler; - - // Invoke prompt to trigger prompt rendering hooks. - const string Input = "I missed the F1 final race"; - var result = await kernel.InvokeAsync(excuseFunction, new() { ["input"] = Input }); - Console.WriteLine($"Function Result: {result.GetValue()}"); - } - - /// - /// Demonstrate using kernel invocation-hooks to post process result: - /// - /// - [Fact] - public async Task ChangingResultAsync() - { - Console.WriteLine("\n======== Changing/Filtering Function Result ========\n"); - - // Create kernel instance - Kernel kernel = Kernel.CreateBuilder() - .AddOpenAIChatCompletion( - modelId: _openAIModelId!, - apiKey: _openAIApiKey!) - .Build(); - - // Initialize function - const string FunctionPrompt = "Write a paragraph about Handlers."; - - var writerFunction = kernel.CreateFunctionFromPrompt( - FunctionPrompt, - functionName: "Writer", - executionSettings: new OpenAIPromptExecutionSettings() { MaxTokens = 100, Temperature = 0.4, TopP = 1 }); - - // Define hook - static void MyChangeDataHandler(object? sender, FunctionInvokedEventArgs e) - { - var originalOutput = e.Result.ToString(); - - //Use Regex to redact all vowels and numbers - var newOutput = Regex.Replace(originalOutput, "[aeiouAEIOU0-9]", "*"); - - e.SetResultValue(newOutput); - } - - kernel.FunctionInvoked += MyChangeDataHandler; - - // Invoke prompt to trigger execution hooks. - var result = await kernel.InvokeAsync(writerFunction); - - Console.WriteLine($"Function Result: {result.GetValue()}"); - } - - /// - /// Demonstrate using kernel invocation-hooks to cancel prior to execution: - /// - /// - /// - [Fact] - public async Task BeforeInvokeCancellationAsync() - { - Console.WriteLine("\n======== Cancelling Pipeline Execution - Invoking event ========\n"); - - // Create kernel instance - Kernel kernel = Kernel.CreateBuilder() - .AddOpenAIChatCompletion( - modelId: _openAIModelId!, - apiKey: _openAIApiKey!) - .Build(); - - // Initialize prompt - const string FunctionPrompt = "Write a paragraph about: Cancellation."; - - var writerFunction = kernel.CreateFunctionFromPrompt( - FunctionPrompt, - functionName: "Writer", - executionSettings: new OpenAIPromptExecutionSettings() { MaxTokens = 1000, Temperature = 1, TopP = 0.5 }); - - // Adding new inline handler to cancel/prevent function execution - kernel.FunctionInvoking += (object? sender, FunctionInvokingEventArgs e) => - { - Console.WriteLine($"{e.Function.Name} : FunctionInvoking - Cancelling before execution"); - e.Cancel = true; - }; - - // Technically invoked will never be called since the function will be cancelled - int functionInvokedCount = 0; - kernel.FunctionInvoked += (object? sender, FunctionInvokedEventArgs e) => - { - functionInvokedCount++; - }; - - // Invoke prompt to trigger execution hooks. - try - { - var result = await kernel.InvokeAsync(writerFunction); - } - catch (KernelFunctionCanceledException fcex) - { - Console.WriteLine(fcex.Message); - } - - Console.WriteLine($"Function Invocation Times: {functionInvokedCount}"); - } - - /// - /// Demonstrate using kernel invocation-hooks to cancel post after execution: - /// - /// - /// - [Fact] - public async Task AfterInvokeCancellationAsync() - { - Console.WriteLine("\n======== Cancelling Pipeline Execution - Invoked event ========\n"); - - // Create kernel instance - Kernel kernel = Kernel.CreateBuilder() - .AddOpenAIChatCompletion( - modelId: _openAIModelId!, - apiKey: _openAIApiKey!) - .Build(); - - // Initialize prompts - int functionInvokingCount = 0; - int functionInvokedCount = 0; - - var firstFunction = kernel.CreateFunctionFromPrompt("Write a phrase with Invoke.", functionName: "InvokePhrase"); - var secondFunction = kernel.CreateFunctionFromPrompt("Write a phrase with Cancellation.", functionName: "CancellationPhrase"); - - // Adding new inline handler to count invoking events - kernel.FunctionInvoking += (object? sender, FunctionInvokingEventArgs e) => - { - functionInvokingCount++; - }; - - // Invoked will never be called twice (for the secondFunction) since Invoked from the first is cancelling. - kernel.FunctionInvoked += (object? sender, FunctionInvokedEventArgs e) => - { - functionInvokedCount++; - e.Cancel = true; - }; - - // Invoke prompt to trigger execution hooks. - try - { - var result = await kernel.InvokeAsync(secondFunction); - } - catch (KernelFunctionCanceledException fcex) - { - Console.WriteLine(fcex.Message); - } - - Console.WriteLine($"Function Invoked Times: {functionInvokedCount}"); - Console.WriteLine($"Function Invoking Times: {functionInvokingCount}"); - } - - private readonly string? _openAIModelId; - private readonly string? _openAIApiKey; - - public Legacy_KernelHooks(ITestOutputHelper output) : base(output) - { - this._openAIModelId = TestConfiguration.OpenAI.ChatModelId; - this._openAIApiKey = TestConfiguration.OpenAI.ApiKey; - - if (this._openAIModelId is null || this._openAIApiKey is null) - { - Console.WriteLine("OpenAI credentials not found. Skipping example."); - return; - } - } -} diff --git a/dotnet/samples/GettingStarted/Step7_Observability.cs b/dotnet/samples/GettingStarted/Step7_Observability.cs index c65c2eb92209..116f3995c0ff 100644 --- a/dotnet/samples/GettingStarted/Step7_Observability.cs +++ b/dotnet/samples/GettingStarted/Step7_Observability.cs @@ -37,61 +37,6 @@ public async Task ObservabilityWithFiltersAsync() Console.WriteLine(await kernel.InvokePromptAsync("How many days until Christmas? Explain your thinking.", new(settings))); } - /// - /// Shows how to observe the execution of a instance with hooks. - /// - [Fact] - [Obsolete("Events are deprecated in favor of filters.")] - public async Task ObservabilityWithHooksAsync() - { - // Create a kernel with OpenAI chat completion - IKernelBuilder kernelBuilder = Kernel.CreateBuilder(); - kernelBuilder.AddOpenAIChatCompletion( - modelId: TestConfiguration.OpenAI.ChatModelId, - apiKey: TestConfiguration.OpenAI.ApiKey); - - kernelBuilder.Plugins.AddFromType(); - - Kernel kernel = kernelBuilder.Build(); - - // Handler which is called before a function is invoked - void MyInvokingHandler(object? sender, FunctionInvokingEventArgs e) - { - Console.WriteLine($"Invoking {e.Function.Name}"); - } - - // Handler which is called before a prompt is rendered - void MyRenderingHandler(object? sender, PromptRenderingEventArgs e) - { - Console.WriteLine($"Rendering prompt for {e.Function.Name}"); - } - - // Handler which is called after a prompt is rendered - void MyRenderedHandler(object? sender, PromptRenderedEventArgs e) - { - Console.WriteLine($"Rendered prompt: {e.RenderedPrompt}"); - } - - // Handler which is called after a function is invoked - void MyInvokedHandler(object? sender, FunctionInvokedEventArgs e) - { - if (e.Result.Metadata is not null && e.Result.Metadata.ContainsKey("Usage")) - { - Console.WriteLine("Token usage: {0}", e.Result.Metadata?["Usage"]?.AsJson()); - } - } - - // Add the handlers to the kernel - kernel.FunctionInvoking += MyInvokingHandler; - kernel.PromptRendering += MyRenderingHandler; - kernel.PromptRendered += MyRenderedHandler; - kernel.FunctionInvoked += MyInvokedHandler; - - // Invoke the kernel with a prompt and allow the AI to automatically invoke functions - OpenAIPromptExecutionSettings settings = new() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }; - Console.WriteLine(await kernel.InvokePromptAsync("How many days until Christmas? Explain your thinking.", new(settings))); - } - /// /// A plugin that returns the current time. /// diff --git a/dotnet/src/SemanticKernel.Abstractions/Functions/KernelFunction.cs b/dotnet/src/SemanticKernel.Abstractions/Functions/KernelFunction.cs index 07dce1c402ce..cc2d260b48a7 100644 --- a/dotnet/src/SemanticKernel.Abstractions/Functions/KernelFunction.cs +++ b/dotnet/src/SemanticKernel.Abstractions/Functions/KernelFunction.cs @@ -240,16 +240,6 @@ public async Task InvokeAsync( // Quick check for cancellation after logging about function start but before doing any real work. cancellationToken.ThrowIfCancellationRequested(); - // Invoke pre-invocation event handler. If it requests cancellation, throw. -#pragma warning disable CS0618 // Events are deprecated - var invokingEventArgs = kernel.OnFunctionInvoking(this, arguments); -#pragma warning restore CS0618 // Events are deprecated - - if (invokingEventArgs?.Cancel is true) - { - throw new OperationCanceledException($"A {nameof(Kernel)}.{nameof(Kernel.FunctionInvoking)} event handler requested cancellation before function invocation."); - } - var invocationContext = await kernel.OnFunctionInvocationAsync(this, arguments, functionResult, isStreaming: false, async (context) => { // Invoking the function and updating context with result. @@ -259,22 +249,6 @@ public async Task InvokeAsync( // Apply any changes from the function filters context to final result. functionResult = invocationContext.Result; - // Invoke the post-invocation event handler. If it requests cancellation, throw. -#pragma warning disable CS0618 // Events are deprecated - var invokedEventArgs = kernel.OnFunctionInvoked(this, arguments, functionResult); -#pragma warning restore CS0618 // Events are deprecated - - if (invokedEventArgs is not null) - { - // Apply any changes from the event handlers to final result. - functionResult = new FunctionResult(this, invokedEventArgs.ResultValue, functionResult.Culture, invokedEventArgs.Metadata ?? functionResult.Metadata); - } - - if (invokedEventArgs?.Cancel is true) - { - throw new OperationCanceledException($"A {nameof(Kernel)}.{nameof(Kernel.FunctionInvoked)} event handler requested cancellation after function invocation."); - } - logger.LogFunctionInvokedSuccess(this.PluginName, this.Name); this.LogFunctionResult(logger, this.PluginName, this.Name, functionResult); @@ -370,16 +344,6 @@ public async IAsyncEnumerable InvokeStreamingAsync( // Quick check for cancellation after logging about function start but before doing any real work. cancellationToken.ThrowIfCancellationRequested(); - // Invoke pre-invocation event handler. If it requests cancellation, throw. -#pragma warning disable CS0618 // Events are deprecated - var invokingEventArgs = kernel.OnFunctionInvoking(this, arguments); -#pragma warning restore CS0618 // Events are deprecated - - if (invokingEventArgs?.Cancel is true) - { - throw new OperationCanceledException($"A {nameof(Kernel)}.{nameof(Kernel.FunctionInvoking)} event handler requested cancellation before function invocation."); - } - FunctionResult functionResult = new(this, culture: kernel.Culture); var invocationContext = await kernel.OnFunctionInvocationAsync(this, arguments, functionResult, isStreaming: true, (context) => diff --git a/dotnet/src/SemanticKernel.Abstractions/Kernel.cs b/dotnet/src/SemanticKernel.Abstractions/Kernel.cs index 5a44a4dffd6a..f275eca325d3 100644 --- a/dotnet/src/SemanticKernel.Abstractions/Kernel.cs +++ b/dotnet/src/SemanticKernel.Abstractions/Kernel.cs @@ -110,10 +110,6 @@ public Kernel( public Kernel Clone() => new(this.Services, this._plugins is { Count: > 0 } ? new KernelPluginCollection(this._plugins) : null) { - FunctionInvoking = this.FunctionInvoking, - FunctionInvoked = this.FunctionInvoked, - PromptRendering = this.PromptRendering, - PromptRendered = this.PromptRendered, _functionInvocationFilters = this._functionInvocationFilters is { Count: > 0 } ? new NonNullCollection(this._functionInvocationFilters) : null, _promptRenderFilters = this._promptRenderFilters is { Count: > 0 } ? new NonNullCollection(this._promptRenderFilters) : null, _autoFunctionInvocationFilters = this._autoFunctionInvocationFilters is { Count: > 0 } ? new NonNullCollection(this._autoFunctionInvocationFilters) : null, @@ -609,6 +605,7 @@ private static bool IsNotEmpty(IEnumerable enumerable) => #region Obsolete +#pragma warning disable CS0067 // The event is never used /// /// Provides an event that's raised prior to a function's invocation. /// @@ -636,58 +633,7 @@ private static bool IsNotEmpty(IEnumerable enumerable) => [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("Events are deprecated in favor of filters. Example in dotnet/samples/GettingStarted/Step7_Observability.cs of Semantic Kernel repository.")] public event EventHandler? PromptRendered; - - [Obsolete("Events are deprecated in favor of filters. Example in dotnet/samples/GettingStarted/Step7_Observability.cs of Semantic Kernel repository.")] - internal FunctionInvokingEventArgs? OnFunctionInvoking(KernelFunction function, KernelArguments arguments) - { - FunctionInvokingEventArgs? eventArgs = null; - if (this.FunctionInvoking is { } functionInvoking) - { - eventArgs = new(function, arguments); - functionInvoking.Invoke(this, eventArgs); - } - - return eventArgs; - } - - [Obsolete("Events are deprecated in favor of filters. Example in dotnet/samples/GettingStarted/Step7_Observability.cs of Semantic Kernel repository.")] - internal FunctionInvokedEventArgs? OnFunctionInvoked(KernelFunction function, KernelArguments arguments, FunctionResult result) - { - FunctionInvokedEventArgs? eventArgs = null; - if (this.FunctionInvoked is { } functionInvoked) - { - eventArgs = new(function, arguments, result); - functionInvoked.Invoke(this, eventArgs); - } - - return eventArgs; - } - - [Obsolete("Events are deprecated in favor of filters. Example in dotnet/samples/GettingStarted/Step7_Observability.cs of Semantic Kernel repository.")] - internal PromptRenderingEventArgs? OnPromptRendering(KernelFunction function, KernelArguments arguments) - { - PromptRenderingEventArgs? eventArgs = null; - if (this.PromptRendering is { } promptRendering) - { - eventArgs = new(function, arguments); - promptRendering.Invoke(this, eventArgs); - } - - return eventArgs; - } - - [Obsolete("Events are deprecated in favor of filters. Example in dotnet/samples/GettingStarted/Step7_Observability.cs of Semantic Kernel repository.")] - internal PromptRenderedEventArgs? OnPromptRendered(KernelFunction function, KernelArguments arguments, string renderedPrompt) - { - PromptRenderedEventArgs? eventArgs = null; - if (this.PromptRendered is { } promptRendered) - { - eventArgs = new(function, arguments, renderedPrompt); - promptRendered.Invoke(this, eventArgs); - } - - return eventArgs; - } +#pragma warning disable CS0067 // The event is never used #endregion } diff --git a/dotnet/src/SemanticKernel.Core/Functions/KernelFunctionFromPrompt.cs b/dotnet/src/SemanticKernel.Core/Functions/KernelFunctionFromPrompt.cs index 8652cfa1cbfe..367e5e7a2553 100644 --- a/dotnet/src/SemanticKernel.Core/Functions/KernelFunctionFromPrompt.cs +++ b/dotnet/src/SemanticKernel.Core/Functions/KernelFunctionFromPrompt.cs @@ -241,13 +241,6 @@ protected override async ValueTask InvokeCoreAsync( isStreaming: false, cancellationToken).ConfigureAwait(false); -#pragma warning disable CS0612 // Events are deprecated - if (promptRenderingResult.RenderedEventArgs?.Cancel is true) - { - throw new OperationCanceledException($"A {nameof(Kernel)}.{nameof(Kernel.PromptRendered)} event handler requested cancellation after prompt rendering."); - } -#pragma warning restore CS0612 // Events are deprecated - // Return function result if it was set in prompt filter. if (promptRenderingResult.FunctionResult is not null) { @@ -278,13 +271,6 @@ protected override async IAsyncEnumerable InvokeStreamingCoreAsync? asyncReference = null; if (result.AIService is IChatCompletionService chatCompletion) @@ -514,10 +500,6 @@ private async Task RenderPromptAsync( Verify.NotNull(aiService); -#pragma warning disable CS0618 // Events are deprecated - kernel.OnPromptRendering(this, arguments); -#pragma warning restore CS0618 // Events are deprecated - var renderingContext = await kernel.OnPromptRenderAsync(this, arguments, isStreaming, async (context) => { renderedPrompt = await this._promptTemplate.RenderAsync(kernel, context.Arguments, cancellationToken).ConfigureAwait(false); @@ -541,26 +523,9 @@ private async Task RenderPromptAsync( } } -#pragma warning disable CS0618 // Events are deprecated - var renderedEventArgs = kernel.OnPromptRendered(this, arguments, renderedPrompt); - - if (renderedEventArgs is not null && - !renderedEventArgs.Cancel && - renderedEventArgs.RenderedPrompt != renderedPrompt) - { - renderedPrompt = renderedEventArgs.RenderedPrompt; - - if (this._logger.IsEnabled(LogLevel.Trace)) - { - this._logger.LogTrace("Rendered prompt changed by event handler: {Prompt}", renderedEventArgs.RenderedPrompt); - } - } -#pragma warning restore CS0618 // Events are deprecated - return new(aiService, renderedPrompt) { ExecutionSettings = executionSettings, - RenderedEventArgs = renderedEventArgs, FunctionResult = renderingContext.Result }; } diff --git a/dotnet/src/SemanticKernel.Core/Functions/PromptRenderingResult.cs b/dotnet/src/SemanticKernel.Core/Functions/PromptRenderingResult.cs index 7aee48fc130b..99a78f6a9653 100644 --- a/dotnet/src/SemanticKernel.Core/Functions/PromptRenderingResult.cs +++ b/dotnet/src/SemanticKernel.Core/Functions/PromptRenderingResult.cs @@ -17,10 +17,6 @@ internal sealed class PromptRenderingResult public FunctionResult? FunctionResult { get; set; } -#pragma warning disable CS0618 // Events are deprecated - public PromptRenderedEventArgs? RenderedEventArgs { get; set; } -#pragma warning restore CS0618 // Events are deprecated - public PromptRenderingResult(IAIService aiService, string renderedPrompt) { this.AIService = aiService; diff --git a/dotnet/src/SemanticKernel.UnitTests/Functions/FunctionFromMethodTests.cs b/dotnet/src/SemanticKernel.UnitTests/Functions/FunctionFromMethodTests.cs index 445ae9304fb5..88c76b12ac2b 100644 --- a/dotnet/src/SemanticKernel.UnitTests/Functions/FunctionFromMethodTests.cs +++ b/dotnet/src/SemanticKernel.UnitTests/Functions/FunctionFromMethodTests.cs @@ -76,94 +76,4 @@ public async Task InvokeStreamingAsyncShouldPropagateMetadataFromNonStreamingMet Assert.Equal("value1", methodContent.Metadata["key1"]); Assert.Equal("value2", methodContent.Metadata["key2"]); } - - [Fact] - public async Task InvokeStreamingAsyncOnlySupportsInvokingEventAsync() - { - // Arrange - var kernel = new Kernel(); - var sut = KernelFunctionFactory.CreateFromMethod(() => "any"); - - var invokedCalled = false; - var invokingCalled = false; - -#pragma warning disable CS0618 // Events are deprecated - kernel.FunctionInvoking += (object? sender, FunctionInvokingEventArgs e) => - { - invokingCalled = true; - }; - - // Invoked is not supported for streaming... - kernel.FunctionInvoked += (object? sender, FunctionInvokedEventArgs e) => - { - invokedCalled = true; - }; -#pragma warning restore CS0618 // Events are deprecated - - // Act - await foreach (var chunk in sut.InvokeStreamingAsync(kernel)) - { - } - - // Assert - Assert.True(invokingCalled); - Assert.False(invokedCalled); - } - - [Fact] - public async Task InvokeStreamingAsyncInvokingCancelingShouldThrowAsync() - { - // Arrange - var kernel = new Kernel(); - var sut = KernelFunctionFactory.CreateFromMethod(() => "any"); - - bool invokingCalled = false; - -#pragma warning disable CS0618 // Type or member is obsolete - kernel.FunctionInvoking += (object? sender, FunctionInvokingEventArgs e) => - { - invokingCalled = true; - e.Cancel = true; - }; -#pragma warning restore CS0618 // Type or member is obsolete - - // Act - IAsyncEnumerable enumerable = sut.InvokeStreamingAsync(kernel); - IAsyncEnumerator enumerator = enumerable.GetAsyncEnumerator(); - Assert.False(invokingCalled); - var e = await Assert.ThrowsAsync(async () => await enumerator.MoveNextAsync()); - - // Assert - Assert.True(invokingCalled); - Assert.Same(sut, e.Function); - Assert.Same(kernel, e.Kernel); - Assert.Empty(e.Arguments); - } - - [Fact] - public async Task InvokeStreamingAsyncUsingInvokedEventHasNoEffectAsync() - { - // Arrange - var kernel = new Kernel(); - var sut = KernelFunctionFactory.CreateFromMethod(() => "any"); - -#pragma warning disable CS0618 // Type or member is obsolete - kernel.FunctionInvoked += (object? sender, FunctionInvokedEventArgs e) => - { - // This will have no effect on streaming - e.Cancel = true; - }; -#pragma warning restore CS0618 // Type or member is obsolete - - var chunkCount = 0; - - // Act - await foreach (var chunk in sut.InvokeStreamingAsync(kernel)) - { - chunkCount++; - } - - // Assert - Assert.Equal(1, chunkCount); - } } diff --git a/dotnet/src/SemanticKernel.UnitTests/Functions/KernelFunctionFromPromptTests.cs b/dotnet/src/SemanticKernel.UnitTests/Functions/KernelFunctionFromPromptTests.cs index d21a9394c453..ff45e19c62d7 100644 --- a/dotnet/src/SemanticKernel.UnitTests/Functions/KernelFunctionFromPromptTests.cs +++ b/dotnet/src/SemanticKernel.UnitTests/Functions/KernelFunctionFromPromptTests.cs @@ -637,35 +637,6 @@ public async Task InvokeAsyncWithNestedPromptsSelectsCorrectServiceAsync() mockTextCompletion2.Verify(m => m.GetTextContentsAsync("Prompt2 Result1", It.Is(settings => settings.MaxTokens == 2000), It.IsAny(), It.IsAny()), Times.Once()); } - [Fact] - public async Task InvokeAsyncWithPromptRenderedHooksExecutesModifiedPromptAsync() - { - // Arrange - var mockTextContent = new TextContent("Result"); - var mockTextCompletion = new Mock(); - mockTextCompletion.Setup(m => m.GetTextContentsAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(new List { mockTextContent }); - -#pragma warning disable CS0618 // Events are deprecated - static void MyRenderedHandler(object? sender, PromptRenderedEventArgs e) - { - e.RenderedPrompt += " USE SHORT, CLEAR, COMPLETE SENTENCES."; - } - - KernelBuilder builder = new(); - builder.Services.AddKeyedSingleton("service", mockTextCompletion.Object); - Kernel kernel = builder.Build(); - kernel.PromptRendered += MyRenderedHandler; -#pragma warning restore CS0618 // Events are deprecated - - KernelFunction function = KernelFunctionFactory.CreateFromPrompt("Prompt"); - - // Act - var result1 = await kernel.InvokeAsync(function); - - // Assert - mockTextCompletion.Verify(m => m.GetTextContentsAsync("Prompt USE SHORT, CLEAR, COMPLETE SENTENCES.", It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); - } - [Theory] [InlineData(KernelInvocationType.InvokePrompt)] [InlineData(KernelInvocationType.InvokePromptStreaming)] diff --git a/dotnet/src/SemanticKernel.UnitTests/KernelTests.cs b/dotnet/src/SemanticKernel.UnitTests/KernelTests.cs index 9ca5e2d49444..b7ed4fc4a480 100644 --- a/dotnet/src/SemanticKernel.UnitTests/KernelTests.cs +++ b/dotnet/src/SemanticKernel.UnitTests/KernelTests.cs @@ -96,348 +96,6 @@ public void ItAllowsToImportTheSamePluginMultipleTimes() kernel.ImportPluginFromType("plugin3"); } - [Fact] - public async Task InvokeAsyncHandlesPreInvocationAsync() - { - // Arrange - var kernel = new Kernel(); - int functionInvocations = 0; - var function = KernelFunctionFactory.CreateFromMethod(() => functionInvocations++); - - var handlerInvocations = 0; - kernel.FunctionInvoking += (object? sender, FunctionInvokingEventArgs e) => - { - handlerInvocations++; - }; - - // Act - var result = await kernel.InvokeAsync(function); - - // Assert - Assert.Equal(1, functionInvocations); - Assert.Equal(1, handlerInvocations); - } - - [Fact] - public async Task RunStreamingAsyncHandlesPreInvocationAsync() - { - // Arrange - var kernel = new Kernel(); - int functionInvocations = 0; - var function = KernelFunctionFactory.CreateFromMethod(() => functionInvocations++); - - var handlerInvocations = 0; - kernel.FunctionInvoking += (object? sender, FunctionInvokingEventArgs e) => - { - handlerInvocations++; - }; - - // Act - await foreach (var chunk in kernel.InvokeStreamingAsync(function)) { } - - // Assert - Assert.Equal(1, functionInvocations); - Assert.Equal(1, handlerInvocations); - } - - [Fact] - public async Task RunStreamingAsyncHandlesPreInvocationWasCancelledAsync() - { - // Arrange - var kernel = new Kernel(); - int functionInvocations = 0; - var function = KernelFunctionFactory.CreateFromMethod(() => functionInvocations++); - - var handlerInvocations = 0; - kernel.FunctionInvoking += (object? sender, FunctionInvokingEventArgs e) => - { - handlerInvocations++; - e.Cancel = true; - }; - - // Act - IAsyncEnumerable enumerable = kernel.InvokeStreamingAsync(function); - IAsyncEnumerator enumerator = enumerable.GetAsyncEnumerator(); - var e = await Assert.ThrowsAsync(async () => await enumerator.MoveNextAsync()); - - // Assert - Assert.Equal(1, handlerInvocations); - Assert.Equal(0, functionInvocations); - Assert.Same(function, e.Function); - Assert.Same(kernel, e.Kernel); - Assert.Empty(e.Arguments); - } - - [Fact] - public async Task RunStreamingAsyncPreInvocationCancelationDontTriggerInvokedHandlerAsync() - { - // Arrange - var kernel = new Kernel(); - var functions = kernel.ImportPluginFromType(); - - var invoked = 0; - kernel.FunctionInvoking += (object? sender, FunctionInvokingEventArgs e) => - { - e.Cancel = true; - }; - - kernel.FunctionInvoked += (object? sender, FunctionInvokedEventArgs e) => - { - invoked++; - }; - - // Act - IAsyncEnumerable enumerable = kernel.InvokeStreamingAsync(functions["GetAnyValue"]); - IAsyncEnumerator enumerator = enumerable.GetAsyncEnumerator(); - var e = await Assert.ThrowsAsync(async () => await enumerator.MoveNextAsync()); - - // Assert - Assert.Equal(0, invoked); - } - - [Fact] - public async Task InvokeStreamingAsyncDoesNotHandlePostInvocationAsync() - { - // Arrange - var kernel = new Kernel(); - int functionInvocations = 0; - var function = KernelFunctionFactory.CreateFromMethod(() => functionInvocations++); - - int handlerInvocations = 0; - kernel.FunctionInvoked += (object? sender, FunctionInvokedEventArgs e) => - { - handlerInvocations++; - }; - - // Act - await foreach (var chunk in kernel.InvokeStreamingAsync(function)) - { - } - - // Assert - Assert.Equal(1, functionInvocations); - Assert.Equal(0, handlerInvocations); - } - - [Fact] - public async Task InvokeAsyncHandlesPreInvocationWasCancelledAsync() - { - // Arrange - var kernel = new Kernel(); - int functionInvocations = 0; - var function = KernelFunctionFactory.CreateFromMethod(() => functionInvocations++); - - var handlerInvocations = 0; - kernel.FunctionInvoking += (object? sender, FunctionInvokingEventArgs e) => - { - handlerInvocations++; - e.Cancel = true; - }; - - // Act - KernelFunctionCanceledException ex = await Assert.ThrowsAsync(() => kernel.InvokeAsync(function)); - - // Assert - Assert.Equal(1, handlerInvocations); - Assert.Equal(0, functionInvocations); - Assert.Same(function, ex.Function); - Assert.Null(ex.FunctionResult?.Value); - } - - [Fact] - public async Task InvokeAsyncHandlesPreInvocationCancelationDontRunSubsequentFunctionsInThePipelineAsync() - { - // Arrange - var kernel = new Kernel(); - int functionInvocations = 0; - var function = KernelFunctionFactory.CreateFromMethod(() => functionInvocations++); - - int handlerInvocations = 0; - kernel.FunctionInvoking += (object? sender, FunctionInvokingEventArgs e) => - { - handlerInvocations++; - e.Cancel = true; - }; - - // Act - KernelFunctionCanceledException ex = await Assert.ThrowsAsync(() => kernel.InvokeAsync(function)); - - // Assert - Assert.Equal(1, handlerInvocations); - Assert.Equal(0, functionInvocations); - Assert.Same(function, ex.Function); - Assert.Null(ex.FunctionResult?.Value); - } - - [Fact] - public async Task InvokeAsyncPreInvocationCancelationDontTriggerInvokedHandlerAsync() - { - // Arrange - var kernel = new Kernel(); - var functions = kernel.ImportPluginFromType(); - - var invoked = 0; - kernel.FunctionInvoking += (object? sender, FunctionInvokingEventArgs e) => - { - e.Cancel = true; - }; - - kernel.FunctionInvoked += (object? sender, FunctionInvokedEventArgs e) => - { - invoked++; - }; - - // Act - KernelFunctionCanceledException ex = await Assert.ThrowsAsync(() => kernel.InvokeAsync(functions["GetAnyValue"])); - - // Assert - Assert.Equal(0, invoked); - Assert.Same(functions["GetAnyValue"], ex.Function); - Assert.Null(ex.FunctionResult?.Value); - } - - [Fact] - public async Task InvokeAsyncHandlesPostInvocationAsync() - { - // Arrange - var kernel = new Kernel(); - int functionInvocations = 0; - var function = KernelFunctionFactory.CreateFromMethod(() => functionInvocations++); - - int handlerInvocations = 0; - kernel.FunctionInvoked += (object? sender, FunctionInvokedEventArgs e) => - { - handlerInvocations++; - }; - - // Act - var result = await kernel.InvokeAsync(function); - - // Assert - Assert.Equal(1, functionInvocations); - Assert.Equal(1, handlerInvocations); - } - - [Fact] - public async Task InvokeAsyncHandlesPostInvocationWithServicesAsync() - { - // Arrange - var (mockTextResult, mockTextCompletion) = this.SetupMocks(); - IKernelBuilder builder = Kernel.CreateBuilder(); - builder.Services.AddSingleton(mockTextCompletion.Object); - Kernel kernel = builder.Build(); - - var function = KernelFunctionFactory.CreateFromPrompt("Write a simple phrase about UnitTests"); - - var invoked = 0; - - kernel.FunctionInvoked += (sender, e) => - { - invoked++; - }; - - // Act - var result = await kernel.InvokeAsync(function); - - // Assert - Assert.Equal(1, invoked); - mockTextCompletion.Verify(m => m.GetTextContentsAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(1)); - } - - [Fact] - public async Task InvokeAsyncHandlesPostInvocationAndCancellationExceptionContainsResultAsync() - { - // Arrange - var kernel = new Kernel(); - object result = 42; - var function = KernelFunctionFactory.CreateFromMethod(() => result); - var args = new KernelArguments() { { "a", "b" } }; - - kernel.FunctionInvoked += (object? sender, FunctionInvokedEventArgs e) => - { - e.Cancel = true; - }; - - // Act - KernelFunctionCanceledException ex = await Assert.ThrowsAsync(() => kernel.InvokeAsync(function, args)); - - // Assert - Assert.Same(kernel, ex.Kernel); - Assert.Same(function, ex.Function); - Assert.Same(args, ex.Arguments); - Assert.NotNull(ex.FunctionResult); - Assert.Same(result, ex.FunctionResult.GetValue()); - } - - [Fact] - public async Task InvokeAsyncHandlesPostInvocationAndCancellationExceptionContainsModifiedResultAsync() - { - // Arrange - var kernel = new Kernel(); - object result = 42; - object newResult = 84; - var function = KernelFunctionFactory.CreateFromMethod(() => result); - var args = new KernelArguments() { { "a", "b" } }; - - kernel.FunctionInvoked += (object? sender, FunctionInvokedEventArgs e) => - { - e.SetResultValue(newResult); - e.Cancel = true; - }; - - // Act - KernelFunctionCanceledException ex = await Assert.ThrowsAsync(() => kernel.InvokeAsync(function, args)); - - // Assert - Assert.Same(kernel, ex.Kernel); - Assert.Same(function, ex.Function); - Assert.Same(args, ex.Arguments); - Assert.NotNull(ex.FunctionResult); - Assert.Same(newResult, ex.FunctionResult.GetValue()); - } - - [Fact] - public async Task InvokeAsyncChangeVariableInvokingHandlerAsync() - { - var kernel = new Kernel(); - var function = KernelFunctionFactory.CreateFromMethod((string originalInput) => originalInput); - - var originalInput = "Importance"; - var newInput = "Problems"; - - kernel.FunctionInvoking += (object? sender, FunctionInvokingEventArgs e) => - { - e.Arguments["originalInput"] = newInput; - }; - - // Act - var result = await kernel.InvokeAsync(function, new() { ["originalInput"] = originalInput }); - - // Assert - Assert.Equal(newInput, result.GetValue()); - } - - [Fact] - public async Task InvokeAsyncChangeVariableInvokedHandlerAsync() - { - var kernel = new Kernel(); - var function = KernelFunctionFactory.CreateFromMethod(() => { }); - - var originalInput = "Importance"; - var newInput = "Problems"; - - kernel.FunctionInvoked += (object? sender, FunctionInvokedEventArgs e) => - { - e.SetResultValue(newInput); - }; - - // Act - var result = await kernel.InvokeAsync(function, new() { [InputParameterName] = originalInput }); - - // Assert - Assert.Equal(newInput, result.GetValue()); - } - [Fact] public async Task ItReturnsFunctionResultsCorrectlyAsync() { @@ -454,50 +112,6 @@ public async Task ItReturnsFunctionResultsCorrectlyAsync() Assert.Equal("Result", result.GetValue()); } - [Fact] - public async Task ItReturnsChangedResultsFromFunctionInvokedEventsAsync() - { - var kernel = new Kernel(); - - // Arrange - var function1 = KernelFunctionFactory.CreateFromMethod(() => "Result1", "Function1"); - const string ExpectedValue = "new result"; - - kernel.FunctionInvoked += (object? sender, FunctionInvokedEventArgs args) => - { - args.SetResultValue(ExpectedValue); - }; - - // Act - var result = await kernel.InvokeAsync(function1); - - // Assert - Assert.NotNull(result); - Assert.Equal(ExpectedValue, result.GetValue()); - } - - [Fact] - public async Task ItReturnsChangedResultsFromFunctionInvokingEventsAsync() - { - // Arrange - var kernel = new Kernel(); - - var function1 = KernelFunctionFactory.CreateFromMethod((string injectedVariable) => injectedVariable, "Function1"); - const string ExpectedValue = "injected value"; - - kernel.FunctionInvoking += (object? sender, FunctionInvokingEventArgs args) => - { - args.Arguments["injectedVariable"] = ExpectedValue; - }; - - // Act - var result = await kernel.InvokeAsync(function1); - - // Assert - Assert.NotNull(result); - Assert.Equal(ExpectedValue, result.GetValue()); - } - [Fact] public async Task ItCanFindAndRunFunctionAsync() {