From 4487ca3d80df8f34624a3c24debea0d6995b9c4c Mon Sep 17 00:00:00 2001
From: Mark Wallace <127216156+markwallace-microsoft@users.noreply.github.com>
Date: Tue, 12 Dec 2023 15:30:14 +0000
Subject: [PATCH] .Net: Add new getting started samples (#4191)
### Motivation and Context
Closes #4019
### Description
### Contribution Checklist
- [ ] The code builds clean without any errors or warnings
- [ ] The PR follows the [SK Contribution
Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
and the [pre-submission formatting
script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts)
raises no violations
- [ ] All unit tests pass, and I have added new tests where possible
- [ ] I didn't break anyone :smile:
---
...75_GPTVision.cs => Example74_GPTVision.cs} | 2 +-
.../Getting_Started/Step1_Create_Kernel.cs | 37 ++++++++
.../Getting_Started/Step2_Add_Plugins.cs | 45 ++++++++++
.../Getting_Started/Step3_Yaml_Prompt.cs | 87 +++++++++++++++++++
.../Step4_Dependency_Injection.cs | 71 +++++++++++++++
.../Step5_Pipelining.cs} | 5 +-
.../KernelSyntaxExamples.csproj | 2 +-
.../samples/KernelSyntaxExamples/Program.cs | 8 +-
8 files changed, 247 insertions(+), 10 deletions(-)
rename dotnet/samples/KernelSyntaxExamples/{Example75_GPTVision.cs => Example74_GPTVision.cs} (96%)
create mode 100644 dotnet/samples/KernelSyntaxExamples/Getting_Started/Step1_Create_Kernel.cs
create mode 100644 dotnet/samples/KernelSyntaxExamples/Getting_Started/Step2_Add_Plugins.cs
create mode 100644 dotnet/samples/KernelSyntaxExamples/Getting_Started/Step3_Yaml_Prompt.cs
create mode 100644 dotnet/samples/KernelSyntaxExamples/Getting_Started/Step4_Dependency_Injection.cs
rename dotnet/samples/KernelSyntaxExamples/{Example74_Pipelining.cs => Getting_Started/Step5_Pipelining.cs} (98%)
diff --git a/dotnet/samples/KernelSyntaxExamples/Example75_GPTVision.cs b/dotnet/samples/KernelSyntaxExamples/Example74_GPTVision.cs
similarity index 96%
rename from dotnet/samples/KernelSyntaxExamples/Example75_GPTVision.cs
rename to dotnet/samples/KernelSyntaxExamples/Example74_GPTVision.cs
index 9d4d3a4dd5cb..891b5d6a5833 100644
--- a/dotnet/samples/KernelSyntaxExamples/Example75_GPTVision.cs
+++ b/dotnet/samples/KernelSyntaxExamples/Example74_GPTVision.cs
@@ -9,7 +9,7 @@
* This example shows how to use GPT Vision model with different content types (text and image).
*/
// ReSharper disable once InconsistentNaming
-public static class Example75_GPTVision
+public static class Example74_GPTVision
{
public static async Task RunAsync()
{
diff --git a/dotnet/samples/KernelSyntaxExamples/Getting_Started/Step1_Create_Kernel.cs b/dotnet/samples/KernelSyntaxExamples/Getting_Started/Step1_Create_Kernel.cs
new file mode 100644
index 000000000000..2a6f12e589a8
--- /dev/null
+++ b/dotnet/samples/KernelSyntaxExamples/Getting_Started/Step1_Create_Kernel.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Threading.Tasks;
+using Microsoft.SemanticKernel;
+
+/**
+ * This example shows how to create and use a .
+ */
+public static class Step1_Create_Kernel
+{
+ ///
+ /// Show how to create a and use it to execute prompts.
+ ///
+ public static async Task RunAsync()
+ {
+ // Create a kernel with OpenAI chat completion
+ Kernel kernel = Kernel.CreateBuilder()
+ .AddOpenAIChatCompletion(
+ modelId: TestConfiguration.OpenAI.ChatModelId,
+ apiKey: TestConfiguration.OpenAI.ApiKey)
+ .Build();
+
+ // Example 1. Invoke the kernel with a prompt and display the result
+ Console.WriteLine(await kernel.InvokePromptAsync("What color is the sky?"));
+
+ // Example 2. Invoke the kernel with a templated prompt and display the result
+ KernelArguments arguments = new() { { "topic", "sea" } };
+ Console.WriteLine(await kernel.InvokePromptAsync("What color is the {{$topic}}?", arguments));
+
+ // Example 3. Invoke the kernel with a templated prompt and stream the results to the display
+ await foreach (var update in kernel.InvokePromptStreamingAsync("What color is the {{$topic}}? Provide a detailed explanation.", arguments))
+ {
+ Console.Write(update);
+ }
+ }
+}
diff --git a/dotnet/samples/KernelSyntaxExamples/Getting_Started/Step2_Add_Plugins.cs b/dotnet/samples/KernelSyntaxExamples/Getting_Started/Step2_Add_Plugins.cs
new file mode 100644
index 000000000000..c388a0f06674
--- /dev/null
+++ b/dotnet/samples/KernelSyntaxExamples/Getting_Started/Step2_Add_Plugins.cs
@@ -0,0 +1,45 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// ReSharper disable once InconsistentNaming
+using System;
+using System.Threading.Tasks;
+using Microsoft.SemanticKernel;
+using Microsoft.SemanticKernel.Connectors.OpenAI;
+/**
+* This example shows how to load a instances.
+*/
+public static class Step2_Add_Plugins
+{
+ ///
+ /// Shows different ways to load a instances.
+ ///
+ public static async Task RunAsync()
+ {
+ // Create a kernel with OpenAI chat completion
+ IKernelBuilder kernelBuilder = Kernel.CreateBuilder();
+ kernelBuilder.AddOpenAIChatCompletion(
+ modelId: TestConfiguration.OpenAI.ChatModelId,
+ apiKey: TestConfiguration.OpenAI.ApiKey);
+ kernelBuilder.Plugins.AddFromType();
+ Kernel kernel = kernelBuilder.Build();
+
+ // Example 1. Invoke the kernel with a prompt that asks the AI for inromation it cannot provide and may hallucinate
+ Console.WriteLine(await kernel.InvokePromptAsync("How many days until Christmas?"));
+
+ // Example 2. Invoke the kernel with a templated prompt that invokes a plugin and display the result
+ Console.WriteLine(await kernel.InvokePromptAsync("The current time is {{TimeInformation.GetCurrentUtcTime}}. How many days until Christmas?"));
+
+ // Example 3. Invoke the kernel with a prompt and allow the AI to automatically invoke functions
+ OpenAIPromptExecutionSettings settings = new() { ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions };
+ Console.WriteLine(await kernel.InvokePromptAsync("How many days until Christmas? Explain your thinking.", new(settings)));
+ }
+
+ ///
+ /// A plugin that returns the current time.
+ ///
+ public class TimeInformation
+ {
+ [KernelFunction]
+ public string GetCurrentUtcTime() => DateTime.UtcNow.ToString("R");
+ }
+}
diff --git a/dotnet/samples/KernelSyntaxExamples/Getting_Started/Step3_Yaml_Prompt.cs b/dotnet/samples/KernelSyntaxExamples/Getting_Started/Step3_Yaml_Prompt.cs
new file mode 100644
index 000000000000..d1188f9eecf5
--- /dev/null
+++ b/dotnet/samples/KernelSyntaxExamples/Getting_Started/Step3_Yaml_Prompt.cs
@@ -0,0 +1,87 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Threading.Tasks;
+using Microsoft.SemanticKernel;
+using Microsoft.SemanticKernel.PromptTemplate.Handlebars;
+
+/**
+ * This example shows how to create a prompt from a YAML resource.
+ */
+public static class Step3_Yaml_Prompt
+{
+ ///
+ /// Show how to create a prompt from a YAML resource.
+ ///
+ public static async Task RunAsync()
+ {
+ // Create a kernel with OpenAI chat completion
+ Kernel kernel = Kernel.CreateBuilder()
+ .AddOpenAIChatCompletion(
+ modelId: TestConfiguration.OpenAI.ChatModelId,
+ apiKey: TestConfiguration.OpenAI.ApiKey)
+ .Build();
+
+ // Load prompt from resource
+ var function = kernel.CreateFunctionFromPromptYaml(GenerateStoryYaml);
+
+ // Invoke the prompt function and display the result
+ Console.WriteLine(await kernel.InvokeAsync(function, arguments: new()
+ {
+ { "topic", "Dog" },
+ { "length", "3" },
+ }));
+
+ // Load prompt from resource
+ function = kernel.CreateFunctionFromPromptYaml(GenerateStoryHandlebarsYaml, new HandlebarsPromptTemplateFactory());
+
+ // Invoke the prompt function and display the result
+ Console.WriteLine(await kernel.InvokeAsync(function, arguments: new()
+ {
+ { "topic", "Cat" },
+ { "length", "3" },
+ }));
+ }
+
+ private const string GenerateStoryYaml = @"
+name: GenerateStory
+template: |
+ Tell a story about {{$topic}} that is {{$length}} sentences long.
+template_format: semantic-kernel
+description: A function that generates a story about a topic.
+input_variables:
+ - name: topic
+ description: The topic of the story.
+ is_required: true
+ - name: length
+ description: The number of sentences in the story.
+ is_required: true
+output_variable:
+ description: The generated story.
+execution_settings:
+ - temperature: 0.6
+";
+
+ private const string GenerateStoryHandlebarsYaml = @"
+name: GenerateStory
+template: |
+ Tell a story about {{topic}} that is {{length}} sentences long.
+template_format: handlebars
+description: A function that generates a story about a topic.
+input_variables:
+ - name: topic
+ description: The topic of the story.
+ is_required: true
+ - name: length
+ description: The number of sentences in the story.
+ is_required: true
+output_variable:
+ description: The generated story.
+execution_settings:
+ - model_id: gpt-4
+ temperature: 0.6
+ - model_id: gpt-3.5-turbo
+ temperature: 0.4
+ - temperature: 0.5
+";
+}
diff --git a/dotnet/samples/KernelSyntaxExamples/Getting_Started/Step4_Dependency_Injection.cs b/dotnet/samples/KernelSyntaxExamples/Getting_Started/Step4_Dependency_Injection.cs
new file mode 100644
index 000000000000..643b68996bdd
--- /dev/null
+++ b/dotnet/samples/KernelSyntaxExamples/Getting_Started/Step4_Dependency_Injection.cs
@@ -0,0 +1,71 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// ReSharper disable once InconsistentNaming
+// ReSharper disable once InconsistentNaming
+using System;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.SemanticKernel;
+using RepoUtils;
+
+// ReSharper disable once InconsistentNaming
+/**
+* This example shows how to using Dependency Injection with the Semantic Kernel
+*/
+public static class Step4_Dependency_Injection
+{
+ ///
+ /// Show how to create a that participates in Dependency Injection.
+ ///
+ public static async Task RunAsync()
+ {
+ // If an application follows DI guidelines, the following line is unnecessary because DI will inject an instance of the KernelClient class to a class that references it.
+ // DI container guidelines - https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines#recommendations
+ var serviceProvider = BuildServiceProvider();
+ var kernel = serviceProvider.GetRequiredService();
+
+ // Invoke the kernel with a templated prompt and stream the results to the display
+ KernelArguments arguments = new() { { "topic", "earth when viewed from space" } };
+ await foreach (var update in kernel.InvokePromptStreamingAsync("What color is the {{$topic}}? Provide a detailed explanation.", arguments))
+ {
+ Console.Write(update);
+ }
+ }
+
+ ///
+ /// Build a ServiceProvdier that can be used to resolve services.
+ ///
+ private static ServiceProvider BuildServiceProvider()
+ {
+ var collection = new ServiceCollection();
+ collection.AddSingleton(ConsoleLogger.LoggerFactory);
+
+ var kernelBuilder = collection.AddKernel();
+ kernelBuilder.Services.AddOpenAITextGeneration(TestConfiguration.OpenAI.ModelId, TestConfiguration.OpenAI.ApiKey);
+ kernelBuilder.Plugins.AddFromType();
+
+ return collection.BuildServiceProvider();
+ }
+
+ ///
+ /// A plugin that returns the current time.
+ ///
+ public class TimeInformation
+ {
+ private readonly ILogger _logger;
+
+ public TimeInformation(ILoggerFactory loggerFactory)
+ {
+ this._logger = loggerFactory.CreateLogger(typeof(TimeInformation));
+ }
+
+ [KernelFunction]
+ public string GetCurrentUtcTime()
+ {
+ var utcNow = DateTime.UtcNow.ToString("R");
+ this._logger.LogInformation("Returning current time {0}", utcNow);
+ return utcNow;
+ }
+ }
+}
diff --git a/dotnet/samples/KernelSyntaxExamples/Example74_Pipelining.cs b/dotnet/samples/KernelSyntaxExamples/Getting_Started/Step5_Pipelining.cs
similarity index 98%
rename from dotnet/samples/KernelSyntaxExamples/Example74_Pipelining.cs
rename to dotnet/samples/KernelSyntaxExamples/Getting_Started/Step5_Pipelining.cs
index be7431c13b27..755076b72fc0 100644
--- a/dotnet/samples/KernelSyntaxExamples/Example74_Pipelining.cs
+++ b/dotnet/samples/KernelSyntaxExamples/Getting_Started/Step5_Pipelining.cs
@@ -10,10 +10,7 @@
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
-#pragma warning disable RCS1110 // Declare type inside namespace.
-#pragma warning disable CA5394
-
-public static class Example74_Pipelining
+public static class Step5_Pipelining
{
///
/// Provides an example of combining multiple functions into a single function that invokes
diff --git a/dotnet/samples/KernelSyntaxExamples/KernelSyntaxExamples.csproj b/dotnet/samples/KernelSyntaxExamples/KernelSyntaxExamples.csproj
index a6d3b58afc16..ba66fb07332b 100644
--- a/dotnet/samples/KernelSyntaxExamples/KernelSyntaxExamples.csproj
+++ b/dotnet/samples/KernelSyntaxExamples/KernelSyntaxExamples.csproj
@@ -9,7 +9,7 @@
Exe
false
- CA1050,CA1707,CA2007,VSTHRD111,CS1591,SKEXP0001,SKEXP0002,SKEXP0003,SKEXP0004,SKEXP0010,SKEXP0011,,SKEXP0012,SKEXP0020,SKEXP0021,SKEXP0022,SKEXP0023,SKEXP0024,SKEXP0025,SKEXP0026,SKEXP0027,SKEXP0028,SKEXP0029,SKEXP0030,SKEXP0031,SKEXP0032,SKEXP0040,SKEXP0041,SKEXP0050,SKEXP0051,SKEXP0052,SKEXP0053,SKEXP0054,SKEXP0055,SKEXP0060,SKEXP0061,SKEXP0101,SKEXP0102
+ CA1050,CA1707,CA2007,VSTHRD111,CS1591,RCS1110,CA5394,SKEXP0001,SKEXP0002,SKEXP0003,SKEXP0004,SKEXP0010,SKEXP0011,,SKEXP0012,SKEXP0020,SKEXP0021,SKEXP0022,SKEXP0023,SKEXP0024,SKEXP0025,SKEXP0026,SKEXP0027,SKEXP0028,SKEXP0029,SKEXP0030,SKEXP0031,SKEXP0032,SKEXP0040,SKEXP0041,SKEXP0050,SKEXP0051,SKEXP0052,SKEXP0053,SKEXP0054,SKEXP0055,SKEXP0060,SKEXP0061,SKEXP0101,SKEXP0102
diff --git a/dotnet/samples/KernelSyntaxExamples/Program.cs b/dotnet/samples/KernelSyntaxExamples/Program.cs
index de2b07fdf982..bebfd6973b42 100644
--- a/dotnet/samples/KernelSyntaxExamples/Program.cs
+++ b/dotnet/samples/KernelSyntaxExamples/Program.cs
@@ -33,7 +33,6 @@ public static async Task Main(string[] args)
private static async Task RunExamplesAsync(string? filter, CancellationToken cancellationToken)
{
var examples = (Assembly.GetExecutingAssembly().GetTypes())
- .Where(type => type.Name.StartsWith("Example", StringComparison.OrdinalIgnoreCase))
.Select(type => type.Name).ToList();
// Filter and run examples
@@ -43,15 +42,15 @@ private static async Task RunExamplesAsync(string? filter, CancellationToken can
{
try
{
- Console.WriteLine($"Running {example}...");
-
var method = Assembly.GetExecutingAssembly().GetType(example)?.GetMethod("RunAsync");
if (method == null)
{
- Console.WriteLine($"Example {example} not found");
+ // Skip if the type does not have a RunAsync method
continue;
}
+ Console.WriteLine($"Running {example}...");
+
bool hasCancellationToken = method.GetParameters().Any(param => param.ParameterType == typeof(CancellationToken));
var taskParameters = hasCancellationToken ? new object[] { cancellationToken } : null;
@@ -99,6 +98,7 @@ private static async Task SafeWaitAsync(this Task task,
try
{
await task.WaitAsync(cancellationToken);
+ Console.WriteLine();
Console.WriteLine("== DONE ==");
}
catch (ConfigurationNotFoundException ex)