// Copyright (c) Microsoft. All rights reserved. using System.ComponentModel; using Microsoft.Extensions.DependencyInjection; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Connectors.OpenAI; namespace GettingStarted; public sealed class Step7_Observability(ITestOutputHelper output) : BaseTest(output) { /// /// Shows how to observe the execution of a instance with filters. /// [Fact] public async Task ObservabilityWithFilters() { // Create a kernel with OpenAI chat completion IKernelBuilder kernelBuilder = Kernel.CreateBuilder(); kernelBuilder.AddOpenAIChatClient( modelId: TestConfiguration.OpenAI.ChatModelId, apiKey: TestConfiguration.OpenAI.ApiKey); kernelBuilder.Plugins.AddFromType(); // Add filter using DI kernelBuilder.Services.AddSingleton(this.Output); kernelBuilder.Services.AddSingleton(); Kernel kernel = kernelBuilder.Build(); // Add filter without DI kernel.PromptRenderFilters.Add(new MyPromptFilter(this.Output)); // 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. /// private sealed class TimeInformation { [KernelFunction] [Description("Retrieves the current time in UTC.")] public string GetCurrentUtcTime() => DateTime.UtcNow.ToString("R"); } /// /// Function filter for observability. /// private sealed class MyFunctionFilter(ITestOutputHelper output) : IFunctionInvocationFilter { private readonly ITestOutputHelper _output = output; public async Task OnFunctionInvocationAsync(FunctionInvocationContext context, Func next) { this._output.WriteLine($"Invoking {context.Function.Name}"); await next(context); var metadata = context.Result?.Metadata; if (metadata is not null && metadata.ContainsKey("Usage")) { this._output.WriteLine($"Token usage: {metadata["Usage"]?.AsJson()}"); } } } /// /// Prompt filter for observability. /// private sealed class MyPromptFilter(ITestOutputHelper output) : IPromptRenderFilter { private readonly ITestOutputHelper _output = output; public async Task OnPromptRenderAsync(PromptRenderContext context, Func next) { this._output.WriteLine($"Rendering prompt for {context.Function.Name}"); await next(context); this._output.WriteLine($"Rendered prompt: {context.RenderedPrompt}"); } } }