Skip to content

Commit

Permalink
.Net Kernel Examples as tests (#4526)
Browse files Browse the repository at this point in the history
### Motivation and Context

Convert Kernel Examples as Integration Tests.
  • Loading branch information
RogerBarreto authored Jan 16, 2024
1 parent f9bef63 commit b99b780
Show file tree
Hide file tree
Showing 70 changed files with 1,565 additions and 1,204 deletions.
3 changes: 2 additions & 1 deletion dotnet/samples/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ resharper_condition_is_always_true_or_false_according_to_nullable_api_contract_h
resharper_inconsistent_naming_highlighting = none # InconsistentNaming
resharper_equal_expression_comparison_highlighting = none # EqualExpressionComparison
resharper_check_namespace_highlighting = none # CheckNamespace
resharper_arrange_object_creation_when_type_not_evident_highlighting = none # Disable "Arrange object creation when type is not evident" highlighting
resharper_arrange_object_creation_when_type_not_evident_highlighting = none # Disable "Arrange object creation when type is not evident" highlighting
resharper_arrange_this_qualifier_highlighting = none # Disable "Arrange 'this.' qualifier" highlighting
47 changes: 47 additions & 0 deletions dotnet/samples/KernelSyntaxExamples/BaseTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) Microsoft. All rights reserved.

using Microsoft.Extensions.Configuration;
using RepoUtils;
using Xunit.Abstractions;

namespace Examples;

public abstract class BaseTest
{
protected ITestOutputHelper Output { get; }

protected BaseTest(ITestOutputHelper output)
{
this.Output = output;
LoadUserSecrets();
}

private static void LoadUserSecrets()
{
IConfigurationRoot configRoot = new ConfigurationBuilder()
.AddJsonFile("appsettings.Development.json", true)
.AddEnvironmentVariables()
.AddUserSecrets<Env>()
.Build();

TestConfiguration.Initialize(configRoot);
}

/// <summary>
/// This method can be substituted by Console.WriteLine when used in Console apps.
/// </summary>
/// <param name="target">Target object to write</param>
protected void WriteLine(object? target = null)
{
this.Output.WriteLine(target ?? string.Empty);
}

/// <summary>
/// Current interface ITestOutputHelper does not have a Write method. This extension method adds it to make it analogous to Console.Write when used in Console apps.
/// </summary>
/// <param name="target">Target object to write</param>
protected void Write(object? target = null)
{
this.Output.WriteLine(target ?? string.Empty);
}
}
18 changes: 13 additions & 5 deletions dotnet/samples/KernelSyntaxExamples/Example01_MethodFunctions.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Threading.Tasks;
using Microsoft.SemanticKernel.Plugins.Core;
using Xunit;
using Xunit.Abstractions;

public static class Example01_MethodFunctions
namespace Examples;

public class Example01_MethodFunctions : BaseTest
{
public static Task RunAsync()
[Fact]
public Task RunAsync()
{
Console.WriteLine("======== Functions ========");
this.WriteLine("======== Functions ========");

// Load native plugin
var text = new TextPlugin();

// Use function without kernel
var result = text.Uppercase("ciao!");

Console.WriteLine(result);
this.WriteLine(result);

return Task.CompletedTask;
}

public Example01_MethodFunctions(ITestOutputHelper output) : base(output)
{
}
}
20 changes: 14 additions & 6 deletions dotnet/samples/KernelSyntaxExamples/Example03_Arguments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@
using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using Plugins;
using Xunit;
using Xunit.Abstractions;

namespace Examples;
// This example shows how to use kernel arguments when invoking functions.
public static class Example03_Arguments
public class Example03_Arguments : BaseTest
{
public static async Task RunAsync()
[Fact]
public async Task RunAsync()
{
Console.WriteLine("======== Arguments ========");
this.WriteLine("======== Arguments ========");

Kernel kernel = new();
var textPlugin = kernel.ImportPluginFromType<StaticTextPlugin>();
Expand All @@ -26,16 +30,20 @@ public static async Task RunAsync()

// Specify and get the value type as generic parameter
string? resultValue = await kernel.InvokeAsync<string>(textPlugin["AppendDay"], arguments);
Console.WriteLine($"string -> {resultValue}");
this.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>()}");
this.WriteLine($"FunctionResult.GetValue<string>() -> {functionResult.GetValue<string>()}");

// FunctionResult.ToString() automatically converts the result to string
Console.WriteLine($"FunctionResult.ToString() -> {functionResult}");
this.WriteLine($"FunctionResult.ToString() -> {functionResult}");
}

public Example03_Arguments(ITestOutputHelper output) : base(output)
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,24 @@
using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Xunit;
using Xunit.Abstractions;

public static class Example05_InlineFunctionDefinition
namespace Examples;

public class Example05_InlineFunctionDefinition : BaseTest
{
public static async Task RunAsync()
[Fact]
public async Task RunAsync()
{
Console.WriteLine("======== Inline Function Definition ========");
this.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.");
this.WriteLine("OpenAI credentials not found. Skipping example.");
return;
}

Expand Down Expand Up @@ -49,14 +54,18 @@ Be creative and be funny. Let your imagination run wild.
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>());
this.WriteLine(result.GetValue<string>());

result = await kernel.InvokeAsync(excuseFunction, new() { ["input"] = "sorry I forgot your birthday" });
Console.WriteLine(result.GetValue<string>());
this.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>());
this.WriteLine(result.GetValue<string>());
}

public Example05_InlineFunctionDefinition(ITestOutputHelper output) : base(output)
{
}
}
26 changes: 17 additions & 9 deletions dotnet/samples/KernelSyntaxExamples/Example06_TemplateLanguage.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Plugins.Core;
using Xunit;
using Xunit.Abstractions;

public static class Example06_TemplateLanguage
namespace Examples;

public class Example06_TemplateLanguage : BaseTest
{
/// <summary>
/// Show how to invoke a Method Function written in C#
/// from a Prompt Function written in natural language
/// </summary>
public static async Task RunAsync()
[Fact]
public async Task RunAsync()
{
Console.WriteLine("======== TemplateLanguage ========");
this.WriteLine("======== TemplateLanguage ========");

string openAIModelId = TestConfiguration.OpenAI.ChatModelId;
string openAIApiKey = TestConfiguration.OpenAI.ApiKey;

if (openAIModelId == null || openAIApiKey == null)
{
Console.WriteLine("OpenAI credentials not found. Skipping example.");
this.WriteLine("OpenAI credentials not found. Skipping example.");
return;
}

Expand All @@ -46,19 +50,19 @@ Is it weekend time (weekend/not weekend)?
";

// This allows to see the prompt before it's sent to OpenAI
Console.WriteLine("--- Rendered Prompt");
this.WriteLine("--- Rendered Prompt");
var promptTemplateFactory = new KernelPromptTemplateFactory();
var promptTemplate = promptTemplateFactory.Create(new PromptTemplateConfig(FunctionDefinition));
var renderedPrompt = await promptTemplate.RenderAsync(kernel);
Console.WriteLine(renderedPrompt);
this.WriteLine(renderedPrompt);

// Run the prompt / prompt function
var kindOfDay = kernel.CreateFunctionFromPrompt(FunctionDefinition, new OpenAIPromptExecutionSettings() { MaxTokens = 100 });

// Show the result
Console.WriteLine("--- Prompt Function result");
this.WriteLine("--- Prompt Function result");
var result = await kernel.InvokeAsync(kindOfDay);
Console.WriteLine(result.GetValue<string>());
this.WriteLine(result.GetValue<string>());

/* OUTPUT:
Expand All @@ -81,4 +85,8 @@ Is it weekend time (weekend/not weekend)?
}
*/
}

public Example06_TemplateLanguage(ITestOutputHelper output) : base(output)
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,27 @@
using Microsoft.SemanticKernel.Plugins.Web;
using Microsoft.SemanticKernel.Plugins.Web.Bing;
using Microsoft.SemanticKernel.Plugins.Web.Google;
using Xunit;
using Xunit.Abstractions;

namespace Examples;

/// <summary>
/// The example shows how to use Bing and Google to search for current data
/// you might want to import into your system, e.g. providing AI prompts with
/// recent information, or for AI to generate recent information to display to users.
/// </summary>
public static class Example07_BingAndGooglePlugins
public class Example07_BingAndGooglePlugins : BaseTest
{
public static async Task RunAsync()
[Fact(Skip = "Setup Credentials")]
public async Task RunAsync()
{
string openAIModelId = TestConfiguration.OpenAI.ChatModelId;
string openAIApiKey = TestConfiguration.OpenAI.ApiKey;

if (openAIModelId == null || openAIApiKey == null)
{
Console.WriteLine("OpenAI credentials not found. Skipping example.");
this.WriteLine("OpenAI credentials not found. Skipping example.");
return;
}

Expand All @@ -36,7 +41,7 @@ public static async Task RunAsync()
string bingApiKey = TestConfiguration.Bing.ApiKey;
if (bingApiKey == null)
{
Console.WriteLine("Bing credentials not found. Skipping example.");
this.WriteLine("Bing credentials not found. Skipping example.");
}
else
{
Expand All @@ -53,7 +58,7 @@ public static async Task RunAsync()

if (googleApiKey == null || googleSearchEngineId == null)
{
Console.WriteLine("Google credentials not found. Skipping example.");
this.WriteLine("Google credentials not found. Skipping example.");
}
else
{
Expand All @@ -62,22 +67,23 @@ public static async Task RunAsync()
searchEngineId: googleSearchEngineId);
var google = new WebSearchEnginePlugin(googleConnector);
kernel.ImportPluginFromObject(new WebSearchEnginePlugin(googleConnector), "google");
// ReSharper disable once ArrangeThisQualifier
await Example1Async(kernel, "google");
}
}

private static async Task Example1Async(Kernel kernel, string searchPluginName)
private async Task Example1Async(Kernel kernel, string searchPluginName)
{
Console.WriteLine("======== Bing and Google Search Plugins ========");
this.WriteLine("======== Bing and Google Search Plugins ========");

// Run
var question = "What's the largest building in the world?";
var function = kernel.Plugins[searchPluginName]["search"];
var result = await kernel.InvokeAsync(function, new() { ["query"] = question });

Console.WriteLine(question);
Console.WriteLine($"----{searchPluginName}----");
Console.WriteLine(result.GetValue<string>());
this.WriteLine(question);
this.WriteLine($"----{searchPluginName}----");
this.WriteLine(result.GetValue<string>());

/* OUTPUT:
Expand All @@ -92,9 +98,9 @@ private static async Task Example1Async(Kernel kernel, string searchPluginName)
*/
}

private static async Task Example2Async(Kernel kernel)
private async Task Example2Async(Kernel kernel)
{
Console.WriteLine("======== Use Search Plugin to answer user questions ========");
this.WriteLine("======== Use Search Plugin to answer user questions ========");

const string SemanticFunction = @"Answer questions only when you know the facts or the information is provided.
When you don't have sufficient information you reply with a list of commands to find the information needed.
Expand Down Expand Up @@ -130,7 +136,7 @@ [END OF EXAMPLES]
Answer: ";

var question = "Who is the most followed person on TikTok right now? What's the exchange rate EUR:USD?";
Console.WriteLine(question);
this.WriteLine(question);

var oracle = kernel.CreateFunctionFromPrompt(SemanticFunction, new OpenAIPromptExecutionSettings() { MaxTokens = 150, Temperature = 0, TopP = 1 });

Expand All @@ -148,11 +154,11 @@ [END OF EXAMPLES]
var promptTemplateFactory = new KernelPromptTemplateFactory();
var promptTemplate = promptTemplateFactory.Create(new PromptTemplateConfig(result));

Console.WriteLine("---- Fetching information from Bing...");
this.WriteLine("---- Fetching information from Bing...");
var information = await promptTemplate.RenderAsync(kernel);

Console.WriteLine("Information found:");
Console.WriteLine(information);
this.WriteLine("Information found:");
this.WriteLine(information);

// Run the prompt function again, now including information from Bing
answer = await kernel.InvokeAsync(oracle, new KernelArguments()
Expand All @@ -164,11 +170,11 @@ [END OF EXAMPLES]
}
else
{
Console.WriteLine("AI had all the information, no need to query Bing.");
this.WriteLine("AI had all the information, no need to query Bing.");
}

Console.WriteLine("---- ANSWER:");
Console.WriteLine(answer.GetValue<string>());
this.WriteLine("---- ANSWER:");
this.WriteLine(answer.GetValue<string>());

/* OUTPUT:
Expand All @@ -188,4 +194,8 @@ rate when sending money. Check send rates Convert Euro to US Dollar Convert US D
* The exchange rate for EUR to USD is 1.1037097 US Dollars for 1 Euro.
*/
}

public Example07_BingAndGooglePlugins(ITestOutputHelper output) : base(output)
{
}
}
Loading

0 comments on commit b99b780

Please sign in to comment.