chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using Microsoft.SemanticKernel;
|
||||
|
||||
namespace Functions;
|
||||
|
||||
// This example shows how to use kernel arguments when invoking functions.
|
||||
public class Arguments(ITestOutputHelper output) : BaseTest(output)
|
||||
{
|
||||
[Fact]
|
||||
public async Task RunAsync()
|
||||
{
|
||||
Console.WriteLine("======== Arguments ========");
|
||||
|
||||
Kernel kernel = new();
|
||||
var textPlugin = kernel.ImportPluginFromType<StaticTextPlugin>();
|
||||
|
||||
var arguments = new KernelArguments()
|
||||
{
|
||||
["input"] = "Today is: ",
|
||||
["day"] = DateTimeOffset.Now.ToString("dddd", CultureInfo.CurrentCulture)
|
||||
};
|
||||
|
||||
// ** Different ways of executing functions with arguments **
|
||||
|
||||
// Specify and get the value type as generic parameter
|
||||
string? resultValue = await kernel.InvokeAsync<string>(textPlugin["AppendDay"], arguments);
|
||||
Console.WriteLine($"string -> {resultValue}");
|
||||
|
||||
// If you need to access the result metadata, you can use the non-generic version to get the FunctionResult
|
||||
FunctionResult functionResult = await kernel.InvokeAsync(textPlugin["AppendDay"], arguments);
|
||||
var metadata = functionResult.Metadata;
|
||||
|
||||
// Specify the type from the FunctionResult
|
||||
Console.WriteLine($"FunctionResult.GetValue<string>() -> {functionResult.GetValue<string>()}");
|
||||
|
||||
// FunctionResult.ToString() automatically converts the result to string
|
||||
Console.WriteLine($"FunctionResult.ToString() -> {functionResult}");
|
||||
}
|
||||
|
||||
public sealed class StaticTextPlugin
|
||||
{
|
||||
[KernelFunction, Description("Change all string chars to uppercase")]
|
||||
public static string Uppercase([Description("Text to uppercase")] string input) =>
|
||||
input.ToUpperInvariant();
|
||||
|
||||
[KernelFunction, Description("Append the day variable")]
|
||||
public static string AppendDay(
|
||||
[Description("Text to append to")] string input,
|
||||
[Description("Value of the day to append")] string day) =>
|
||||
input + day;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.SemanticKernel;
|
||||
|
||||
namespace Functions;
|
||||
|
||||
public class FunctionResult_Metadata(ITestOutputHelper output) : BaseTest(output)
|
||||
{
|
||||
[Fact]
|
||||
public async Task GetTokenUsageMetadataAsync()
|
||||
{
|
||||
Console.WriteLine("======== Inline Function Definition + Invocation ========");
|
||||
|
||||
// Create kernel
|
||||
var kernel = Kernel.CreateBuilder()
|
||||
.AddOpenAIChatCompletion(
|
||||
modelId: TestConfiguration.OpenAI.ChatModelId,
|
||||
apiKey: TestConfiguration.OpenAI.ApiKey)
|
||||
.Build();
|
||||
|
||||
// Create function
|
||||
const string FunctionDefinition = "Hi, give me 5 book suggestions about: {{$input}}";
|
||||
KernelFunction myFunction = kernel.CreateFunctionFromPrompt(FunctionDefinition);
|
||||
|
||||
// Invoke function through kernel
|
||||
FunctionResult result = await kernel.InvokeAsync(myFunction, new() { ["input"] = "travel" });
|
||||
|
||||
// Display results
|
||||
Console.WriteLine(result.GetValue<string>());
|
||||
Console.WriteLine(result.Metadata?["Usage"]?.AsJson());
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetFullModelMetadataAsync()
|
||||
{
|
||||
Console.WriteLine("======== Inline Function Definition + Invocation ========");
|
||||
|
||||
// Create kernel
|
||||
var kernel = Kernel.CreateBuilder()
|
||||
.AddOpenAIChatCompletion(
|
||||
modelId: TestConfiguration.OpenAI.ChatModelId,
|
||||
apiKey: TestConfiguration.OpenAI.ApiKey)
|
||||
.Build();
|
||||
|
||||
// Create function
|
||||
const string FunctionDefinition = "1 + 1 = ?";
|
||||
KernelFunction myFunction = kernel.CreateFunctionFromPrompt(FunctionDefinition);
|
||||
|
||||
// Invoke function through kernel
|
||||
FunctionResult result = await kernel.InvokeAsync(myFunction);
|
||||
|
||||
// Display results
|
||||
Console.WriteLine(result.GetValue<string>());
|
||||
Console.WriteLine(result.Metadata?.AsJson());
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetMetadataFromStreamAsync()
|
||||
{
|
||||
var kernel = Kernel.CreateBuilder()
|
||||
.AddOpenAIChatCompletion(
|
||||
modelId: TestConfiguration.OpenAI.ChatModelId,
|
||||
apiKey: TestConfiguration.OpenAI.ApiKey)
|
||||
.Build();
|
||||
|
||||
// Create function
|
||||
const string FunctionDefinition = "1 + 1 = ?";
|
||||
KernelFunction myFunction = kernel.CreateFunctionFromPrompt(FunctionDefinition);
|
||||
|
||||
await foreach (var content in kernel.InvokeStreamingAsync(myFunction))
|
||||
{
|
||||
Console.WriteLine(content.Metadata?.AsJson());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json;
|
||||
using Microsoft.SemanticKernel;
|
||||
using OpenAI.Chat;
|
||||
|
||||
namespace Functions;
|
||||
|
||||
// The following example shows how to receive the results from the kernel in a strongly typed object
|
||||
// which stores the usage in tokens and converts the JSON result to a strongly typed object, where a validation can also
|
||||
// be performed
|
||||
public class FunctionResult_StronglyTyped(ITestOutputHelper output) : BaseTest(output)
|
||||
{
|
||||
[Fact]
|
||||
public async Task RunAsync()
|
||||
{
|
||||
Console.WriteLine("======== Extended function result ========");
|
||||
|
||||
Kernel kernel = Kernel.CreateBuilder()
|
||||
.AddOpenAIChatCompletion(
|
||||
modelId: TestConfiguration.OpenAI.ChatModelId,
|
||||
apiKey: TestConfiguration.OpenAI.ApiKey)
|
||||
.Build();
|
||||
|
||||
var promptTestDataGeneration = "Return a JSON with an array of 3 JSON objects with the following fields: " +
|
||||
"First, an id field with a random GUID, next a name field with a random company name and last a description field with a random short company description. " +
|
||||
"Ensure the JSON is valid and it contains a JSON array named testcompanies with the three fields.";
|
||||
|
||||
// Time it
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
FunctionResult functionResult = await kernel.InvokePromptAsync(promptTestDataGeneration);
|
||||
|
||||
// Stop the timer
|
||||
sw.Stop();
|
||||
|
||||
var functionResultTestDataGen = new FunctionResultTestDataGen(functionResult!, sw.ElapsedMilliseconds);
|
||||
|
||||
Console.WriteLine($"Test data: {functionResultTestDataGen.Result} \n");
|
||||
Console.WriteLine($"Milliseconds: {functionResultTestDataGen.ExecutionTimeInMilliseconds} \n");
|
||||
Console.WriteLine($"Total Tokens: {functionResultTestDataGen.TokenCounts!.TotalTokens} \n");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper classes for the example,
|
||||
/// put in the same file for simplicity
|
||||
/// </summary>
|
||||
/// <remarks>The structure to put the JSON result in a strongly typed object</remarks>
|
||||
private sealed class RootObject
|
||||
{
|
||||
public List<TestCompany> TestCompanies { get; set; }
|
||||
}
|
||||
|
||||
private sealed class TestCompany
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The FunctionResult custom wrapper to parse the result and the tokens
|
||||
/// </summary>
|
||||
private sealed class FunctionResultTestDataGen : FunctionResultExtended
|
||||
{
|
||||
public List<TestCompany> TestCompanies { get; set; }
|
||||
|
||||
public long ExecutionTimeInMilliseconds { get; init; }
|
||||
|
||||
public FunctionResultTestDataGen(FunctionResult functionResult, long executionTimeInMilliseconds)
|
||||
: base(functionResult)
|
||||
{
|
||||
this.TestCompanies = ParseTestCompanies();
|
||||
this.ExecutionTimeInMilliseconds = executionTimeInMilliseconds;
|
||||
this.TokenCounts = this.ParseTokenCounts();
|
||||
}
|
||||
|
||||
private TokenCounts? ParseTokenCounts()
|
||||
{
|
||||
var usage = FunctionResult.Metadata?["Usage"] as ChatTokenUsage;
|
||||
|
||||
return new TokenCounts(
|
||||
completionTokens: usage?.OutputTokenCount ?? 0,
|
||||
promptTokens: usage?.InputTokenCount ?? 0,
|
||||
totalTokens: usage?.TotalTokenCount ?? 0);
|
||||
}
|
||||
|
||||
private static readonly JsonSerializerOptions s_jsonSerializerOptions = new()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
};
|
||||
|
||||
private List<TestCompany> ParseTestCompanies()
|
||||
{
|
||||
// This could also perform some validation logic
|
||||
var rootObject = JsonSerializer.Deserialize<RootObject>(this.Result, s_jsonSerializerOptions);
|
||||
List<TestCompany> companies = rootObject!.TestCompanies;
|
||||
|
||||
return companies;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class TokenCounts(int completionTokens, int promptTokens, int totalTokens)
|
||||
{
|
||||
public int CompletionTokens { get; init; } = completionTokens;
|
||||
public int PromptTokens { get; init; } = promptTokens;
|
||||
public int TotalTokens { get; init; } = totalTokens;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The FunctionResult extension to provide base functionality
|
||||
/// </summary>
|
||||
private class FunctionResultExtended
|
||||
{
|
||||
public string Result { get; init; }
|
||||
public TokenCounts? TokenCounts { get; set; }
|
||||
|
||||
public FunctionResult FunctionResult { get; init; }
|
||||
|
||||
public FunctionResultExtended(FunctionResult functionResult)
|
||||
{
|
||||
this.FunctionResult = functionResult;
|
||||
this.Result = this.ParseResultFromFunctionResult();
|
||||
}
|
||||
|
||||
private string ParseResultFromFunctionResult()
|
||||
{
|
||||
return this.FunctionResult.GetValue<string>() ?? string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.SemanticKernel.Plugins.Core;
|
||||
|
||||
namespace Functions;
|
||||
|
||||
public class MethodFunctions(ITestOutputHelper output) : BaseTest(output)
|
||||
{
|
||||
[Fact]
|
||||
public Task RunAsync()
|
||||
{
|
||||
Console.WriteLine("======== Functions ========");
|
||||
|
||||
// Load native plugin
|
||||
var text = new TextPlugin();
|
||||
|
||||
// Use function without kernel
|
||||
var result = text.Uppercase("ciao!");
|
||||
|
||||
Console.WriteLine(result);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using Microsoft.SemanticKernel;
|
||||
|
||||
namespace Functions;
|
||||
|
||||
/// <summary>
|
||||
/// These samples show advanced usage of method functions.
|
||||
/// </summary>
|
||||
public class MethodFunctions_Advanced(ITestOutputHelper output) : BaseTest(output)
|
||||
{
|
||||
/// <summary>
|
||||
/// This example executes Function1, which in turn executes Function2.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task MethodFunctionsChaining()
|
||||
{
|
||||
Console.WriteLine("Running Method Function Chaining example...");
|
||||
|
||||
var kernel = new Kernel();
|
||||
|
||||
var functions = kernel.ImportPluginFromType<Plugin>();
|
||||
|
||||
var customType = await kernel.InvokeAsync<MyCustomType>(functions["Function1"]);
|
||||
|
||||
Console.WriteLine($"CustomType.Number: {customType!.Number}"); // 2
|
||||
Console.WriteLine($"CustomType.Text: {customType.Text}"); // From Function1 + From Function2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This example shows how to access the custom <see cref="InvocationSettingsAttribute"/> attribute the underlying method wrapped by Kernel Function is annotated with.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task AccessUnderlyingMethodAttributes()
|
||||
{
|
||||
// Import the plugin containing the method with the InvocationSettingsAttribute custom attribute
|
||||
var kernel = new Kernel();
|
||||
|
||||
var functions = kernel.ImportPluginFromType<Plugin>();
|
||||
|
||||
// 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<InvocationSettingsAttribute>();
|
||||
|
||||
Console.WriteLine($"Priority: {invocationSettingsAttribute?.Priority}");
|
||||
}
|
||||
|
||||
private sealed class Plugin
|
||||
{
|
||||
private const string PluginName = nameof(Plugin);
|
||||
|
||||
[KernelFunction]
|
||||
public async Task<MyCustomType> Function1Async(Kernel kernel)
|
||||
{
|
||||
// Execute another function
|
||||
var value = await kernel.InvokeAsync<MyCustomType>(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()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// In order to use custom types, <see cref="TypeConverter"/> should be specified,
|
||||
/// that will convert object instance to string representation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="TypeConverter"/> 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.
|
||||
/// </remarks>
|
||||
[TypeConverter(typeof(MyCustomTypeConverter))]
|
||||
private sealed class MyCustomType
|
||||
{
|
||||
public int Number { get; set; }
|
||||
|
||||
public string? Text { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of <see cref="TypeConverter"/> for <see cref="MyCustomType"/>.
|
||||
/// In this example, object instance is serialized with <see cref="JsonSerializer"/> from System.Text.Json,
|
||||
/// but it's possible to convert object to string using any other serialization logic.
|
||||
/// </summary>
|
||||
private sealed class MyCustomTypeConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) => true;
|
||||
|
||||
/// <summary>
|
||||
/// This method is used to convert object from string to actual type. This will allow to pass object to
|
||||
/// method function which requires it.
|
||||
/// </summary>
|
||||
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
|
||||
{
|
||||
return JsonSerializer.Deserialize<MyCustomType>((string)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is used to convert actual type to string representation, so it can be passed to AI
|
||||
/// for further processing.
|
||||
/// </summary>
|
||||
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,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.ChatCompletion;
|
||||
|
||||
namespace Functions;
|
||||
|
||||
public class MethodFunctions_Types(ITestOutputHelper output) : BaseTest(output)
|
||||
{
|
||||
[Fact]
|
||||
public async Task RunAsync()
|
||||
{
|
||||
Console.WriteLine("======== Method Function types ========");
|
||||
|
||||
var builder = Kernel.CreateBuilder()
|
||||
.AddOpenAIChatCompletion(TestConfiguration.OpenAI.ChatModelId, TestConfiguration.OpenAI.ApiKey);
|
||||
builder.Services.AddLogging(services => services.AddConsole().SetMinimumLevel(LogLevel.Warning));
|
||||
builder.Services.AddSingleton(this.Output);
|
||||
var kernel = builder.Build();
|
||||
kernel.Culture = new CultureInfo("pt-BR");
|
||||
|
||||
// Load native plugin into the kernel function collection, sharing its functions with prompt templates
|
||||
var plugin = kernel.ImportPluginFromType<LocalExamplePlugin>("Examples");
|
||||
|
||||
string folder = RepoFiles.SamplePluginsPath();
|
||||
kernel.ImportPluginFromPromptDirectory(Path.Combine(folder, "SummarizePlugin"));
|
||||
|
||||
// Different ways to invoke a function (not limited to these examples)
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.NoInputWithVoidResult)]);
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.NoInputTaskWithVoidResult)]);
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.InputDateTimeWithStringResult)], new() { ["currentDate"] = DateTime.Now });
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.NoInputTaskWithStringResult)]);
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.MultipleInputsWithVoidResult)], new() { ["x"] = "x string", ["y"] = 100, ["z"] = 1.5 });
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.ComplexInputWithStringResult)], new() { ["complexObject"] = new LocalExamplePlugin(this.Output) });
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.InputStringTaskWithStringResult)], new() { ["echoInput"] = "return this" });
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.InputStringTaskWithVoidResult)], new() { ["x"] = "x input" });
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.NoInputWithFunctionResult)]);
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.NoInputTaskWithFunctionResult)]);
|
||||
|
||||
// Injecting Parameters Examples
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.TaskInjectingKernelFunctionWithStringResult)]);
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.TaskInjectingLoggerWithNoResult)]);
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.TaskInjectingLoggerFactoryWithNoResult)]);
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.TaskInjectingCultureInfoOrIFormatProviderWithStringResult)]);
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.TaskInjectingCancellationTokenWithStringResult)]);
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.TaskInjectingServiceSelectorWithStringResult)]);
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.TaskInjectingKernelWithInputTextAndStringResult)],
|
||||
new()
|
||||
{
|
||||
["textToSummarize"] = @"C# is a modern, versatile language by Microsoft, blending the efficiency of C++
|
||||
with Visual Basic's simplicity. It's ideal for a wide range of applications,
|
||||
emphasizing type safety, modularity, and modern programming paradigms."
|
||||
});
|
||||
|
||||
// You can also use the kernel.Plugins collection to invoke a function
|
||||
await kernel.InvokeAsync(kernel.Plugins["Examples"][nameof(LocalExamplePlugin.NoInputWithVoidResult)]);
|
||||
}
|
||||
}
|
||||
// Task functions when are imported as plugins loose the "Async" suffix if present.
|
||||
#pragma warning disable IDE1006 // Naming Styles
|
||||
|
||||
public class LocalExamplePlugin(ITestOutputHelper output)
|
||||
{
|
||||
private readonly ITestOutputHelper _output = output;
|
||||
|
||||
/// <summary>
|
||||
/// Example of using a void function with no input
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public void NoInputWithVoidResult()
|
||||
{
|
||||
this._output.WriteLine($"Running {nameof(this.NoInputWithVoidResult)} -> No input");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example of using a void task function with no input
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public Task NoInputTaskWithVoidResult()
|
||||
{
|
||||
this._output.WriteLine($"Running {nameof(this.NoInputTaskWithVoidResult)} -> No input");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example of using a function with a DateTime input and a string result
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public string InputDateTimeWithStringResult(DateTime currentDate)
|
||||
{
|
||||
var result = currentDate.ToString(CultureInfo.InvariantCulture);
|
||||
this._output.WriteLine($"Running {nameof(this.InputDateTimeWithStringResult)} -> [currentDate = {currentDate}] -> result: {result}");
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example of using a Task function with no input and a string result
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public Task<string> NoInputTaskWithStringResult()
|
||||
{
|
||||
var result = "string result";
|
||||
this._output.WriteLine($"Running {nameof(this.NoInputTaskWithStringResult)} -> No input -> result: {result}");
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example passing multiple parameters with multiple types
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public void MultipleInputsWithVoidResult(string x, int y, double z)
|
||||
{
|
||||
this._output.WriteLine($"Running {nameof(this.MultipleInputsWithVoidResult)} -> input: [x = {x}, y = {y}, z = {z}]");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example passing a complex object and returning a string result
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public string ComplexInputWithStringResult(object complexObject)
|
||||
{
|
||||
var result = complexObject.GetType().Name;
|
||||
this._output.WriteLine($"Running {nameof(this.ComplexInputWithStringResult)} -> input: [complexObject = {complexObject}] -> result: {result}");
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example using an async task function echoing the input
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public Task<string> InputStringTaskWithStringResult(string echoInput)
|
||||
{
|
||||
this._output.WriteLine($"Running {nameof(this.InputStringTaskWithStringResult)} -> input: [echoInput = {echoInput}] -> result: {echoInput}");
|
||||
return Task.FromResult(echoInput);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example using an async void task with string input
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public Task InputStringTaskWithVoidResult(string x)
|
||||
{
|
||||
this._output.WriteLine($"Running {nameof(this.InputStringTaskWithVoidResult)} -> input: [x = {x}]");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example using a function to return the result of another inner function
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public FunctionResult NoInputWithFunctionResult()
|
||||
{
|
||||
var myInternalFunction = KernelFunctionFactory.CreateFromMethod(() => { });
|
||||
var result = new FunctionResult(myInternalFunction);
|
||||
this._output.WriteLine($"Running {nameof(this.NoInputWithFunctionResult)} -> No input -> result: {result.GetType().Name}");
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example using a task function to return the result of another kernel function
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public async Task<FunctionResult> NoInputTaskWithFunctionResult(Kernel kernel)
|
||||
{
|
||||
var result = await kernel.InvokeAsync(kernel.Plugins["Examples"][nameof(this.NoInputWithVoidResult)]);
|
||||
this._output.WriteLine($"Running {nameof(this.NoInputTaskWithFunctionResult)} -> Injected kernel -> result: {result.GetType().Name}");
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example how to inject Kernel in your function
|
||||
/// This example uses the injected kernel to invoke a plugin from within another function
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public async Task<string> TaskInjectingKernelWithInputTextAndStringResult(Kernel kernel, string textToSummarize)
|
||||
{
|
||||
var summary = await kernel.InvokeAsync<string>(kernel.Plugins["SummarizePlugin"]["Summarize"], new() { ["input"] = textToSummarize });
|
||||
this._output.WriteLine($"Running {nameof(this.TaskInjectingKernelWithInputTextAndStringResult)} -> Injected kernel + input: [textToSummarize: {textToSummarize[..15]}...{textToSummarize[^15..]}] -> result: {summary}");
|
||||
return summary!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example how to inject the executing KernelFunction as a parameter
|
||||
/// </summary>
|
||||
[KernelFunction, Description("Example function injecting itself as a parameter")]
|
||||
public async Task<string> TaskInjectingKernelFunctionWithStringResult(KernelFunction executingFunction)
|
||||
{
|
||||
var result = $"Name: {executingFunction.Name}, Description: {executingFunction.Description}";
|
||||
this._output.WriteLine($"Running {nameof(this.TaskInjectingKernelWithInputTextAndStringResult)} -> Injected Function -> result: {result}");
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example how to inject ILogger in your function
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public Task TaskInjectingLoggerWithNoResult(ILogger logger)
|
||||
{
|
||||
logger.LogWarning("Running {FunctionName} -> Injected Logger", nameof(this.TaskInjectingLoggerWithNoResult));
|
||||
this._output.WriteLine($"Running {nameof(this.TaskInjectingKernelWithInputTextAndStringResult)} -> Injected Logger");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example how to inject ILoggerFactory in your function
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public Task TaskInjectingLoggerFactoryWithNoResult(ILoggerFactory loggerFactory)
|
||||
{
|
||||
loggerFactory
|
||||
.CreateLogger<LocalExamplePlugin>()
|
||||
.LogWarning("Running {FunctionName} -> Injected Logger", nameof(this.TaskInjectingLoggerWithNoResult));
|
||||
|
||||
this._output.WriteLine($"Running {nameof(this.TaskInjectingKernelWithInputTextAndStringResult)} -> Injected Logger");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example how to inject a service selector in your function and use a specific service
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public async Task<string> TaskInjectingServiceSelectorWithStringResult(Kernel kernel, KernelFunction function, KernelArguments arguments, IAIServiceSelector serviceSelector)
|
||||
{
|
||||
ChatMessageContent? chatMessageContent = null;
|
||||
if (serviceSelector.TrySelectAIService<IChatCompletionService>(kernel, function, arguments, out var chatCompletion, out var executionSettings))
|
||||
{
|
||||
chatMessageContent = await chatCompletion.GetChatMessageContentAsync(new ChatHistory("How much is 5 + 5 ?"), executionSettings);
|
||||
}
|
||||
|
||||
var result = chatMessageContent?.Content;
|
||||
this._output.WriteLine($"Running {nameof(this.TaskInjectingKernelWithInputTextAndStringResult)} -> Injected Kernel, KernelFunction, KernelArguments, Service Selector -> result: {result}");
|
||||
return result ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example how to inject CultureInfo or IFormatProvider in your function
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public async Task<string> TaskInjectingCultureInfoOrIFormatProviderWithStringResult(CultureInfo cultureInfo, IFormatProvider formatProvider)
|
||||
{
|
||||
var result = $"Culture Name: {cultureInfo.Name}, FormatProvider Equals CultureInfo?: {formatProvider.Equals(cultureInfo)}";
|
||||
this._output.WriteLine($"Running {nameof(this.TaskInjectingCultureInfoOrIFormatProviderWithStringResult)} -> Injected CultureInfo, IFormatProvider -> result: {result}");
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example how to inject current CancellationToken in your function
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public async Task<string> TaskInjectingCancellationTokenWithStringResult(CancellationToken cancellationToken)
|
||||
{
|
||||
var result = $"Cancellation resquested: {cancellationToken.IsCancellationRequested}";
|
||||
this._output.WriteLine($"Running {nameof(this.TaskInjectingCultureInfoOrIFormatProviderWithStringResult)} -> Injected Cancellation Token -> result: {result}");
|
||||
return result;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Complex type result ToString override";
|
||||
}
|
||||
}
|
||||
#pragma warning restore IDE1006 // Naming Styles
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Reflection;
|
||||
using Microsoft.SemanticKernel;
|
||||
|
||||
namespace Functions;
|
||||
|
||||
public class MethodFunctions_Yaml(ITestOutputHelper output) : BaseTest(output)
|
||||
{
|
||||
private const string FunctionConfig = """
|
||||
name: ValidateTaskId
|
||||
description: Validate a task id.
|
||||
input_variables:
|
||||
- name: kernel
|
||||
description: Kernel instance.
|
||||
- name: taskId
|
||||
description: Task identifier.
|
||||
is_required: true
|
||||
output_variable:
|
||||
description: String indicating whether or not the task id is valid.
|
||||
""";
|
||||
|
||||
/// <summary>
|
||||
/// This example create a plugin and uses a separate configuration file for the function metadata.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Some reasons you would want to do this:
|
||||
/// 1. It's not possible to modify the existing code to add the KernelFunction attribute.
|
||||
/// 2. You want to keep the function metadata separate from the function implementation.
|
||||
/// </remarks>
|
||||
[Fact]
|
||||
public async Task CreateFunctionFromMethodWithYamlConfigAsync()
|
||||
{
|
||||
var kernel = new Kernel();
|
||||
|
||||
var config = KernelFunctionYaml.ToPromptTemplateConfig(FunctionConfig);
|
||||
|
||||
var target = new ValidatorPlugin();
|
||||
MethodInfo method = target.GetType().GetMethod(config.Name!)!;
|
||||
var functions = new List<KernelFunction>();
|
||||
var functionName = config.Name;
|
||||
var description = config.Description;
|
||||
var parameters = config.InputVariables;
|
||||
functions.Add(KernelFunctionFactory.CreateFromMethod(method, target, new()
|
||||
{
|
||||
FunctionName = functionName,
|
||||
Description = description,
|
||||
Parameters = parameters.Select(p => new KernelParameterMetadata(p.Name) { Description = p.Description, IsRequired = p.IsRequired }).ToList(),
|
||||
}));
|
||||
|
||||
var plugin = kernel.ImportPluginFromFunctions("ValidatorPlugin", functions);
|
||||
|
||||
var function = plugin["ValidateTaskId"];
|
||||
var result = await kernel.InvokeAsync(function, new() { { "taskId", "1234" } });
|
||||
Console.WriteLine(result.GetValue<string>());
|
||||
|
||||
Console.WriteLine("Function Metadata:");
|
||||
Console.WriteLine(function.Metadata.Description);
|
||||
Console.WriteLine(function.Metadata.Parameters[0].Description);
|
||||
Console.WriteLine(function.Metadata.Parameters[1].Description);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plugin example with no KernelFunction or Description attributes.
|
||||
/// </summary>
|
||||
private sealed class ValidatorPlugin
|
||||
{
|
||||
public string ValidateTaskId(Kernel kernel, string taskId)
|
||||
{
|
||||
return taskId.Equals("1234", StringComparison.Ordinal) ? "Valid task id" : "Invalid task id";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
|
||||
namespace Functions;
|
||||
|
||||
public class PromptFunctions_Inline(ITestOutputHelper output) : BaseTest(output)
|
||||
{
|
||||
[Fact]
|
||||
public async Task RunAsync()
|
||||
{
|
||||
Console.WriteLine("======== Inline Function Definition ========");
|
||||
|
||||
string openAIModelId = TestConfiguration.OpenAI.ChatModelId;
|
||||
string openAIApiKey = TestConfiguration.OpenAI.ApiKey;
|
||||
|
||||
if (openAIModelId is null || openAIApiKey is null)
|
||||
{
|
||||
Console.WriteLine("OpenAI credentials not found. Skipping example.");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Example: normally you would place prompt templates in a folder to separate
|
||||
* C# code from natural language code, but you can also define a semantic
|
||||
* function inline if you like.
|
||||
*/
|
||||
|
||||
Kernel kernel = Kernel.CreateBuilder()
|
||||
.AddOpenAIChatCompletion(
|
||||
modelId: openAIModelId,
|
||||
apiKey: openAIApiKey)
|
||||
.Build();
|
||||
|
||||
// Function defined using few-shot design pattern
|
||||
string promptTemplate = @"
|
||||
Generate a creative reason or excuse for the given event.
|
||||
Be creative and be funny. Let your imagination run wild.
|
||||
|
||||
Event: I am running late.
|
||||
Excuse: I was being held ransom by giraffe gangsters.
|
||||
|
||||
Event: I haven't been to the gym for a year
|
||||
Excuse: I've been too busy training my pet dragon.
|
||||
|
||||
Event: {{$input}}
|
||||
";
|
||||
|
||||
var excuseFunction = kernel.CreateFunctionFromPrompt(promptTemplate, new OpenAIPromptExecutionSettings() { MaxTokens = 100, Temperature = 0.4, TopP = 1 });
|
||||
|
||||
var result = await kernel.InvokeAsync(excuseFunction, new() { ["input"] = "I missed the F1 final race" });
|
||||
Console.WriteLine(result.GetValue<string>());
|
||||
|
||||
result = await kernel.InvokeAsync(excuseFunction, new() { ["input"] = "sorry I forgot your birthday" });
|
||||
Console.WriteLine(result.GetValue<string>());
|
||||
|
||||
var fixedFunction = kernel.CreateFunctionFromPrompt($"Translate this date {DateTimeOffset.Now:f} to French format", new OpenAIPromptExecutionSettings() { MaxTokens = 100 });
|
||||
|
||||
result = await kernel.InvokeAsync(fixedFunction);
|
||||
Console.WriteLine(result.GetValue<string>());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
using Microsoft.SemanticKernel.Plugins.Core;
|
||||
|
||||
namespace Functions;
|
||||
|
||||
public class PromptFunctions_MultipleArguments(ITestOutputHelper output) : BaseTest(output)
|
||||
{
|
||||
/// <summary>
|
||||
/// Show how to invoke a Method Function written in C# with multiple arguments
|
||||
/// from a Prompt Function written in natural language
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task RunAsync()
|
||||
{
|
||||
Console.WriteLine("======== TemplateMethodFunctionsWithMultipleArguments ========");
|
||||
|
||||
string serviceId = TestConfiguration.AzureOpenAI.ServiceId;
|
||||
string apiKey = TestConfiguration.AzureOpenAI.ApiKey;
|
||||
string deploymentName = TestConfiguration.AzureOpenAI.ChatDeploymentName;
|
||||
string modelId = TestConfiguration.AzureOpenAI.ChatModelId;
|
||||
string endpoint = TestConfiguration.AzureOpenAI.Endpoint;
|
||||
|
||||
if (apiKey is null || deploymentName is null || modelId is null || endpoint is null)
|
||||
{
|
||||
Console.WriteLine("AzureOpenAI modelId, endpoint, apiKey, or deploymentName not found. Skipping example.");
|
||||
return;
|
||||
}
|
||||
|
||||
IKernelBuilder builder = Kernel.CreateBuilder();
|
||||
builder.Services.AddLogging(c => c.AddConsole());
|
||||
builder.AddAzureOpenAIChatCompletion(
|
||||
deploymentName: deploymentName,
|
||||
endpoint: endpoint,
|
||||
serviceId: serviceId,
|
||||
apiKey: apiKey,
|
||||
modelId: modelId);
|
||||
Kernel kernel = builder.Build();
|
||||
|
||||
var arguments = new KernelArguments
|
||||
{
|
||||
["word2"] = " Potter"
|
||||
};
|
||||
|
||||
// Load native plugin into the kernel function collection, sharing its functions with prompt templates
|
||||
// Functions loaded here are available as "text.*"
|
||||
kernel.ImportPluginFromType<TextPlugin>("text");
|
||||
|
||||
// Prompt Function invoking text.Concat method function with named arguments input and input2 where input is a string and input2 is set to a variable from context called word2.
|
||||
const string FunctionDefinition = @"
|
||||
Write a haiku about the following: {{text.Concat input='Harry' input2=$word2}}
|
||||
";
|
||||
|
||||
// This allows to see the prompt before it's sent to OpenAI
|
||||
Console.WriteLine("--- Rendered Prompt");
|
||||
var promptTemplateFactory = new KernelPromptTemplateFactory();
|
||||
var promptTemplate = promptTemplateFactory.Create(new PromptTemplateConfig(FunctionDefinition));
|
||||
var renderedPrompt = await promptTemplate.RenderAsync(kernel, arguments);
|
||||
Console.WriteLine(renderedPrompt);
|
||||
|
||||
// Run the prompt / prompt function
|
||||
var haiku = kernel.CreateFunctionFromPrompt(FunctionDefinition, new OpenAIPromptExecutionSettings() { MaxTokens = 100 });
|
||||
|
||||
// Show the result
|
||||
Console.WriteLine("--- Prompt Function result");
|
||||
var result = await kernel.InvokeAsync(haiku, arguments);
|
||||
Console.WriteLine(result.GetValue<string>());
|
||||
|
||||
/* OUTPUT:
|
||||
|
||||
--- Rendered Prompt
|
||||
|
||||
Write a haiku about the following: Harry Potter
|
||||
|
||||
--- Prompt Function result
|
||||
A boy with a scar,
|
||||
Wizarding world he explores,
|
||||
Harry Potter's tale.
|
||||
*/
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user