// Copyright (c) Microsoft. All rights reserved. using System.ComponentModel; using System.Globalization; using System.Reflection; using System.Text.Json; using Microsoft.SemanticKernel; namespace Functions; /// /// These samples show advanced usage of method functions. /// public class MethodFunctions_Advanced(ITestOutputHelper output) : BaseTest(output) { /// /// This example executes Function1, which in turn executes Function2. /// [Fact] public async Task MethodFunctionsChaining() { Console.WriteLine("Running Method Function Chaining example..."); var kernel = new Kernel(); var functions = kernel.ImportPluginFromType(); var customType = await kernel.InvokeAsync(functions["Function1"]); Console.WriteLine($"CustomType.Number: {customType!.Number}"); // 2 Console.WriteLine($"CustomType.Text: {customType.Text}"); // From Function1 + From Function2 } /// /// This example shows how to access the custom attribute the underlying method wrapped by Kernel Function is annotated with. /// [Fact] public async Task AccessUnderlyingMethodAttributes() { // Import the plugin containing the method with the InvocationSettingsAttribute custom attribute var kernel = new Kernel(); var functions = kernel.ImportPluginFromType(); // Get the kernel function wrapping the method with the InvocationSettingsAttribute var kernelFunction = functions[nameof(Plugin.FunctionWithInvocationSettingsAttribute)]; // Access the custom attribute the underlying method is annotated with var invocationSettingsAttribute = kernelFunction.UnderlyingMethod!.GetCustomAttribute(); Console.WriteLine($"Priority: {invocationSettingsAttribute?.Priority}"); } private sealed class Plugin { private const string PluginName = nameof(Plugin); [KernelFunction] public async Task Function1Async(Kernel kernel) { // Execute another function var value = await kernel.InvokeAsync(PluginName, "Function2"); return new MyCustomType { Number = 2 * value?.Number ?? 0, Text = "From Function1 + " + value?.Text }; } [KernelFunction] public static MyCustomType Function2() { return new MyCustomType { Number = 1, Text = "From Function2" }; } [KernelFunction, InvocationSettingsAttribute(priority: Priority.High)] public static void FunctionWithInvocationSettingsAttribute() { } } /// /// In order to use custom types, should be specified, /// that will convert object instance to string representation. /// /// /// is used to represent complex object as meaningful string, so /// it can be passed to AI for further processing using prompt functions. /// It's possible to choose any format (e.g. XML, JSON, YAML) to represent your object. /// [TypeConverter(typeof(MyCustomTypeConverter))] private sealed class MyCustomType { public int Number { get; set; } public string? Text { get; set; } } /// /// Implementation of for . /// In this example, object instance is serialized with from System.Text.Json, /// but it's possible to convert object to string using any other serialization logic. /// private sealed class MyCustomTypeConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) => true; /// /// This method is used to convert object from string to actual type. This will allow to pass object to /// method function which requires it. /// public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { return JsonSerializer.Deserialize((string)value); } /// /// This method is used to convert actual type to string representation, so it can be passed to AI /// for further processing. /// public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { return JsonSerializer.Serialize(value); } } [AttributeUsage(AttributeTargets.Method)] private sealed class InvocationSettingsAttribute : Attribute { public InvocationSettingsAttribute(Priority priority = Priority.Normal) { this.Priority = priority; } public Priority Priority { get; } } private enum Priority { Normal, High, } }