chore: import upstream snapshot with attribution
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:21:23 +08:00
commit b957a53def
5423 changed files with 863745 additions and 0 deletions
@@ -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.
*/
}
}