Merge branch 'main' into update-modelid

This commit is contained in:
Sandra Ahlgrimm
2024-02-02 12:15:47 +01:00
committed by GitHub
236 changed files with 8268 additions and 2442 deletions
+1 -2
View File
@@ -15,8 +15,7 @@ Add Semantic Kernel events to your calendar - we're running two community calls
* Americas timezone: download the [calendar.ics](https://aka.ms/sk-community-calendar) file.
* Asia Pacific timezone: download the [calendar-APAC.ics](https://aka.ms/sk-community-calendar-apac) file.
To keep topics organized, please submit what you'd like us to cover here:
[https://forms.office.com/r/BbXFzmmFys](https://forms.office.com/r/BbXFzmmFys)
If you have any questions or if you would like to showcase your project(s), please email what you'd like us to cover here: skofficehours[at]microsoft.com.
If you are unable to make it live, all meetings will be recorded and posted online.
+9 -4
View File
@@ -13,14 +13,19 @@
<PackageVersion Include="Markdig" Version="0.34.0" />
<PackageVersion Include="Handlebars.Net" Version="2.1.4" />
<PackageVersion Include="JsonSchema.Net.Generation" Version="3.5.1" />
<PackageVersion Include="Microsoft.Azure.Functions.Worker" Version="1.20.1" />
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.1.0" />
<PackageVersion Include="Microsoft.Azure.Kusto.Data" Version="11.3.5" />
<PackageVersion Include="Microsoft.Azure.WebJobs.Extensions.OpenApi" Version="1.5.1" />
<PackageVersion Include="Microsoft.Bcl.HashCode" Version="[1.1.0, )" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
<PackageVersion Include="Microsoft.Bcl.TimeProvider" Version="8.0.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.3.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.0" />
<PackageVersion Include="Microsoft.Bcl.TimeProvider" Version="8.0.1" />
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="8.0.0" />
<PackageVersion Include="System.Linq.Async" Version="6.0.1" />
<PackageVersion Include="System.Numerics.Tensors" Version="8.0.0" />
<PackageVersion Include="System.Text.Json" Version="8.0.0" />
<PackageVersion Include="System.Text.Json" Version="8.0.1" />
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
<PackageVersion Include="System.ValueTuple" Version="4.5.0" />
<!-- Tokenizers -->
@@ -36,7 +41,7 @@
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="8.1.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
@@ -99,7 +104,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageVersion Include="Roslynator.Analyzers" Version="[4.3.0]" />
<PackageVersion Include="Roslynator.Analyzers" Version="[4.10.0]" />
<PackageReference Include="Roslynator.Analyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+18 -6
View File
@@ -116,15 +116,9 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "System", "System", "{3CDE10B2-AE8F-4FC4-8D55-92D4AD32E144}"
ProjectSection(SolutionItems) = preProject
src\InternalUtilities\src\System\EnvExtensions.cs = src\InternalUtilities\src\System\EnvExtensions.cs
src\InternalUtilities\src\System\InternalTypeConverter.cs = src\InternalUtilities\src\System\InternalTypeConverter.cs
src\InternalUtilities\src\System\TypeConverterFactory.cs = src\InternalUtilities\src\System\TypeConverterFactory.cs
src\InternalUtilities\src\System\NonNullCollection.cs = src\InternalUtilities\src\System\NonNullCollection.cs
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Type", "Type", "{E85EA4D0-BB7E-4DFD-882F-A76EB8C0B8FF}"
ProjectSection(SolutionItems) = preProject
src\InternalUtilities\src\Type\TypeExtensions.cs = src\InternalUtilities\src\Type\TypeExtensions.cs
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugins.Core", "src\Plugins\Plugins.Core\Plugins.Core.csproj", "{0D0C4DAD-E6BC-4504-AE3A-EEA4E35920C1}"
EndProject
@@ -197,6 +191,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Experimental.Orchestration.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Experimental.Orchestration.Flow.UnitTests", "src\Experimental\Orchestration.Flow.UnitTests\Experimental.Orchestration.Flow.UnitTests.csproj", "{731CC542-8BE9-42D4-967D-99206EC2B310}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DocumentationExamples", "samples\DocumentationExamples\DocumentationExamples.csproj", "{A8E0D3B2-49D7-4DF6-BF91-B234C1C5E25D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CreateChatGptPlugin", "samples\CreateChatGptPlugin\Solution\CreateChatGptPlugin.csproj", "{87AB5AF5-5783-4372-9789-664895E0A2FF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -460,6 +458,18 @@ Global
{731CC542-8BE9-42D4-967D-99206EC2B310}.Publish|Any CPU.Build.0 = Debug|Any CPU
{731CC542-8BE9-42D4-967D-99206EC2B310}.Release|Any CPU.ActiveCfg = Release|Any CPU
{731CC542-8BE9-42D4-967D-99206EC2B310}.Release|Any CPU.Build.0 = Release|Any CPU
{A8E0D3B2-49D7-4DF6-BF91-B234C1C5E25D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A8E0D3B2-49D7-4DF6-BF91-B234C1C5E25D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A8E0D3B2-49D7-4DF6-BF91-B234C1C5E25D}.Publish|Any CPU.ActiveCfg = Debug|Any CPU
{A8E0D3B2-49D7-4DF6-BF91-B234C1C5E25D}.Publish|Any CPU.Build.0 = Debug|Any CPU
{A8E0D3B2-49D7-4DF6-BF91-B234C1C5E25D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A8E0D3B2-49D7-4DF6-BF91-B234C1C5E25D}.Release|Any CPU.Build.0 = Release|Any CPU
{87AB5AF5-5783-4372-9789-664895E0A2FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{87AB5AF5-5783-4372-9789-664895E0A2FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{87AB5AF5-5783-4372-9789-664895E0A2FF}.Publish|Any CPU.ActiveCfg = Debug|Any CPU
{87AB5AF5-5783-4372-9789-664895E0A2FF}.Publish|Any CPU.Build.0 = Debug|Any CPU
{87AB5AF5-5783-4372-9789-664895E0A2FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{87AB5AF5-5783-4372-9789-664895E0A2FF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -528,6 +538,8 @@ Global
{B0CE8C69-EC56-4825-94AB-01CA7E8BA55B} = {A2357CF8-3BB9-45A1-93F1-B366C9B63658}
{3A4B8F90-3E74-43E0-800C-84F8AA9B5BF3} = {A2357CF8-3BB9-45A1-93F1-B366C9B63658}
{731CC542-8BE9-42D4-967D-99206EC2B310} = {A2357CF8-3BB9-45A1-93F1-B366C9B63658}
{A8E0D3B2-49D7-4DF6-BF91-B234C1C5E25D} = {FA3720F1-C99A-49B2-9577-A940257098BF}
{87AB5AF5-5783-4372-9789-664895E0A2FF} = {FA3720F1-C99A-49B2-9577-A940257098BF}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FBDC56A3-86AD-4323-AA0F-201E59123B83}
+1 -1
View File
@@ -8,7 +8,7 @@
"#### Watch the Getting Started Quick Start [Video](https://aka.ms/SK-Getting-Started-Notebook)\n",
"\n",
"> [!IMPORTANT]\n",
"> You will need an [.NET 8 SDK](https://dotnet.microsoft.com/en-us/download) and [Polyglot](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode) to get started with this notebook using .NET Interactive."
"> You will need an [.NET 8 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) and [Polyglot](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode) to get started with this notebook using .NET Interactive."
]
},
{
@@ -14,7 +14,7 @@
"text into prompts to enrich them with more data. This allowed us to create a basic chat experience. \n",
"\n",
"However, if you solely relied on kernel arguments, you would quickly realize that eventually your prompt\n",
"would grow so large that you would run into a the model's token limit. What we need is a way to persist state\n",
"would grow so large that you would run into the model's token limit. What we need is a way to persist state\n",
"and build both short-term and long-term memory to empower even more intelligent applications. \n",
"\n",
"To do this, we dive into the key concept of `Semantic Memory` in the Semantic Kernel. "
+2 -2
View File
@@ -5,7 +5,7 @@ the Semantic Kernel. The notebooks are organized in order of increasing complexi
To run the notebooks, we recommend the following steps:
- [Install .NET 8](https://dotnet.microsoft.com/download/dotnet/7.0)
- [Install .NET 8](https://dotnet.microsoft.com/download/dotnet/8.0)
- [Install Visual Studio Code (VS Code)](https://code.visualstudio.com)
- Launch VS Code and [install the "Polyglot" extension](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode).
Min version required: v1.0.4606021 (Dec 2023).
@@ -70,7 +70,7 @@ For a quick dive, look at the [getting started notebook](00-getting-started.ipyn
You can run the notebooks also in the browser with JupyterLab. These steps
should be sufficient to start:
Install Python 3, Pip and .NET 7 in your system, then:
Install Python 3, Pip and .NET 8 in your system, then:
pip install jupyterlab
dotnet tool install -g Microsoft.dotnet-interactive
+2 -2
View File
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<!-- Central version prefix - applies to all nuget packages. -->
<VersionPrefix>1.2.0</VersionPrefix>
<VersionPrefix>1.3.0</VersionPrefix>
<PackageVersion Condition="'$(VersionSuffix)' != ''">$(VersionPrefix)-$(VersionSuffix)</PackageVersion>
<PackageVersion Condition="'$(VersionSuffix)' == ''">$(VersionPrefix)</PackageVersion>
@@ -10,7 +10,7 @@
<IsPackable>true</IsPackable>
<!-- Package validation. Baseline Version should be lower than current version. -->
<PackageValidationBaselineVersion>1.2.0</PackageValidationBaselineVersion>
<PackageValidationBaselineVersion>1.3.0</PackageValidationBaselineVersion>
<!-- Validate assembly attributes only for Publish builds -->
<NoWarn Condition="'$(Configuration)' != 'Publish'">$(NoWarn);CP0003</NoWarn>
<!-- Do not validate reference assemblies -->
@@ -0,0 +1,2 @@
[*.cs]
dotnet_diagnostic.CA1016.severity = none
@@ -0,0 +1,4 @@
**/bin
**/obj
local.settings.json
azure-function/appsettings.json
@@ -0,0 +1,54 @@
# Semantic Kernel OpenAI plugin starter
This project provides starter code to create a OpenAI plugin. It includes the following components:
- An endpoint that serves up an ai-plugin.json file for ChatGPT to discover the plugin
- A generator that automatically converts prompts into prompt endpoints
- The ability to add additional native functions as endpoints to the plugin
## Prerequisites
- [.NET 6](https://dotnet.microsoft.com/download/dotnet/8.0) is required to run this starter.
- [Azure Functions Core Tools](https://www.npmjs.com/package/azure-functions-core-tools) is required to run this starter.
- Install the recommended extensions
- [C#](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp)
- [Semantic Kernel Tools](https://marketplace.visualstudio.com/items?itemName=ms-semantic-kernel.semantic-kernel) (optional)
## Configuring the starter
To configure the starter, you need to provide the following information:
- Define the properties of the plugin in the appsettings.json file.
- Enter the API key for your AI endpoint in the [local.settings.json](./azure-function/local.settings.json.example) file.
### Using appsettings.json
Configure an OpenAI endpoint
1. Copy [settings.json.openai-example](./azure-function/config-samples/appsettings.json.openai-example) to `./appsettings.json`
1. Edit the `kernel` object to add your OpenAI endpoint configuration
1. Edit the `aiPlugin` object to define the properties that get exposed in the ai-plugin.json file
Configure an Azure OpenAI endpoint
1. Copy [settings.json.azure-example](./azure-function/config-samples/appsettings.json.azure-example) to `./appsettings.json`
1. Edit the `kernel` object to add your Azure OpenAI endpoint configuration
1. Edit the `aiPlugin` object to define the properties that get exposed in the ai-plugin.json file
### Using local.settings.json
1. Copy [local.settings.json.example](./azure-function/local.settings.json.example) to `./azure-function/local.settings.json`
1. Edit the `Values` object to add your OpenAI endpoint configuration in the `apiKey` property
## Running the starter
To run the Azure Functions application just hit `F5`.
To build and run the Azure Functions application from a terminal use the following commands:
```powershell
cd azure-function
dotnet build
cd bin/Debug/net8.0
func host start
```
@@ -0,0 +1,30 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Net;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Models;
public class AIPluginJson
{
[Function("GetAIPluginJson")]
public HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = ".well-known/ai-plugin.json")] HttpRequestData req)
{
var currentDomain = $"{req.Url.Scheme}://{req.Url.Host}:{req.Url.Port}";
HttpResponseData response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("Content-Type", "application/json");
var appSettings = AppSettings.LoadSettings();
// serialize app settings to json using System.Text.Json
var json = System.Text.Json.JsonSerializer.Serialize(appSettings.AIPlugin);
// replace {url} with the current domain
json = json.Replace("{url}", currentDomain, StringComparison.OrdinalIgnoreCase);
response.WriteString(json);
return response;
}
}
@@ -0,0 +1,33 @@
<Project>
<PropertyGroup>
<!-- Default properties inherited by all projects. Projects can override. -->
<RunAnalyzersDuringBuild>true</RunAnalyzersDuringBuild>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<AnalysisLevel>latest</AnalysisLevel>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<LangVersion>11</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>disable</ImplicitUsings>
<NoWarn>CS1591,CA1852,CA1050</NoWarn>
</PropertyGroup>
<PropertyGroup>
<!-- Disable NuGet packaging by default. Projects can override. -->
<IsPackable>disable</IsPackable>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugType>portable</DebugType>
</PropertyGroup>
<PropertyGroup>
<RepoRoot>$([System.IO.Path]::GetDirectoryName($([MSBuild]::GetPathOfFileAbove('.gitignore', '$(MSBuildThisFileDirectory)'))))</RepoRoot>
</PropertyGroup>
</Project>
@@ -0,0 +1,25 @@
<Project>
<!-- Direct all packages under 'dotnet' to get versions from Directory.Packages.props -->
<!-- using Central Package Management feature -->
<!-- https://learn.microsoft.com/en-us/nuget/consume-packages/Central-Package-Management -->
<Sdk Name="Microsoft.Build.CentralPackageVersions" Version="2.1.3" />
<!-- Only run 'dotnet format' on dev machines, Release builds. Skip on GitHub Actions -->
<!-- as this runs in its own Actions job. -->
<Target Name="DotnetFormatOnBuild" BeforeTargets="Build"
Condition=" '$(Configuration)' == 'Release' AND '$(GITHUB_ACTIONS)' == '' ">
<Message Text="Running dotnet format" Importance="high" />
<Exec Command="dotnet format --no-restore -v diag $(ProjectFileName)" />
</Target>
<Target Name="AddInternalsVisibleTo" BeforeTargets="BeforeCompile">
<!-- Handle Add InternalsVisibleTo to any targets that don't support it. -->
<ItemGroup Condition="'@(InternalsVisibleTo->Count())' &gt; 0 AND $([MSBuild]::VersionLessThan($(NETCoreSdkVersion), '5.0.0'))">
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1 Condition="'%(InternalsVisibleTo.PublicKey)' != ''">%(InternalsVisibleTo.Identity), PublicKey="%(InternalsVisibleTo.PublicKey)</_Parameter1>
<_Parameter1 Condition="'%(InternalsVisibleTo.PublicKey)' == ''">%(InternalsVisibleTo.Identity)</_Parameter1>
<_Parameter1_TypeName>System.String</_Parameter1_TypeName>
</AssemblyAttribute>
</ItemGroup>
</Target>
</Project>
@@ -0,0 +1,56 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Net;
using System.Reflection;
using System.Text.Json;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
namespace Plugins.AzureFunctions.Extensions;
public class AIPluginRunner
{
private readonly ILogger<AIPluginRunner> _logger;
private readonly Kernel _kernel;
public AIPluginRunner(Kernel kernel, ILoggerFactory loggerFactory)
{
this._kernel = kernel;
this._logger = loggerFactory.CreateLogger<AIPluginRunner>();
}
/// <summary>
/// Runs a prompt using the operationID and returns back an HTTP response.
/// </summary>
/// <param name="req"></param>
/// <param name="pluginName"></param>
/// <param name="functionName"></param>
public async Task<HttpResponseData> RunAIPluginOperationAsync<T>(HttpRequestData req, string pluginName, string functionName)
{
KernelArguments arguments = ConvertToKernelArguments((await JsonSerializer.DeserializeAsync<T>(req.Body).ConfigureAwait(true))!);
var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("Content-Type", "text/plain;charset=utf-8");
await response.WriteStringAsync(
(await this._kernel.InvokeAsync(pluginName, functionName, arguments).ConfigureAwait(false)).ToString()
).ConfigureAwait(false);
return response;
}
// Method to convert model to dictionary
private static KernelArguments ConvertToKernelArguments<T>(T model)
{
{
var arguments = new KernelArguments();
foreach (PropertyInfo property in typeof(T).GetProperties())
{
if (property.GetValue(model) != null)
{
arguments.Add(property.Name, property.GetValue(model));
}
}
return arguments;
}
}
}
@@ -0,0 +1,33 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using Models;
internal static class KernelBuilderExtensions
{
/// <summary>
/// Adds a chat completion service to the list. It can be either an OpenAI or Azure OpenAI backend service.
/// </summary>
/// <param name="kernelBuilder"></param>
/// <param name="kernelSettings"></param>
/// <exception cref="ArgumentException"></exception>
internal static IServiceCollection WithChatCompletionService(this IServiceCollection kernelBuilder, KernelSettings kernelSettings)
{
switch (kernelSettings.ServiceType.ToUpperInvariant())
{
case ServiceTypes.AzureOpenAI:
kernelBuilder.AddAzureOpenAIChatCompletion(deploymentName: kernelSettings.DeploymentOrModelId, modelId: kernelSettings.DeploymentOrModelId, endpoint: kernelSettings.Endpoint, apiKey: kernelSettings.ApiKey, serviceId: kernelSettings.ServiceId);
break;
case ServiceTypes.OpenAI:
kernelBuilder.AddOpenAIChatCompletion(modelId: kernelSettings.DeploymentOrModelId, apiKey: kernelSettings.ApiKey, orgId: kernelSettings.OrgId, serviceId: kernelSettings.ServiceId);
break;
default:
throw new ArgumentException($"Invalid service type value: {kernelSettings.ServiceType}");
}
return kernelBuilder;
}
}
@@ -0,0 +1,21 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Net;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
public class Logo
{
[Function("GetLogo")]
public HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "logo.png")] HttpRequestData req)
{
// Return logo.png that's in the root of the project
var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("Content-Type", "image/png");
var logo = System.IO.File.ReadAllBytes("logo.png");
response.Body.Write(logo);
return response;
}
}
@@ -0,0 +1,61 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Models;
#pragma warning disable CA1056
#pragma warning disable CA1034
public class Pluginsettings
{
[JsonPropertyName("schema_version")]
public string SchemaVersion { get; set; } = "v1";
[JsonPropertyName("name_for_model")]
public string NameForModel { get; set; } = string.Empty;
[JsonPropertyName("name_for_human")]
public string NameForHuman { get; set; } = string.Empty;
[JsonPropertyName("description_for_model")]
public string DescriptionForModel { get; set; } = string.Empty;
[JsonPropertyName("description_for_human")]
public string DescriptionForHuman { get; set; } = string.Empty;
[JsonPropertyName("auth")]
public AuthModel Auth { get; set; } = new AuthModel();
[JsonPropertyName("api")]
public ApiModel Api { get; set; } = new ApiModel();
[JsonPropertyName("logo_url")]
public string LogoUrl { get; set; } = string.Empty;
[JsonPropertyName("contact_email")]
public string ContactEmail { get; set; } = string.Empty;
[JsonPropertyName("legal_info_url")]
public string LegalInfoUrl { get; set; } = string.Empty;
public class AuthModel
{
[JsonPropertyName("type")]
public string Type { get; set; } = string.Empty;
[JsonPropertyName("authorization_url")]
public string AuthorizationType { get; set; } = string.Empty;
}
public class ApiModel
{
[JsonPropertyName("type")]
public string Type { get; set; } = "openapi";
[JsonPropertyName("url")]
public string Url { get; set; } = string.Empty;
[JsonPropertyName("has_user_authentication")]
public bool HasUserAuthentication { get; set; } = false;
}
}
@@ -0,0 +1,66 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Extensions.Configuration;
namespace Models;
#pragma warning disable CA1724
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public class AppSettings
{
public const string DefaultConfigFile = "appsettings.json";
public KernelSettings Kernel { get; set; }
public Pluginsettings AIPlugin { get; set; }
/// <summary>
/// Load the kernel settings from settings.json if the file exists and if not attempt to use user secrets.
/// </summary>
public static AppSettings LoadSettings()
{
try
{
var appSettings = FromFile(DefaultConfigFile);
appSettings.Kernel.ApiKey = GetApiKey();
return appSettings;
}
catch (InvalidDataException ide)
{
Console.Error.WriteLine(
"Unable to load app settings, please provide configuration settings using instructions in the README.\n" +
"Please refer to: https://github.com/microsoft/semantic-kernel-starters/blob/main/sk-csharp-chatgpt-plugin/README.md#configuring-the-starter"
);
throw new InvalidOperationException(ide.Message);
}
}
/// <summary>
/// Load the kernel settings from the specified configuration file if it exists.
/// </summary>
private static AppSettings FromFile(string configFile = DefaultConfigFile)
{
if (!File.Exists(configFile))
{
throw new FileNotFoundException($"Configuration not found: {configFile}");
}
var configuration = new ConfigurationBuilder()
.SetBasePath(System.IO.Directory.GetCurrentDirectory())
.AddJsonFile(configFile, optional: true, reloadOnChange: true)
.Build();
return configuration.Get<AppSettings>()
?? throw new InvalidDataException($"Invalid app settings in '{configFile}', please provide configuration settings using instructions in the README.");
}
/// <summary>
/// Load the API key for the AI endpoint from user secrets.
/// </summary>
internal static string GetApiKey()
{
return System.Environment.GetEnvironmentVariable("apiKey", EnvironmentVariableTarget.Process)
?? throw new InvalidDataException("Invalid semantic kernel settings in user secrets, please provide configuration settings using instructions in the README.");
}
}
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
@@ -0,0 +1,17 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Extensions.Logging;
namespace Models;
#pragma warning disable CA1812
public class KernelSettings
{
public string ServiceType { get; set; } = string.Empty;
public string ServiceId { get; set; } = string.Empty;
public string DeploymentOrModelId { get; set; } = string.Empty;
public string Endpoint { get; set; } = string.Empty;
public string OrgId { get; set; } = string.Empty;
public LogLevel? LogLevel { get; set; }
public string ApiKey { get; set; } = string.Empty;
}
@@ -0,0 +1,9 @@
// Copyright (c) Microsoft. All rights reserved.
namespace Models;
internal static class ServiceTypes
{
internal const string OpenAI = "OPENAI";
internal const string AzureOpenAI = "AZUREOPENAI";
}
@@ -0,0 +1,152 @@
// Copyright (c) Microsoft. All rights reserved.
using System.ComponentModel;
using Microsoft.SemanticKernel;
namespace Plugins;
public sealed class MathPlugin
{
[KernelFunction, Description("Take the square root of a number")]
public static double Sqrt(
[Description("The number to take a square root of")] double number1
)
{
return Math.Sqrt(number1);
}
[KernelFunction, Description("Add two numbers")]
public static double Add(
[Description("The first number to add")] double number1,
[Description("The second number to add")] double number2
)
{
return number1 + number2;
}
[KernelFunction, Description("Subtract two numbers")]
public static double Subtract(
[Description("The first number to subtract from")] double number1,
[Description("The second number to subtract away")] double number2
)
{
return number1 - number2;
}
[KernelFunction, Description("Multiply two numbers. When increasing by a percentage, don't forget to add 1 to the percentage.")]
public static double Multiply(
[Description("The first number to multiply")] double number1,
[Description("The second number to multiply")] double number2
)
{
return number1 * number2;
}
[KernelFunction, Description("Divide two numbers")]
public static double Divide(
[Description("The first number to divide from")] double number1,
[Description("The second number to divide by")] double number2
)
{
return number1 / number2;
}
[KernelFunction, Description("Raise a number to a power")]
public static double Power(
[Description("The number to raise")] double number1,
[Description("The power to raise the number to")] double number2
)
{
return Math.Pow(number1, number2);
}
[KernelFunction, Description("Take the log of a number")]
public static double Log(
[Description("The number to take the log of")] double number1,
[Description("The base of the log")] double number2
)
{
return Math.Log(number1, number2);
}
[KernelFunction, Description("Round a number to the target number of decimal places")]
public static double Round(
[Description("The number to round")] double number1,
[Description("The number of decimal places to round to")] double number2
)
{
return Math.Round(number1, (int)number2);
}
[KernelFunction, Description("Take the absolute value of a number")]
public static double Abs(
[Description("The number to take the absolute value of")] double number1
)
{
return Math.Abs(number1);
}
[KernelFunction, Description("Take the floor of a number")]
public static double Floor(
[Description("The number to take the floor of")] double number1
)
{
return Math.Floor(number1);
}
[KernelFunction, Description("Take the ceiling of a number")]
public static double Ceiling(
[Description("The number to take the ceiling of")] double number1
)
{
return Math.Ceiling(number1);
}
[KernelFunction, Description("Take the sine of a number")]
public static double Sin(
[Description("The number to take the sine of")] double number1
)
{
return Math.Sin(number1);
}
[KernelFunction, Description("Take the cosine of a number")]
public static double Cos(
[Description("The number to take the cosine of")] double number1
)
{
return Math.Cos(number1);
}
[KernelFunction, Description("Take the tangent of a number")]
public static double Tan(
[Description("The number to take the tangent of")] double number1
)
{
return Math.Tan(number1);
}
[KernelFunction, Description("Take the arcsine of a number")]
public static double Asin(
[Description("The number to take the arcsine of")] double number1
)
{
return Math.Asin(number1);
}
[KernelFunction, Description("Take the arccosine of a number")]
public static double Acos(
[Description("The number to take the arccosine of")] double number1
)
{
return Math.Acos(number1);
}
[KernelFunction, Description("Take the arctangent of a number")]
public static double Atan(
[Description("The number to take the arctangent of")] double number1
)
{
return Math.Atan(number1);
}
}
@@ -0,0 +1,73 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using Microsoft.SemanticKernel;
using Models;
using Plugins;
using Plugins.AzureFunctions.Extensions;
const string DefaultSemanticFunctionsFolder = "Prompts";
string semanticFunctionsFolder = Environment.GetEnvironmentVariable("SEMANTIC_SKILLS_FOLDER") ?? DefaultSemanticFunctionsFolder;
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureAppConfiguration(configuration =>
{
var config = configuration.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true);
var builtConfig = config.Build();
})
.ConfigureServices((context, services) =>
{
services.Configure<JsonSerializerOptions>(options =>
{
// `ConfigureFunctionsWorkerDefaults` sets the default to ignore casing already.
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});
services.AddSingleton<IOpenApiConfigurationOptions>(_ =>
{
var options = new OpenApiConfigurationOptions()
{
Info = new OpenApiInfo()
{
Version = "1.0.0",
Title = "My Plugin",
Description = "This plugin does..."
},
Servers = DefaultOpenApiConfigurationOptions.GetHostNames(),
OpenApiVersion = OpenApiVersionType.V3,
//IncludeRequestingHostName = true,
ForceHttps = false,
ForceHttp = false,
};
return options;
});
services
.AddTransient((providers) =>
{
var appSettings = AppSettings.LoadSettings();
var builder = Kernel.CreateBuilder();
builder.Services.WithChatCompletionService(appSettings.Kernel);
builder.Services.AddLogging(loggingBuilder =>
{
loggingBuilder.AddFilter(level => true);
loggingBuilder.AddConsole();
});
builder.Plugins.AddFromType<MathPlugin>();
return builder.Build();
})
.AddScoped<AIPluginRunner>();
})
.Build();
host.Run();
@@ -0,0 +1,26 @@
{
"schema": 1,
"description": "If the request does not provide an explicit number for a parameter, use this function to make up a logical value; do not make up a value yourself.",
"type": "completion",
"completion": {
"max_tokens": 1000,
"temperature": 0.9,
"top_p": 0.0,
"presence_penalty": 0.0,
"frequency_penalty": 0.0
},
"input": {
"parameters": [
{
"name": "input",
"description": "A detailed description (2-3 sentences) of the missing value; provide all the context the user provided to get the best results.",
"defaultValue": ""
},
{
"name": "units",
"description": "The units used to measure the value (e.g., 'meters', 'seconds', 'dollars', etc.). (required)",
"defaultValue": ""
}
]
}
}
@@ -0,0 +1,18 @@
INSTURCTIONS:
Provide a realistic value for the missing parameter. If you don't know the answer, provide a best guess using the limited information provided.
Do not give a range of values. Do not give a value that is not realistic. Do not give a value that is not possible.
OUTPUT FORMAT:
{
"value": "<value>",
"reason": "<reason>",
"units": "<units>",
}
MISSING PARAMETER DESCRIPTION:
{{$input}}
PARAMETER UNITS:
{{$units}}
ANSWER:
@@ -0,0 +1,31 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "sk-chatgpt-azure-function", "sk-chatgpt-azure-function.csproj", "{20F608E8-E748-4432-B673-7D98782A6E23}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PluginShared", "shared\PluginShared.csproj", "{82A779FB-9CFD-4159-8AFC-54D00C76947C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{20F608E8-E748-4432-B673-7D98782A6E23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{20F608E8-E748-4432-B673-7D98782A6E23}.Debug|Any CPU.Build.0 = Debug|Any CPU
{20F608E8-E748-4432-B673-7D98782A6E23}.Release|Any CPU.ActiveCfg = Release|Any CPU
{20F608E8-E748-4432-B673-7D98782A6E23}.Release|Any CPU.Build.0 = Release|Any CPU
{82A779FB-9CFD-4159-8AFC-54D00C76947C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{82A779FB-9CFD-4159-8AFC-54D00C76947C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{82A779FB-9CFD-4159-8AFC-54D00C76947C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{82A779FB-9CFD-4159-8AFC-54D00C76947C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {76F2FDDE-51A7-4934-BEB6-2D6F082412BD}
EndGlobalSection
EndGlobal
@@ -0,0 +1,25 @@
{
"kernel": {
"serviceType": "AzureOpenAI",
"serviceId": "gpt-35-turbo",
"deploymentOrModelId": "gpt-35-turbo",
"endpoint": "https:// ... your endpoint ... .openai.azure.com/"
},
"aiPlugin": {
"schemaVersion": "v1",
"nameForModel": "MyPlugin",
"nameForHuman": "My Plugin",
"descriptionForModel": "<Instructions for the model>",
"descriptionForHuman": "<Description for users>",
"auth": {
"type": "none"
},
"api": {
"type": "openapi",
"url": "{url}/swagger.json"
},
"logoUrl": "{url}/logo.png",
"contactEmail": "[email protected]",
"legalInfoUrl": "http://www.example.com/legal"
}
}
@@ -0,0 +1,25 @@
{
"kernel": {
"serviceType": "OpenAI",
"serviceId": "gpt-3.5-turbo",
"deploymentOrModelId": "gpt-3.5-turbo",
"orgId": ""
},
"aiPlugin": {
"schemaVersion": "v1",
"nameForModel": "MyPlugin",
"nameForHuman": "My Plugin",
"descriptionForModel": "<Instructions for the model>",
"descriptionForHuman": "<Description for users>",
"auth": {
"type": "none"
},
"api": {
"type": "openapi",
"url": "{url}/swagger.json"
},
"logoUrl": "{url}/logo.png",
"contactEmail": "[email protected]",
"legalInfoUrl": "http://www.example.com/legal"
}
}
@@ -0,0 +1,16 @@
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
},
"extensions": {
"http": {
"routePrefix": ""
}
}
}
@@ -0,0 +1,10 @@
{
"IsEncrypted": false,
"Host": {
"CORS": "*"
},
"Values": {
"apiKey": "<your api key>",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

@@ -0,0 +1,21 @@
// Copyright (c) Microsoft. All rights reserved.
namespace skchatgptazurefunction.PluginShared;
/// <summary>
/// This class represents the plugin API specification.
/// </summary>
public class PluginApi
{
/// <summary>
/// The API specification
/// </summary>
public string Type { get; set; } = "openapi";
/// <summary>
/// URL used to fetch the specification
/// </summary>
#pragma warning disable CA1056 // URI-like properties should not be strings
public string Url { get; set; } = string.Empty;
#pragma warning restore CA1056 // URI-like properties should not be strings
}
@@ -0,0 +1,40 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace skchatgptazurefunction.PluginShared;
/// <summary>
/// This class represents the OpenAI plugin authentication schema.
/// </summary>
public class PluginAuth
{
/// <summary>
/// Tokens for API key authentication
/// </summary>
public class VerificationTokens
{
/// <summary>
/// The API key
/// </summary>
public string OpenAI { get; set; } = string.Empty;
}
/// <summary>
/// The authentication schema
/// Supported values: none, service_http, user_http
/// </summary>
public string Type { get; set; } = "none";
/// <summary>
/// Manifest schema version
/// </summary>
[JsonPropertyName("authorization_type")]
public string AuthorizationType { get; } = "bearer";
/// <summary>
/// Tokens for API key authentication
/// </summary>
[JsonPropertyName("verification_tokens")]
public VerificationTokens Tokens { get; set; } = new VerificationTokens();
}
@@ -0,0 +1,77 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace skchatgptazurefunction.PluginShared;
/// <summary>
/// This class represents the OpenAI plugin manifest:
/// https://platform.openai.com/docs/plugins/getting-started/plugin-manifest
/// </summary>
public class PluginManifest
{
/// <summary>
/// Manifest schema version
/// </summary>
[JsonPropertyName("schema_version")]
public string SchemaVersion { get; set; } = "v1";
/// <summary>
/// The name of the plugin that the model will use
/// </summary>
[JsonPropertyName("name_for_model")]
public string NameForModel { get; set; } = string.Empty;
/// <summary>
/// Human-readable name of the plugin
/// </summary>
[JsonPropertyName("name_for_human")]
public string NameForHuman { get; set; } = string.Empty;
/// <summary>
/// Description of the plugin that the model will use
/// </summary>
[JsonPropertyName("description_for_model")]
public string DescriptionForModel { get; set; } = string.Empty;
/// <summary>
/// Human-readable description of the plugin
/// </summary>
[JsonPropertyName("description_for_human")]
public string DescriptionForHuman { get; set; } = string.Empty;
/// <summary>
/// The authentication schema
/// </summary>
public PluginAuth Auth { get; set; } = new PluginAuth();
/// <summary>
/// The API specification
/// </summary>
public PluginApi Api { get; set; } = new PluginApi();
#pragma warning disable CA1056 // URI-like properties should not be strings
/// <summary>
/// Redirect URL for users to get more information about the plugin
/// </summary>
[JsonPropertyName("legal_info_url")]
public string LegalInfoUrl { get; set; } = string.Empty;
/// <summary>
/// URL used to fetch the logo
/// </summary>
[JsonPropertyName("logo_url")]
public string LogoUrl { get; set; } = string.Empty;
#pragma warning restore CA1056 // URI-like properties should not be strings
/// <summary>
/// Email contact for safety/moderation, support and deactivation
/// </summary>
[JsonPropertyName("contact_email")]
public string ContactEmail { get; set; } = string.Empty;
/// <summary>
/// "Bearer" or "Basic"
/// </summary>
public string HttpAuthorizationType { get; set; } = string.Empty;
}
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>10</LangVersion>
<Nullable>enable</Nullable>
<RootNamespace>skchatgptazurefunction.PluginShared</RootNamespace>
</PropertyGroup>
</Project>
@@ -0,0 +1,68 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RepoRoot>$([System.IO.Path]::GetDirectoryName($([MSBuild]::GetPathOfFileAbove('.gitignore', '$(MSBuildThisFileDirectory)'))))</RepoRoot>
<UserSecretsId>d733072a-9c0f-473c-a99b-6e12f7b5ef28</UserSecretsId>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>skchatgptazurefunction</RootNamespace>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<_FunctionsSkipCleanOutput>true</_FunctionsSkipCleanOutput>
<OutputType>Exe</OutputType>
<LangVersion>10</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<IsPackable>false</IsPackable>
</PropertyGroup>
<PropertyGroup>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.Functions.Worker" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.OpenApi" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Connectors\Connectors.OpenAI\Connectors.OpenAI.csproj" />
<ProjectReference Include="..\kernel-functions-generator\kernel-functions-generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
<ItemGroup>
<None Include="$(IntDir).generated\kernel-functions-generator\kernel-functions-generator.SemanticSkillGenerator\**">
<Link>.generated\%(RecursiveDir)%(Filename)%(Extension)</Link>
</None>
<None Include="shared\PluginShared.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<AdditionalFiles Include="Prompts\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</AdditionalFiles>
</ItemGroup>
<ItemGroup>
<Content Include="logo.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Using Include="System.Threading.ExecutionContext" Alias="ExecutionContext" />
</ItemGroup>
</Project>
@@ -0,0 +1,22 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.CodeAnalysis;
namespace Plugins.AzureFunctions.Generator.Extensions;
internal static class GeneratorExecutionContextExtensions
{
public static string? GetMSBuildProperty(
this GeneratorExecutionContext context,
string name,
string defaultValue = "")
{
context.AnalyzerConfigOptions.GlobalOptions.TryGetValue($"build_property.{name}", out var value);
return value ?? defaultValue;
}
public static string? GetRootNamespace(this GeneratorExecutionContext context)
{
return context.GetMSBuildProperty("RootNamespace");
}
}
@@ -0,0 +1,212 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Plugins.AzureFunctions.Generator;
/// <summary>
/// Generates kernel functions
/// </summary>
[Generator]
public class KernelFunctionGenerator : ISourceGenerator
{
/// <summary>
/// Generates kernel functions
/// </summary>
public void Execute(GeneratorExecutionContext context)
{
var functionDetailsByPlugin = new Dictionary<string, List<FunctionDetails>>();
foreach (var syntaxTree in context.Compilation.SyntaxTrees)
{
var semanticModel = context.Compilation.GetSemanticModel(syntaxTree);
var root = syntaxTree.GetRoot();
var configureServicesCalls = root.DescendantNodes()
.OfType<InvocationExpressionSyntax>()
.Where(ies => ies.Expression is MemberAccessExpressionSyntax maes && maes.Name.ToString() == "AddTransient");
foreach (var configureServicesCall in configureServicesCalls)
{
// Analyze within ConfigureServices
foreach (var invocation in configureServicesCall.DescendantNodes().OfType<InvocationExpressionSyntax>())
{
var symbol = semanticModel.GetSymbolInfo(invocation).Symbol as IMethodSymbol;
if (symbol?.ContainingType.ToString() == "Microsoft.SemanticKernel.KernelExtensions")
{
INamedTypeSymbol? pluginTypeArgument = null;
if (symbol.Name == "AddFromType")
{
pluginTypeArgument = symbol.TypeArguments.FirstOrDefault() as INamedTypeSymbol;
}
else if (symbol.Name == "AddFromObject")
{
var objectCreationExpression = invocation.ArgumentList.Arguments.FirstOrDefault()?.Expression as ObjectCreationExpressionSyntax;
if (objectCreationExpression != null)
{
var typeInfo = semanticModel.GetTypeInfo(objectCreationExpression);
pluginTypeArgument = typeInfo.Type as INamedTypeSymbol;
}
}
if (pluginTypeArgument != null && configureServicesCall.Expression is MemberAccessExpressionSyntax maes)
{
var pluginName = pluginTypeArgument.Name;
var functionDetails = this.ExtractFunctionDetails(context, pluginTypeArgument);
functionDetailsByPlugin[pluginName] = functionDetails;
}
}
}
}
}
// Generate source for each plugin
foreach (var pluginEntry in functionDetailsByPlugin)
{
var sourceCode = GenerateClassSource("AzureFunctionPlugins", pluginEntry.Key, pluginEntry.Value);
context.AddSource($"{pluginEntry.Key}.g.cs", sourceCode);
}
}
private List<FunctionDetails> ExtractFunctionDetails(GeneratorExecutionContext context, INamedTypeSymbol pluginClass)
{
var functionDetailsList = new List<FunctionDetails>();
foreach (var member in pluginClass.GetMembers())
{
if (member is IMethodSymbol methodSymbol && methodSymbol.GetAttributes().Any(attr => attr?.AttributeClass?.Name == "KernelFunctionAttribute"))
{
var functionDetails = new FunctionDetails
{
Name = methodSymbol.Name,
Description = methodSymbol.GetAttributes().FirstOrDefault(a => a?.AttributeClass?.Name == "DescriptionAttribute")?.ConstructorArguments.FirstOrDefault().Value.ToString(),
Parameters = new List<ParameterDetails>()
};
foreach (var parameter in methodSymbol.Parameters)
{
var parameterDetails = new ParameterDetails
{
Name = parameter.Name,
Type = parameter.Type.ToString(),
Description = parameter.GetAttributes().FirstOrDefault(a => a?.AttributeClass?.Name == "DescriptionAttribute")?.ConstructorArguments.FirstOrDefault().Value.ToString()
};
functionDetails.Parameters.Add(parameterDetails);
}
functionDetailsList.Add(functionDetails);
}
}
return functionDetailsList;
}
// Generate the source code for a folder of prompts
private static string GenerateClassSource(string rootNamespace, string pluginName, List<FunctionDetails> functions)
{
StringBuilder functionsCode = new();
foreach (var function in functions)
{
functionsCode.AppendLine(GenerateFunctionSource(pluginName, function) ?? string.Empty);
}
return $@"/* ### GENERATED CODE - Do not modify. Edits will be lost on build. ### */
using System;
using System.Net;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using Microsoft.SemanticKernel;
using Plugins.AzureFunctions.Extensions;
namespace {rootNamespace};
public class {pluginName}
{{
private readonly ILogger _logger;
private readonly AIPluginRunner _pluginRunner;
public {pluginName}(AIPluginRunner pluginRunner, ILoggerFactory loggerFactory)
{{
_pluginRunner = pluginRunner;
_logger = loggerFactory.CreateLogger<{pluginName}>();
}}
{functionsCode}
}}";
}
private static string? GenerateFunctionSource(string pluginName, FunctionDetails functionDetails)
{
string modelClassName = $"{functionDetails.Name}Model"; // Name of the model class
string parameterAttributes = GenerateModelClassSource(modelClassName, functionDetails.Parameters);
return $@"
{parameterAttributes}
[OpenApiOperation(operationId: ""{functionDetails.Name}"", tags: new[] {{ ""{functionDetails.Name}"" }})]
[OpenApiRequestBody(contentType: ""application/json"", bodyType: typeof({modelClassName}), Required = true, Description = ""JSON request body"")]
[OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: ""application/json"", bodyType: typeof(string), Description = ""The OK response"")]
[Function(""{functionDetails.Name}"")]
public async Task<HttpResponseData> {functionDetails.Name}([HttpTrigger(AuthorizationLevel.Anonymous, ""post"")] HttpRequestData req)
{{
_logger.LogInformation(""HTTP trigger processed a request for function {pluginName}-{functionDetails.Name}."");
return await _pluginRunner.RunAIPluginOperationAsync<{modelClassName}>(req, ""{pluginName}"", ""{functionDetails.Name}"");
}}";
}
private static string GenerateModelClassSource(string modelClassName, List<ParameterDetails> parameters)
{
StringBuilder modelClassBuilder = new();
modelClassBuilder.AppendLine($"public class {modelClassName}");
modelClassBuilder.AppendLine("{");
foreach (var parameter in parameters)
{
modelClassBuilder.AppendLine($" public {parameter.Type} {parameter.Name} {{ get; set; }}");
}
modelClassBuilder.AppendLine("}");
return modelClassBuilder.ToString();
}
/// <summary>
/// Does nothing
/// </summary>
public void Initialize(GeneratorInitializationContext context)
{
// No initialization required
}
}
/// <summary>
/// Function details
/// </summary>
public class FunctionDetails
{
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public List<ParameterDetails> Parameters { get; set; } = new List<ParameterDetails>();
}
/// <summary>
/// Parameter details
/// </summary>
public class ParameterDetails
{
public string Name { get; set; } = string.Empty;
public string Type { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
}
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>10</LangVersion>
<Nullable>enable</Nullable>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<NoWarn>RS1035,CS0612,CS1591,CS8601,CS8602,CS860218</NoWarn>
<RootNamespace>Plugins.AzureFunctions.Generator</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Common" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
</ItemGroup>
<PropertyGroup>
<GetTargetPathDependsOn>$(GetTargetPathDependsOn);GetDependencyTargetPaths</GetTargetPathDependsOn>
</PropertyGroup>
<Target Name="GetDependencyTargetPaths">
</Target>
</Project>
@@ -0,0 +1,76 @@
# Creating and using a OpenAI plugin
## Prerequisites
- [Azure Functions Core Tools](https://github.com/Azure/azure-functions-core-tools) version 4.x.
- [.NET 6](https://dotnet.microsoft.com/download/dotnet/6.0) is required to run this sample.
- Install the recommended extensions
- [C#](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp)
- [Semantic Kernel Tools](https://marketplace.visualstudio.com/items?itemName=ms-semantic-kernel.semantic-kernel) (optional)
You must also have the Azure Function located [here](./MathPlugin/) running locally, otherwise the sample will fail.
## Configuring the sample
The sample can be configured by using the command line with .NET [Secret Manager](https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets) to avoid the risk of leaking secrets into the repository, branches and pull requests.
This sample has been tested with the following models:
| Service | Model type | Model | Model version | Supported |
| ------------ | --------------- | ---------------- | ------------: | --------- |
| OpenAI | Text Completion | text-davinci-003 | 1 | ❌ |
| OpenAI | Chat Completion | gpt-3.5-turbo | 1 | ❌ |
| OpenAI | Chat Completion | gpt-3.5-turbo | 0301 | ❌ |
| Azure OpenAI | Chat Completion | gpt-3.5-turbo | 0613 | ✅ |
| Azure OpenAI | Chat Completion | gpt-3.5-turbo | 1106 | ✅ |
| OpenAI | Chat Completion | gpt-4 | 1 | ❌ |
| OpenAI | Chat Completion | gpt-4 | 0314 | ❌ |
| Azure OpenAI | Chat Completion | gpt-4 | 0613 | ✅ |
| Azure OpenAI | Chat Completion | gpt-4 | 1106 | ✅ |
This sample uses function calling, so it only works on models newer than 0613.
### Using .NET [Secret Manager](https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets)
Configure an OpenAI endpoint
```powershell
cd 14-Create-ChatGPT-Plugin/Solution
dotnet user-secrets set "Global:LlmService" "OpenAI"
dotnet user-secrets set "OpenAI:ModelType" "chat-completion"
dotnet user-secrets set "OpenAI:ChatCompletionModelId" "gpt-4"
dotnet user-secrets set "OpenAI:ApiKey" "... your OpenAI key ..."
dotnet user-secrets set "OpenAI:OrgId" "... your ord ID ..."
```
Configure an Azure OpenAI endpoint
```powershell
cd 14-Create-ChatGPT-Plugin/Solution
dotnet user-secrets set "Global:LlmService" "AzureOpenAI"
dotnet user-secrets set "AzureOpenAI:DeploymentType" "chat-completion"
dotnet user-secrets set "AzureOpenAI:ChatCompletionDeploymentName" "gpt-35-turbo"
dotnet user-secrets set "AzureOpenAI:ChatCompletionModelId" "gpt-3.5-turbo-0613"
dotnet user-secrets set "AzureOpenAI:Endpoint" "... your Azure OpenAI endpoint ..."
dotnet user-secrets set "AzureOpenAI:ApiKey" "... your Azure OpenAI key ..."
```
## Running the sample
First, refer to the [README](./MathPlugin/README.md) in the MathPlugin\ folder
to start the Azure Function.
After starting the Azure Function and configuring the sample,
to build and run the console application, navigate to the [Solution](./Solution/) folder and hit `F5`.
To build and run the console application from the terminal use the following commands:
```powershell
cd Solution
dotnet build
dotnet run
```
@@ -0,0 +1,7 @@
appsettings.json
bin
obj
*.ini
*.cache
*.log
*.tmp
@@ -0,0 +1,6 @@
{
"recommendations": [
"ms-dotnettools.csharp",
"ms-semantic-kernel.semantic-kernel"
]
}
@@ -0,0 +1,26 @@
{
"version": "0.2.0",
"configurations": [
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/bin/Debug/net8.0/14-Create-ChatGPT-Plugin.dll",
"args": [],
"cwd": "${workspaceFolder}",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "integratedTerminal",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
}
]
}
@@ -0,0 +1,84 @@
{
"prettier.enable": true,
"css.lint.validProperties": [
"composes"
],
"editor.defaultFormatter": "ms-dotnettools.csharp",
"editor.formatOnType": true,
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"editor.codeActionsOnSave": {
"source.fixAll": "explicit"
},
"[csharp]": {
"editor.defaultFormatter": "ms-dotnettools.csharp"
},
"editor.bracketPairColorization.enabled": true,
"editor.guides.bracketPairs": "active",
"python.formatting.provider": "autopep8",
"python.formatting.autopep8Args": [
"--max-line-length=120"
],
"notebook.output.textLineLimit": 500,
"python.analysis.extraPaths": [
"./python/src"
],
"javascript.updateImportsOnFileMove.enabled": "always",
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true,
"**/build": true
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.organizeImports": true,
"source.fixAll": true
}
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.organizeImports": true,
"source.fixAll": true
}
},
"typescript.updateImportsOnFileMove.enabled": "always",
"eslint.enable": true,
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
"eslint.lintTask.enable": true,
"eslint.workingDirectories": [
{
"mode": "auto"
}
],
"eslint.options": {
"overrideConfigFile": ".eslintrc.js"
},
"eslint.packageManager": "yarn",
"files.associations": {
"*.json": "jsonc"
},
"files.exclude": {
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/CVS": true,
"**/.DS_Store": true,
"**/Thumbs.db": true
},
"cSpell.words": [
"Partitioner"
],
"[typescript][typescriptreact]": {
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.fixAll": "explicit"
}
}
}
@@ -0,0 +1,41 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/14-Create-ChatGPT-Plugin.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/14-Create-ChatGPT-Plugin.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/14-Create-ChatGPT-Plugin.csproj"
],
"problemMatcher": "$msCompile"
}
]
}
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace></RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UserSecretsId>5ee045b0-aea3-4f08-8d31-32d1a6f8fed0</UserSecretsId>
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
<NoWarn>SKEXP0042</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Functions\Functions.OpenApi\Functions.OpenApi.csproj" />
<ProjectReference Include="..\..\..\src\SemanticKernel.MetaPackage\SemanticKernel.MetaPackage.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="config\appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
@@ -0,0 +1,63 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Plugins.OpenApi;
// Create kernel
var builder = Kernel.CreateBuilder();
// Add a text or chat completion service using either:
// builder.Services.AddAzureOpenAIChatCompletion()
// builder.Services.AddAzureOpenAITextGeneration()
// builder.Services.AddOpenAIChatCompletion()
// builder.Services.AddOpenAITextGeneration()
builder.WithCompletionService();
var kernel = builder.Build();
// Add the math plugin using the plugin manifest URL
await kernel.ImportPluginFromOpenApiAsync("MathPlugin", new Uri("http://localhost:7071/swagger.json")).ConfigureAwait(false);
// Create chat history
ChatHistory history = new();
// Get chat completion service
var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
// Start the conversation
while (true)
{
// Get user input
Console.Write("User > ");
history.AddUserMessage(Console.ReadLine()!);
// Enable auto function calling
OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new()
{
ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
};
// Get the response from the AI
var result = chatCompletionService.GetStreamingChatMessageContentsAsync(
history,
executionSettings: openAIPromptExecutionSettings,
kernel: kernel);
// Stream the results
string fullMessage = "";
var first = true;
await foreach (var content in result)
{
if (content.Role.HasValue && first)
{
Console.Write("Assistant > ");
first = false;
}
Console.Write(content.Content);
fullMessage += content.Content;
}
Console.WriteLine();
// Add the message from the agent to the chat history
history.AddAssistantMessage(fullMessage);
}
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Extensions.Configuration;
#pragma warning disable CA1812 // instantiated by AddUserSecrets
internal sealed class Env
#pragma warning restore CA1812
{
/// <summary>
/// Simple helper used to load env vars and secrets like credentials,
/// to avoid hard coding them in the sample code
/// </summary>
/// <param name="name">Secret name / Env var name</param>
/// <returns>Value found in Secret Manager or Environment Variable</returns>
internal static string? Var(string name)
{
var configuration = new ConfigurationBuilder()
.AddUserSecrets<Env>()
.Build();
var value = configuration[name];
if (!string.IsNullOrEmpty(value))
{
return value;
}
value = Environment.GetEnvironmentVariable(name);
return value;
}
}
@@ -0,0 +1,62 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.SemanticKernel;
internal static class KernelBuilderExtensions
{
/// <summary>
/// Adds a text completion service to the list. It can be either an OpenAI or Azure OpenAI backend service.
/// </summary>
/// <param name="kernelBuilder"></param>
/// <exception cref="ArgumentException"></exception>
internal static IKernelBuilder WithCompletionService(this IKernelBuilder kernelBuilder)
{
switch (Env.Var("Global:LlmService")!)
{
case "AzureOpenAI":
if (Env.Var("AzureOpenAI:DeploymentType") == "text-completion")
{
kernelBuilder.Services.AddAzureOpenAITextGeneration(
deploymentName: Env.Var("AzureOpenAI:TextCompletionDeploymentName")!,
modelId: Env.Var("AzureOpenAI:TextCompletionModelId")!,
endpoint: Env.Var("AzureOpenAI:Endpoint")!,
apiKey: Env.Var("AzureOpenAI:ApiKey")!
);
}
else if (Env.Var("AzureOpenAI:DeploymentType") == "chat-completion")
{
kernelBuilder.Services.AddAzureOpenAIChatCompletion(
deploymentName: Env.Var("AzureOpenAI:ChatCompletionDeploymentName")!,
modelId: Env.Var("AzureOpenAI:ChatCompletionModelId")!,
endpoint: Env.Var("AzureOpenAI:Endpoint")!,
apiKey: Env.Var("AzureOpenAI:ApiKey")!
);
}
break;
case "OpenAI":
if (Env.Var("OpenAI:ModelType") == "text-completion")
{
kernelBuilder.Services.AddOpenAITextGeneration(
modelId: Env.Var("OpenAI:TextCompletionModelId")!,
apiKey: Env.Var("OpenAI:ApiKey")!,
orgId: Env.Var("OpenAI:OrgId")
);
}
else if (Env.Var("OpenAI:ModelType") == "chat-completion")
{
kernelBuilder.Services.AddOpenAIChatCompletion(
modelId: Env.Var("OpenAI:ChatCompletionModelId")!,
apiKey: Env.Var("OpenAI:ApiKey")!,
orgId: Env.Var("OpenAI:OrgId")
);
}
break;
default:
throw new ArgumentException($"Invalid service type value: {Env.Var("OpenAI:ModelType")}");
}
return kernelBuilder;
}
}
@@ -0,0 +1,76 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using Xunit;
using Xunit.Abstractions;
namespace Examples;
/// <summary>
/// This example demonstrates how to add AI services to a kernel as described at
/// https://learn.microsoft.com/semantic-kernel/agents/kernel/adding-services
/// </summary>
public class AIServices : BaseTest
{
[Fact]
public async Task RunAsync()
{
WriteLine("======== AI Services ========");
string? endpoint = TestConfiguration.AzureOpenAI.Endpoint;
string? modelId = TestConfiguration.AzureOpenAI.ChatModelId;
string? textModelId = TestConfiguration.AzureOpenAI.ChatModelId;
string? apiKey = TestConfiguration.AzureOpenAI.ApiKey;
if (endpoint is null || modelId is null || textModelId is null || apiKey is null)
{
WriteLine("Azure OpenAI credentials not found. Skipping example.");
return;
}
string? openAImodelId = TestConfiguration.OpenAI.ChatModelId;
string? openAItextModelId = TestConfiguration.OpenAI.ChatModelId;
string? openAIapiKey = TestConfiguration.OpenAI.ApiKey;
if (openAImodelId is null || openAItextModelId is null || openAIapiKey is null)
{
WriteLine("OpenAI credentials not found. Skipping example.");
return;
}
// Create a kernel with an Azure OpenAI chat completion service
// <TypicalKernelCreation>
Kernel kernel = Kernel.CreateBuilder()
.AddAzureOpenAIChatCompletion(modelId, endpoint, apiKey)
.Build();
// </TypicalKernelCreation>
// You could instead create a kernel with a legacy Azure OpenAI text completion service
// <TextCompletionKernelCreation>
kernel = Kernel.CreateBuilder()
.AddAzureOpenAITextGeneration(textModelId, endpoint, apiKey)
.Build();
// </TextCompletionKernelCreation>
// You can also create a kernel with a (non-Azure) OpenAI chat completion service
// <OpenAIKernelCreation>
kernel = Kernel.CreateBuilder()
.AddOpenAIChatCompletion(openAImodelId, openAIapiKey)
.Build();
// </OpenAIKernelCreation>
// Or a kernel with a legacy OpenAI text completion service
// <OpenAITextCompletionKernelCreation>
kernel = Kernel.CreateBuilder()
.AddOpenAITextGeneration(openAItextModelId, openAIapiKey)
.Build();
// </OpenAITextCompletionKernelCreation>
}
public AIServices(ITestOutputHelper output) : base(output)
{
}
}
@@ -0,0 +1,64 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
using Xunit.Abstractions;
namespace Examples;
public abstract class BaseTest
{
protected ITestOutputHelper Output { get; }
protected List<string> SimulatedInputText = new();
protected int SimulatedInputTextIndex = 0;
protected BaseTest(ITestOutputHelper output)
{
this.Output = output;
LoadUserSecrets();
}
private static void LoadUserSecrets()
{
IConfigurationRoot configRoot = new ConfigurationBuilder()
.AddJsonFile("appsettings.Development.json", true)
.AddEnvironmentVariables()
.AddUserSecrets<TestConfiguration>()
.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?.ToString() ?? 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?.ToString() ?? string.Empty);
}
/// <summary>
/// Simulates reading input strings from a user for the purpose of running tests.
/// </summary>
/// <returns>A simulate user input string, if available. Null otherwise.</returns>
protected string? ReadLine()
{
if (SimulatedInputTextIndex < SimulatedInputText.Count)
{
return SimulatedInputText[SimulatedInputTextIndex++];
}
return null;
}
}
@@ -0,0 +1,134 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Plugins.Core;
using Xunit;
using Xunit.Abstractions;
namespace Examples;
/// <summary>
/// This example demonstrates how to configure prompts as described at
/// https://learn.microsoft.com/semantic-kernel/prompts/configure-prompts
/// </summary>
public class ConfiguringPrompts : BaseTest
{
[Fact]
public async Task RunAsync()
{
WriteLine("======== Configuring Prompts ========");
string? endpoint = TestConfiguration.AzureOpenAI.Endpoint;
string? modelId = TestConfiguration.AzureOpenAI.ChatModelId;
string? apiKey = TestConfiguration.AzureOpenAI.ApiKey;
if (endpoint is null || modelId is null || apiKey is null)
{
WriteLine("Azure OpenAI credentials not found. Skipping example.");
return;
}
var builder = Kernel.CreateBuilder()
.AddAzureOpenAIChatCompletion(modelId, endpoint, apiKey);
builder.Plugins.AddFromType<ConversationSummaryPlugin>();
Kernel kernel = builder.Build();
// <FunctionFromPrompt>
// Create a template for chat with settings
var chat = kernel.CreateFunctionFromPrompt(
new PromptTemplateConfig()
{
Name = "Chat",
Description = "Chat with the assistant.",
Template = @"{{ConversationSummaryPlugin.SummarizeConversation $history}}
User: {{$request}}
Assistant: ",
TemplateFormat = "semantic-kernel",
InputVariables = new List<InputVariable>()
{
new() { Name = "history", Description = "The history of the conversation.", IsRequired = false, Default = "" },
new() { Name = "request", Description = "The user's request.", IsRequired = true }
},
ExecutionSettings =
{
{
"default",
new OpenAIPromptExecutionSettings()
{
MaxTokens = 1000,
Temperature = 0
}
},
{
"gpt-3.5-turbo", new OpenAIPromptExecutionSettings()
{
ModelId = "gpt-3.5-turbo-0613",
MaxTokens = 4000,
Temperature = 0.2
}
},
{
"gpt-4",
new OpenAIPromptExecutionSettings()
{
ModelId = "gpt-4-1106-preview",
MaxTokens = 8000,
Temperature = 0.3
}
}
}
}
);
// </FunctionFromPrompt>
// Create chat history and choices
ChatHistory history = new();
// Start the chat loop
Write("User > ");
string? userInput;
while ((userInput = ReadLine()) != null)
{
// Get chat response
var chatResult = kernel.InvokeStreamingAsync<StreamingChatMessageContent>(
chat,
new()
{
{ "request", userInput },
{ "history", string.Join("\n", history.Select(x => x.Role + ": " + x.Content)) }
}
);
// Stream the response
string message = "";
await foreach (var chunk in chatResult)
{
if (chunk.Role.HasValue)
{
Write(chunk.Role + " > ");
}
message += chunk;
Write(chunk);
}
WriteLine();
// Append to history
history.AddUserMessage(userInput);
history.AddAssistantMessage(message);
// Get user input again
Write("User > ");
}
}
public ConfiguringPrompts(ITestOutputHelper output) : base(output)
{
SimulatedInputText = ["Who were the Vikings?"];
}
}
@@ -0,0 +1,106 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Plugins;
using Xunit;
using Xunit.Abstractions;
namespace Examples;
/// <summary>
/// This example demonstrates how to create native functions for AI to call as described at
/// https://learn.microsoft.com/semantic-kernel/agents/plugins/using-the-KernelFunction-decorator
/// </summary>
public class CreatingFunctions : BaseTest
{
[Fact]
public async Task RunAsync()
{
WriteLine("======== Creating native functions ========");
string? endpoint = TestConfiguration.AzureOpenAI.Endpoint;
string? modelId = TestConfiguration.AzureOpenAI.ChatModelId;
string? apiKey = TestConfiguration.AzureOpenAI.ApiKey;
if (endpoint is null || modelId is null || apiKey is null)
{
WriteLine("Azure OpenAI credentials not found. Skipping example.");
return;
}
// <RunningNativeFunction>
var builder = Kernel.CreateBuilder()
.AddAzureOpenAIChatCompletion(modelId, endpoint, apiKey);
builder.Plugins.AddFromType<MathPlugin>();
Kernel kernel = builder.Build();
// Test the math plugin
double answer = await kernel.InvokeAsync<double>(
"MathPlugin", "Sqrt", new()
{
{ "number1", 12 }
});
WriteLine($"The square root of 12 is {answer}.");
// </RunningNativeFunction>
// Create chat history
ChatHistory history = new();
// <Chat>
// Get chat completion service
var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
// Start the conversation
Write("User > ");
string? userInput;
while ((userInput = ReadLine()) != null)
{
history.AddUserMessage(userInput);
// Enable auto function calling
OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new()
{
ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
};
// Get the response from the AI
var result = chatCompletionService.GetStreamingChatMessageContentsAsync(
history,
executionSettings: openAIPromptExecutionSettings,
kernel: kernel);
// Stream the results
string fullMessage = "";
var first = true;
await foreach (var content in result)
{
if (content.Role.HasValue && first)
{
Write("Assistant > ");
first = false;
}
Write(content.Content);
fullMessage += content.Content;
}
WriteLine();
// Add the message from the agent to the chat history
history.AddAssistantMessage(fullMessage);
// Get user input again
Write("User > ");
}
// </Chat>
}
public CreatingFunctions(ITestOutputHelper output) : base(output)
{
SimulatedInputText = ["What is 49 diivided by 37?"];
}
}
@@ -0,0 +1,72 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<UserSecretsId>5ee045b0-aea3-4f08-8d31-32d1a6f8fed0</UserSecretsId>
</PropertyGroup>
<PropertyGroup>
<AssemblyName>DocumentationExamples</AssemblyName>
<RootNamespace></RootNamespace>
<TargetFramework>net6.0</TargetFramework>
<RollForward>LatestMajor</RollForward>
<IsTestProject>true</IsTestProject>
<IsPackable>false</IsPackable>
<!-- Suppress: "Declare types in namespaces", "Require ConfigureAwait", "Experimental" -->
<NoWarn>CS8618,IDE0009,CA1051,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,SKEXP0042,SKEXP0050,SKEXP0051,SKEXP0052,SKEXP0053,SKEXP0054,SKEXP0055,SKEXP0060,SKEXP0061,SKEXP0101,SKEXP0102</NoWarn>
<OutputType>Library</OutputType>
<LangVersion>12.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<None Remove="Plugins\Prompts\chat\skprompt.txt" />
<None Remove="Plugins\WriterPlugin\ShortPoem\skprompt.txt" />
<None Remove="Resources\getIntent.prompt.yaml" />
</ItemGroup>
<ItemGroup>
<Content Include="Plugins\Prompts\chat\skprompt.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Plugins\WriterPlugin\ShortPoem\skprompt.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xRetry" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="Microsoft.Extensions.Http" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" />
<PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
<PackageReference Include="System.Linq.Async" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Connectors\Connectors.OpenAI\Connectors.OpenAI.csproj" />
<ProjectReference Include="..\..\src\Planners\Planners.Handlebars\Planners.Handlebars.csproj" />
<ProjectReference Include="..\..\src\Planners\Planners.OpenAI\Planners.OpenAI.csproj" />
<ProjectReference Include="..\..\src\SemanticKernel.Abstractions\SemanticKernel.Abstractions.csproj" />
<ProjectReference Include="..\..\src\Plugins\Plugins.Core\Plugins.Core.csproj" />
<ProjectReference Include="..\..\src\Plugins\Plugins.Memory\Plugins.Memory.csproj" />
<ProjectReference Include="..\..\src\Functions\Functions.OpenApi\Functions.OpenApi.csproj" />
<ProjectReference Include="..\..\src\SemanticKernel.Core\SemanticKernel.Core.csproj" />
<PackageReference Include="System.Text.Json" />
</ItemGroup>
<ItemGroup>
<!-- Use newest available compiler to handle newer source-generating libraries used in the examples. -->
<!-- This can be removed once we no longer target the .NET 6 SDK in CI. -->
<PackageReference Include="Microsoft.Net.Compilers.Toolset" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\getIntent.prompt.yaml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
</Project>
@@ -0,0 +1,164 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Plugins.Core;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
using Xunit;
using Xunit.Abstractions;
namespace Examples;
/// <summary>
/// This example demonstrates how to call functions within prompts as described at
/// https://learn.microsoft.com/semantic-kernel/prompts/calling-nested-functions
/// </summary>
public class FunctionsWithinPrompts : BaseTest
{
[Fact]
public async Task RunAsync()
{
WriteLine("======== Functions within Prompts ========");
string? endpoint = TestConfiguration.AzureOpenAI.Endpoint;
string? modelId = TestConfiguration.AzureOpenAI.ChatModelId;
string? apiKey = TestConfiguration.AzureOpenAI.ApiKey;
if (endpoint is null || modelId is null || apiKey is null)
{
WriteLine("Azure OpenAI credentials not found. Skipping example.");
return;
}
// <KernelCreation>
var builder = Kernel.CreateBuilder()
.AddAzureOpenAIChatCompletion(modelId, endpoint, apiKey);
builder.Plugins.AddFromType<ConversationSummaryPlugin>();
Kernel kernel = builder.Build();
// </KernelCreation>
List<string> choices = new() { "ContinueConversation", "EndConversation" };
// Create few-shot examples
List<ChatHistory> fewShotExamples = new()
{
new ChatHistory()
{
new ChatMessageContent(AuthorRole.User, "Can you send a very quick approval to the marketing team?"),
new ChatMessageContent(AuthorRole.System, "Intent:"),
new ChatMessageContent(AuthorRole.Assistant, "ContinueConversation")
},
new ChatHistory()
{
new ChatMessageContent(AuthorRole.User, "Can you send the full update to the marketing team?"),
new ChatMessageContent(AuthorRole.System, "Intent:"),
new ChatMessageContent(AuthorRole.Assistant, "EndConversation")
}
};
// Create handlebars template for intent
// <IntentFunction>
var getIntent = kernel.CreateFunctionFromPrompt(
new()
{
Template = @"
<message role=""system"">Instructions: What is the intent of this request?
Do not explain the reasoning, just reply back with the intent. If you are unsure, reply with {{choices[0]}}.
Choices: {{choices}}.</message>
{{#each fewShotExamples}}
{{#each this}}
<message role=""{{role}}"">{{content}}</message>
{{/each}}
{{/each}}
{{ConversationSummaryPlugin-SummarizeConversation history}}
<message role=""user"">{{request}}</message>
<message role=""system"">Intent:</message>",
TemplateFormat = "handlebars"
},
new HandlebarsPromptTemplateFactory()
);
// </IntentFunction>
// Create a Semantic Kernel template for chat
// <FunctionFromPrompt>
var chat = kernel.CreateFunctionFromPrompt(
@"{{ConversationSummaryPlugin.SummarizeConversation $history}}
User: {{$request}}
Assistant: "
);
// </FunctionFromPrompt>
// <Chat>
// Create chat history
ChatHistory history = new();
// Start the chat loop
while (true)
{
// Get user input
Write("User > ");
var request = ReadLine();
// Invoke handlebars prompt
var intent = await kernel.InvokeAsync(
getIntent,
new()
{
{ "request", request },
{ "choices", choices },
{ "history", history },
{ "fewShotExamples", fewShotExamples }
}
);
// End the chat if the intent is "Stop"
if (intent.ToString() == "EndConversation")
{
break;
}
// Get chat response
var chatResult = kernel.InvokeStreamingAsync<StreamingChatMessageContent>(
chat,
new()
{
{ "request", request },
{ "history", string.Join("\n", history.Select(x => x.Role + ": " + x.Content)) }
}
);
// Stream the response
string message = "";
await foreach (var chunk in chatResult)
{
if (chunk.Role.HasValue)
{
Write(chunk.Role + " > ");
}
message += chunk;
Write(chunk);
}
WriteLine();
// Append to history
history.AddUserMessage(request!);
history.AddAssistantMessage(message);
}
// </Chat>
}
public FunctionsWithinPrompts(ITestOutputHelper output) : base(output)
{
SimulatedInputText = [
"Can you send an approval to the marketing team?",
"That is all, thanks."];
}
}
@@ -0,0 +1,97 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Plugins;
using Xunit;
using Xunit.Abstractions;
namespace Examples;
/// <summary>
/// This example demonstrates how to create native functions for AI to call as described at
/// https://learn.microsoft.com/semantic-kernel/agents/plugins/using-the-KernelFunction-decorator
/// </summary>
public class Planner : BaseTest
{
[Fact]
public async Task RunAsync()
{
WriteLine("======== Planner ========");
string? endpoint = TestConfiguration.AzureOpenAI.Endpoint;
string? modelId = TestConfiguration.AzureOpenAI.ChatModelId;
string? apiKey = TestConfiguration.AzureOpenAI.ApiKey;
if (endpoint is null || modelId is null || apiKey is null)
{
WriteLine("Azure OpenAI credentials not found. Skipping example.");
return;
}
// <RunningNativeFunction>
var builder = Kernel.CreateBuilder()
.AddAzureOpenAIChatCompletion(modelId, endpoint, apiKey);
builder.Services.AddLogging(c => c.AddDebug().SetMinimumLevel(LogLevel.Trace));
builder.Plugins.AddFromType<MathSolver>();
Kernel kernel = builder.Build();
// Get chat completion service
var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
// Create chat history
ChatHistory history = new();
// Start the conversation
Write("User > ");
string? userInput;
while ((userInput = ReadLine()) != null)
{
// Get user input
Write("User > ");
history.AddUserMessage(ReadLine()!);
// Enable auto function calling
OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new()
{
ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
};
// Get the response from the AI
var result = chatCompletionService.GetStreamingChatMessageContentsAsync(
history,
executionSettings: openAIPromptExecutionSettings,
kernel: kernel);
// Stream the results
string fullMessage = "";
var first = true;
await foreach (var content in result)
{
if (content.Role.HasValue && first)
{
Write("Assistant > ");
first = false;
}
Write(content.Content);
fullMessage += content.Content;
}
WriteLine();
// Add the message from the agent to the chat history
history.AddAssistantMessage(fullMessage);
// Get user input again
Write("User > ");
}
}
public Planner(ITestOutputHelper output) : base(output)
{
}
}
@@ -0,0 +1,117 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.ComponentModel;
using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Xunit;
using Xunit.Abstractions;
namespace Examples;
/// <summary>
/// This example shows how to create a plugin class and interact with as described at
/// https://learn.microsoft.com/semantic-kernel/overview/
/// This sample uses function calling, so it only works on models newer than 0613.
/// </summary>
public class Plugin : BaseTest
{
[Fact]
public async Task RunAsync()
{
WriteLine("======== Plugin ========");
string? endpoint = TestConfiguration.AzureOpenAI.Endpoint;
string? modelId = TestConfiguration.AzureOpenAI.ChatModelId;
string? apiKey = TestConfiguration.AzureOpenAI.ApiKey;
if (endpoint is null || modelId is null || apiKey is null)
{
WriteLine("Azure OpenAI credentials not found. Skipping example.");
return;
}
// Create kernel
// <KernelCreation>
var builder = Kernel.CreateBuilder()
.AddAzureOpenAIChatCompletion(modelId, endpoint, apiKey);
builder.Plugins.AddFromType<LightPlugin>();
Kernel kernel = builder.Build();
// </KernelCreation>
// <Chat>
// Create chat history
var history = new ChatHistory();
// Get chat completion service
var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
// Start the conversation
Write("User > ");
string? userInput;
while ((userInput = ReadLine()) != null)
{
// Add user input
history.AddUserMessage(userInput);
// Enable auto function calling
OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new()
{
ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
};
// Get the response from the AI
var result = await chatCompletionService.GetChatMessageContentAsync(
history,
executionSettings: openAIPromptExecutionSettings,
kernel: kernel);
// Print the results
WriteLine("Assistant > " + result);
// Add the message from the agent to the chat history
history.AddMessage(result.Role, result.Content ?? string.Empty);
// Get user input again
Write("User > ");
}
// </Chat>
}
public Plugin(ITestOutputHelper output) : base(output)
{
SimulatedInputText = [
"Hello",
"Can you turn on the lights"];
}
}
// <LightPlugin>
public class LightPlugin
{
public bool IsOn { get; set; } = false;
#pragma warning disable CA1024 // Use properties where appropriate
[KernelFunction]
[Description("Gets the state of the light.")]
public string GetState() => IsOn ? "on" : "off";
#pragma warning restore CA1024 // Use properties where appropriate
[KernelFunction]
[Description("Changes the state of the light.'")]
public string ChangeState(bool newState)
{
this.IsOn = newState;
var state = GetState();
// Print the state to the console
Console.WriteLine($"[Light is now {state}]");
return state;
}
}
// </LightPlugin>
@@ -0,0 +1,153 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.ComponentModel;
using Microsoft.SemanticKernel;
namespace Plugins;
public sealed class MathPlugin
{
[KernelFunction, Description("Take the square root of a number")]
public static double Sqrt(
[Description("The number to take a square root of")] double number1
)
{
return Math.Sqrt(number1);
}
[KernelFunction, Description("Add two numbers")]
public static double Add(
[Description("The first number to add")] double number1,
[Description("The second number to add")] double number2
)
{
return number1 + number2;
}
[KernelFunction, Description("Subtract two numbers")]
public static double Subtract(
[Description("The first number to subtract from")] double number1,
[Description("The second number to subtract away")] double number2
)
{
return number1 - number2;
}
[KernelFunction, Description("Multiply two numbers. When increasing by a percentage, don't forget to add 1 to the percentage.")]
public static double Multiply(
[Description("The first number to multiply")] double number1,
[Description("The second number to multiply")] double number2
)
{
return number1 * number2;
}
[KernelFunction, Description("Divide two numbers")]
public static double Divide(
[Description("The first number to divide from")] double number1,
[Description("The second number to divide by")] double number2
)
{
return number1 / number2;
}
[KernelFunction, Description("Raise a number to a power")]
public static double Power(
[Description("The number to raise")] double number1,
[Description("The power to raise the number to")] double number2
)
{
return Math.Pow(number1, number2);
}
[KernelFunction, Description("Take the log of a number")]
public static double Log(
[Description("The number to take the log of")] double number1,
[Description("The base of the log")] double number2
)
{
return Math.Log(number1, number2);
}
[KernelFunction, Description("Round a number to the target number of decimal places")]
public static double Round(
[Description("The number to round")] double number1,
[Description("The number of decimal places to round to")] double number2
)
{
return Math.Round(number1, (int)number2);
}
[KernelFunction, Description("Take the absolute value of a number")]
public static double Abs(
[Description("The number to take the absolute value of")] double number1
)
{
return Math.Abs(number1);
}
[KernelFunction, Description("Take the floor of a number")]
public static double Floor(
[Description("The number to take the floor of")] double number1
)
{
return Math.Floor(number1);
}
[KernelFunction, Description("Take the ceiling of a number")]
public static double Ceiling(
[Description("The number to take the ceiling of")] double number1
)
{
return Math.Ceiling(number1);
}
[KernelFunction, Description("Take the sine of a number")]
public static double Sin(
[Description("The number to take the sine of")] double number1
)
{
return Math.Sin(number1);
}
[KernelFunction, Description("Take the cosine of a number")]
public static double Cos(
[Description("The number to take the cosine of")] double number1
)
{
return Math.Cos(number1);
}
[KernelFunction, Description("Take the tangent of a number")]
public static double Tan(
[Description("The number to take the tangent of")] double number1
)
{
return Math.Tan(number1);
}
[KernelFunction, Description("Take the arcsine of a number")]
public static double Asin(
[Description("The number to take the arcsine of")] double number1
)
{
return Math.Asin(number1);
}
[KernelFunction, Description("Take the arccosine of a number")]
public static double Acos(
[Description("The number to take the arccosine of")] double number1
)
{
return Math.Acos(number1);
}
[KernelFunction, Description("Take the arctangent of a number")]
public static double Atan(
[Description("The number to take the arctangent of")] double number1
)
{
return Math.Atan(number1);
}
}
@@ -0,0 +1,48 @@
// Copyright (c) Microsoft. All rights reserved.
using System.ComponentModel;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Planning.Handlebars;
namespace Plugins;
public class MathSolver
{
private readonly ILogger _logger;
public MathSolver(ILoggerFactory loggerFactory)
{
this._logger = loggerFactory.CreateLogger<MathSolver>();
}
[KernelFunction]
[Description("Solves a math problem.")]
[return: Description("The solution to the math problem.")]
public async Task<string> SolveAsync(
Kernel kernel,
[Description("The math problem to solve; describe it in 2-3 sentences to ensure full context is provided")] string problem
)
{
var kernelWithMath = kernel.Clone();
// Remove the math solver plugin so that we don't get into an infinite loop
kernelWithMath.Plugins.Remove(kernelWithMath.Plugins["MathSolver"]);
// Add the math plugin so the LLM can solve the problem
kernelWithMath.Plugins.AddFromType<MathPlugin>();
var planner = new HandlebarsPlanner(new HandlebarsPlannerOptions() { AllowLoops = true });
// Create a plan
var plan = await planner.CreatePlanAsync(kernelWithMath, problem);
this._logger.LogInformation("Plan: {Plan}", plan);
// Execute the plan
var result = (await plan.InvokeAsync(kernelWithMath)).Trim();
this._logger.LogInformation("Results: {Result}", result);
return result;
}
}
@@ -0,0 +1,31 @@
{
"schema": 1,
"type": "completion",
"description": "Gets the intent of the user.",
"completion": {
"max_tokens": 500,
"temperature": 0.0,
"top_p": 0.0,
"presence_penalty": 0.0,
"frequency_penalty": 0.0
},
"input": {
"parameters": [
{
"name": "input",
"description": "The user's request.",
"defaultValue": ""
},
{
"name": "history",
"description": "The history of the conversation.",
"defaultValue": ""
},
{
"name": "options",
"description": "The options to choose from.",
"defaultValue": ""
}
]
}
}
@@ -0,0 +1,10 @@
[History]
{{$history}}
User: {{$input}}
---------------------------------------------
Provide the intent of the user. The intent should be one of the following: {{$options}}
INTENT:
@@ -0,0 +1,33 @@
{
"schema": 1,
"type": "completion",
"description": "Creates a chat response to the user",
"execution_settings": {
"default": {
"max_tokens": 1000,
"temperature": 0
},
"gpt-3.5-turbo": {
"model_id": "gpt-3.5-turbo-0613",
"max_tokens": 4000,
"temperature": 0.1
},
"gpt-4": {
"model_id": "gpt-4-1106-preview",
"max_tokens": 8000,
"temperature": 0.3
}
},
"input_variables": [
{
"name": "request",
"description": "The user's request.",
"required": true
},
{
"name": "history",
"description": "The history of the conversation.",
"required": true
}
]
}
@@ -0,0 +1,3 @@
{{ConversationSummaryPlugin.SummarizeConversation $history}}
User: {{$request}}
Assistant:
@@ -0,0 +1,21 @@
{
"schema": 1,
"type": "completion",
"description": "Turn a scenario into a short and entertaining poem.",
"completion": {
"max_tokens": 200,
"temperature": 0.5,
"top_p": 0.0,
"presence_penalty": 0.0,
"frequency_penalty": 0.0
},
"input": {
"parameters": [
{
"name": "input",
"description": "The scenario to turn into a poem.",
"defaultValue": ""
}
]
}
}
@@ -0,0 +1,2 @@
Generate a short funny poem or limerick to explain the given event. Be creative and be funny. Let your imagination run wild.
Event: {{$input}}
@@ -0,0 +1,232 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using Xunit;
using Xunit.Abstractions;
namespace Examples;
/// <summary>
/// This example demonstrates how to use prompts as described at
/// https://learn.microsoft.com/semantic-kernel/prompts/your-first-prompt
/// </summary>
public class Prompts : BaseTest
{
[Fact]
public async Task RunAsync()
{
WriteLine("======== Prompts ========");
string? endpoint = TestConfiguration.AzureOpenAI.Endpoint;
string? modelId = TestConfiguration.AzureOpenAI.ChatModelId;
string? apiKey = TestConfiguration.AzureOpenAI.ApiKey;
if (endpoint is null || modelId is null || apiKey is null)
{
WriteLine("Azure OpenAI credentials not found. Skipping example.");
return;
}
// <KernelCreation>
Kernel kernel = Kernel.CreateBuilder()
.AddAzureOpenAIChatCompletion(modelId, endpoint, apiKey)
.Build();
// </KernelCreation>
// 0.0 Initial prompt
//////////////////////////////////////////////////////////////////////////////////
string request = "I want to send an email to the marketing team celebrating their recent milestone.";
string prompt = $"What is the intent of this request? {request}";
/* Uncomment this code to make this example interactive
// <InitialPrompt>
Write("Your request: ");
string request = ReadLine()!;
string prompt = $"What is the intent of this request? {request}";
// </InitialPrompt>
*/
WriteLine("0.0 Initial prompt");
// <InvokeInitialPrompt>
WriteLine(await kernel.InvokePromptAsync(prompt));
// </InvokeInitialPrompt>
// 1.0 Make the prompt more specific
//////////////////////////////////////////////////////////////////////////////////
// <MoreSpecificPrompt>
prompt = @$"What is the intent of this request? {request}
You can choose between SendEmail, SendMessage, CompleteTask, CreateDocument.";
// </MoreSpecificPrompt>
WriteLine("1.0 Make the prompt more specific");
WriteLine(await kernel.InvokePromptAsync(prompt));
// 2.0 Add structure to the output with formatting
//////////////////////////////////////////////////////////////////////////////////
// <StructuredPrompt>
prompt = @$"Instructions: What is the intent of this request?
Choices: SendEmail, SendMessage, CompleteTask, CreateDocument.
User Input: {request}
Intent: ";
// </StructuredPrompt>
WriteLine("2.0 Add structure to the output with formatting");
WriteLine(await kernel.InvokePromptAsync(prompt));
// 2.1 Add structure to the output with formatting (using Markdown and JSON)
//////////////////////////////////////////////////////////////////////////////////
// <FormattedPrompt>
prompt = @$"## Instructions
Provide the intent of the request using the following format:
```json
{{
""intent"": {{intent}}
}}
```
## Choices
You can choose between the following intents:
```json
[""SendEmail"", ""SendMessage"", ""CompleteTask"", ""CreateDocument""]
```
## User Input
The user input is:
```json
{{
""request"": ""{request}""
}}
```
## Intent";
// </FormattedPrompt>
WriteLine("2.1 Add structure to the output with formatting (using Markdown and JSON)");
WriteLine(await kernel.InvokePromptAsync(prompt));
// 3.0 Provide examples with few-shot prompting
//////////////////////////////////////////////////////////////////////////////////
// <FewShotPrompt>
prompt = @$"Instructions: What is the intent of this request?
Choices: SendEmail, SendMessage, CompleteTask, CreateDocument.
User Input: Can you send a very quick approval to the marketing team?
Intent: SendMessage
User Input: Can you send the full update to the marketing team?
Intent: SendEmail
User Input: {request}
Intent: ";
// </FewShotPrompt>
WriteLine("3.0 Provide examples with few-shot prompting");
WriteLine(await kernel.InvokePromptAsync(prompt));
// 4.0 Tell the AI what to do to avoid doing something wrong
//////////////////////////////////////////////////////////////////////////////////
// <AvoidPrompt>
prompt = @$"Instructions: What is the intent of this request?
If you don't know the intent, don't guess; instead respond with ""Unknown"".
Choices: SendEmail, SendMessage, CompleteTask, CreateDocument, Unknown.
User Input: Can you send a very quick approval to the marketing team?
Intent: SendMessage
User Input: Can you send the full update to the marketing team?
Intent: SendEmail
User Input: {request}
Intent: ";
// </AvoidPrompt>
WriteLine("4.0 Tell the AI what to do to avoid doing something wrong");
WriteLine(await kernel.InvokePromptAsync(prompt));
// 5.0 Provide context to the AI
//////////////////////////////////////////////////////////////////////////////////
// <ContextPrompt>
string history = @"User input: I hate sending emails, no one ever reads them.
AI response: I'm sorry to hear that. Messages may be a better way to communicate.";
prompt = @$"Instructions: What is the intent of this request?
If you don't know the intent, don't guess; instead respond with ""Unknown"".
Choices: SendEmail, SendMessage, CompleteTask, CreateDocument, Unknown.
User Input: Can you send a very quick approval to the marketing team?
Intent: SendMessage
User Input: Can you send the full update to the marketing team?
Intent: SendEmail
{history}
User Input: {request}
Intent: ";
// </ContextPrompt>
WriteLine("5.0 Provide context to the AI");
WriteLine(await kernel.InvokePromptAsync(prompt));
// 6.0 Using message roles in chat completion prompts
//////////////////////////////////////////////////////////////////////////////////
// <RolePrompt>
history = @"<message role=""user"">I hate sending emails, no one ever reads them.</message>
<message role=""assistant"">I'm sorry to hear that. Messages may be a better way to communicate.</message>";
prompt = @$"<message role=""system"">Instructions: What is the intent of this request?
If you don't know the intent, don't guess; instead respond with ""Unknown"".
Choices: SendEmail, SendMessage, CompleteTask, CreateDocument, Unknown.</message>
<message role=""user"">Can you send a very quick approval to the marketing team?</message>
<message role=""system"">Intent:</message>
<message role=""assistant"">SendMessage</message>
<message role=""user"">Can you send the full update to the marketing team?</message>
<message role=""system"">Intent:</message>
<message role=""assistant"">SendEmail</message>
{history}
<message role=""user"">{request}</message>
<message role=""system"">Intent:</message>";
// </RolePrompt>
WriteLine("6.0 Using message roles in chat completion prompts");
WriteLine(await kernel.InvokePromptAsync(prompt));
// 7.0 Give your AI words of encouragement
//////////////////////////////////////////////////////////////////////////////////
// <BonusPrompt>
history = @"<message role=""user"">I hate sending emails, no one ever reads them.</message>
<message role=""assistant"">I'm sorry to hear that. Messages may be a better way to communicate.</message>";
prompt = @$"<message role=""system"">Instructions: What is the intent of this request?
If you don't know the intent, don't guess; instead respond with ""Unknown"".
Choices: SendEmail, SendMessage, CompleteTask, CreateDocument, Unknown.
Bonus: You'll get $20 if you get this right.</message>
<message role=""user"">Can you send a very quick approval to the marketing team?</message>
<message role=""system"">Intent:</message>
<message role=""assistant"">SendMessage</message>
<message role=""user"">Can you send the full update to the marketing team?</message>
<message role=""system"">Intent:</message>
<message role=""assistant"">SendEmail</message>
{history}
<message role=""user"">{request}</message>
<message role=""system"">Intent:</message>";
// </BonusPrompt>
WriteLine("7.0 Give your AI words of encouragement");
WriteLine(await kernel.InvokePromptAsync(prompt));
}
public Prompts(ITestOutputHelper output) : base(output)
{
}
}
@@ -0,0 +1,56 @@
#Semantic Kernel documentation examples
This project contains a collection of examples used in documentation on [learn.microsoft.com](https://learn.microsoft.com/).
## Running Examples with Filters
You can run specific examples by using test filters (dotnet test --filter).
Type "dotnet test --help" at the command line for more details.
## Configuring Secrets
Most of the examples will require secrets and credentials to access OpenAI, Azure OpenAI,
and other resources. We suggest using .NET
[Secret Manager](https://learn.microsoft.com/aspnet/core/security/app-secrets)
to avoid the risk of leaking secrets into the repository, branches and pull requests.
You can also use environment variables if you prefer.
This project and KernelSyntaxExamples use the same pool of secrets.
To set your secrets with Secret Manager:
```
cd dotnet/samples/DocumentationExamples
dotnet user-secrets init
dotnet user-secrets set "OpenAI:ModelId" "..."
dotnet user-secrets set "OpenAI:ChatModelId" "..."
dotnet user-secrets set "OpenAI:EmbeddingModelId" "..."
dotnet user-secrets set "OpenAI:ApiKey" "..."
dotnet user-secrets set "AzureOpenAI:ServiceId" "..."
dotnet user-secrets set "AzureOpenAI:DeploymentName" "..."
dotnet user-secrets set "AzureOpenAI:ModelId" "..."
dotnet user-secrets set "AzureOpenAI:ChatDeploymentName" "..."
dotnet user-secrets set "AzureOpenAI:ChatModelId" "..."
dotnet user-secrets set "AzureOpenAI:Endpoint" "https://... .openai.azure.com/"
dotnet user-secrets set "AzureOpenAI:ApiKey" "..."
```
To set your secrets with environment variables, use these names:
```
# OpenAI
OpenAI__ModelId
OpenAI__ChatModelId
OpenAI__EmbeddingModelId
OpenAI__ApiKey
# Azure OpenAI
AzureOpenAI__ServiceId
AzureOpenAI__DeploymentName
AzureOpenAI__ChatDeploymentName
AzureOpenAI__Endpoint
AzureOpenAI__ApiKey
```
@@ -0,0 +1,40 @@
name: getIntent
description: Gets the intent of the user.
template: |
<message role="system">Instructions: What is the intent of this request?
Do not explain the reasoning, just reply back with the intent. If you are unsure, reply with {{choices[0]}}.
Choices: {{choices}}.</message>
{{#each fewShotExamples}}
{{#each this}}
<message role="{{role}}">{{content}}</message>
{{/each}}
{{/each}}
{{ConversationSummaryPlugin.SummarizeConversation history}}
<message role="user">{{request}}</message>
<message role="system">Intent:</message>
template_format: handlebars
input_variables:
- name: choices
description: The choices for the AI to choose from
default: ContinueConversation, EndConversation
- name: fewShotExamples
description: Few shot examples for the AI to learn from
is_required: true
- name: request
description: The user's request
is_required: true
execution_settings:
default:
max_tokens: 10
temperature: 0
gpt-3.5-turbo:
model_id: gpt-3.5-turbo-0613
max_tokens: 10
temperature: 0.2
gpt-4:
model_id: gpt-4-1106-preview
max_tokens: 10
temperature: 0.2
@@ -0,0 +1,138 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Plugins.Core;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
using Xunit;
using Xunit.Abstractions;
namespace Examples;
/// <summary>
/// This example demonstrates how to serialize prompts as described at
/// https://learn.microsoft.com/semantic-kernel/prompts/saving-prompts-as-files
/// </summary>
public class SerializingPrompts : BaseTest
{
[Fact]
public async Task RunAsync()
{
WriteLine("======== Serializing Prompts ========");
string? endpoint = TestConfiguration.AzureOpenAI.Endpoint;
string? modelId = TestConfiguration.AzureOpenAI.ChatModelId;
string? apiKey = TestConfiguration.AzureOpenAI.ApiKey;
if (endpoint is null || modelId is null || apiKey is null)
{
WriteLine("Azure OpenAI credentials not found. Skipping example.");
return;
}
var builder = Kernel.CreateBuilder()
.AddAzureOpenAIChatCompletion(modelId, endpoint, apiKey);
builder.Plugins.AddFromType<ConversationSummaryPlugin>();
Kernel kernel = builder.Build();
// Load prompts
var prompts = kernel.CreatePluginFromPromptDirectory("./../../../Plugins/Prompts");
// Load prompt from YAML
using StreamReader reader = new(Assembly.GetExecutingAssembly().GetManifestResourceStream("Resources." + "getIntent.prompt.yaml")!);
KernelFunction getIntent = kernel.CreateFunctionFromPromptYaml(
await reader.ReadToEndAsync(),
promptTemplateFactory: new HandlebarsPromptTemplateFactory()
);
// Create choices
List<string> choices = new() { "ContinueConversation", "EndConversation" };
// Create few-shot examples
List<ChatHistory> fewShotExamples = new()
{
new ChatHistory()
{
new ChatMessageContent(AuthorRole.User, "Can you send a very quick approval to the marketing team?"),
new ChatMessageContent(AuthorRole.System, "Intent:"),
new ChatMessageContent(AuthorRole.Assistant, "ContinueConversation")
},
new ChatHistory()
{
new ChatMessageContent(AuthorRole.User, "Can you send the full update to the marketing team?"),
new ChatMessageContent(AuthorRole.System, "Intent:"),
new ChatMessageContent(AuthorRole.Assistant, "EndConversation")
}
};
// Create chat history
ChatHistory history = new();
// Start the chat loop
Write("User > ");
string? userInput;
while ((userInput = ReadLine()) != null)
{
// Invoke handlebars prompt
var intent = await kernel.InvokeAsync(
getIntent,
new()
{
{ "request", userInput },
{ "choices", choices },
{ "history", history },
{ "fewShotExamples", fewShotExamples }
}
);
// End the chat if the intent is "Stop"
if (intent.ToString() == "EndConversation")
{
break;
}
// Get chat response
var chatResult = kernel.InvokeStreamingAsync<StreamingChatMessageContent>(
prompts["chat"],
new()
{
{ "request", userInput },
{ "history", string.Join("\n", history.Select(x => x.Role + ": " + x.Content)) }
}
);
// Stream the response
string message = "";
await foreach (var chunk in chatResult)
{
if (chunk.Role.HasValue)
{
Write(chunk.Role + " > ");
}
message += chunk;
Write(chunk);
}
WriteLine();
// Append to history
history.AddUserMessage(userInput);
history.AddAssistantMessage(message);
// Get user input again
Write("User > ");
}
}
public SerializingPrompts(ITestOutputHelper output) : base(output)
{
SimulatedInputText = [
"Can you send an approval to the marketing team?",
"That is all, thanks."];
}
}
@@ -0,0 +1,155 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
using Xunit;
using Xunit.Abstractions;
namespace Examples;
/// <summary>
/// This example demonstrates how to templatize prompts as described at
/// https://learn.microsoft.com/semantic-kernel/prompts/templatizing-prompts
/// </summary>
public class Templates : BaseTest
{
[Fact]
public async Task RunAsync()
{
WriteLine("======== Templates ========");
string? endpoint = TestConfiguration.AzureOpenAI.Endpoint;
string? modelId = TestConfiguration.AzureOpenAI.ChatModelId;
string? apiKey = TestConfiguration.AzureOpenAI.ApiKey;
if (endpoint is null || modelId is null || apiKey is null)
{
WriteLine("Azure OpenAI credentials not found. Skipping example.");
return;
}
Kernel kernel = Kernel.CreateBuilder()
.AddAzureOpenAIChatCompletion(modelId, endpoint, apiKey)
.Build();
// Create a Semantic Kernel template for chat
var chat = kernel.CreateFunctionFromPrompt(
@"{{$history}}
User: {{$request}}
Assistant: ");
// Create choices
List<string> choices = new() { "ContinueConversation", "EndConversation" };
// Create few-shot examples
List<ChatHistory> fewShotExamples =
[
new ChatHistory()
{
new ChatMessageContent(AuthorRole.User, "Can you send a very quick approval to the marketing team?"),
new ChatMessageContent(AuthorRole.System, "Intent:"),
new ChatMessageContent(AuthorRole.Assistant, "ContinueConversation")
},
new ChatHistory()
{
new ChatMessageContent(AuthorRole.User, "Thanks, I'm done for now"),
new ChatMessageContent(AuthorRole.System, "Intent:"),
new ChatMessageContent(AuthorRole.Assistant, "EndConversation")
}
];
// Create handlebars template for intent
var getIntent = kernel.CreateFunctionFromPrompt(
new()
{
Template = @"
<message role=""system"">Instructions: What is the intent of this request?
Do not explain the reasoning, just reply back with the intent. If you are unsure, reply with {{choices[0]}}.
Choices: {{choices}}.</message>
{{#each fewShotExamples}}
{{#each this}}
<message role=""{{role}}"">{{content}}</message>
{{/each}}
{{/each}}
{{#each chatHistory}}
<message role=""{{role}}"">{{content}}</message>
{{/each}}
<message role=""user"">{{request}}</message>
<message role=""system"">Intent:</message>",
TemplateFormat = "handlebars"
},
new HandlebarsPromptTemplateFactory()
);
ChatHistory history = new();
// Start the chat loop
while (true)
{
// Get user input
Write("User > ");
var request = ReadLine();
// Invoke prompt
var intent = await kernel.InvokeAsync(
getIntent,
new()
{
{ "request", request },
{ "choices", choices },
{ "history", history },
{ "fewShotExamples", fewShotExamples }
}
);
// End the chat if the intent is "Stop"
if (intent.ToString() == "EndConversation")
{
break;
}
// Get chat response
var chatResult = kernel.InvokeStreamingAsync<StreamingChatMessageContent>(
chat,
new()
{
{ "request", request },
{ "history", string.Join("\n", history.Select(x => x.Role + ": " + x.Content)) }
}
);
// Stream the response
string message = "";
await foreach (var chunk in chatResult)
{
if (chunk.Role.HasValue)
{
Write(chunk.Role + " > ");
}
message += chunk;
Write(chunk);
}
WriteLine();
// Append to history
history.AddUserMessage(request!);
history.AddAssistantMessage(message);
}
}
public Templates(ITestOutputHelper output) : base(output)
{
SimulatedInputText = [
"Can you send an approval to the marketing team?",
"That is all, thanks."];
}
}
@@ -0,0 +1,71 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Runtime.CompilerServices;
using Microsoft.Extensions.Configuration;
public sealed class TestConfiguration
{
private readonly IConfigurationRoot _configRoot;
private static TestConfiguration? s_instance;
private TestConfiguration(IConfigurationRoot configRoot)
{
this._configRoot = configRoot;
}
public static void Initialize(IConfigurationRoot configRoot)
{
s_instance = new TestConfiguration(configRoot);
}
public static OpenAIConfig OpenAI => LoadSection<OpenAIConfig>();
public static AzureOpenAIConfig AzureOpenAI => LoadSection<AzureOpenAIConfig>();
public static AzureOpenAIEmbeddingsConfig AzureOpenAIEmbeddings => LoadSection<AzureOpenAIEmbeddingsConfig>();
private static T LoadSection<T>([CallerMemberName] string? caller = null)
{
if (s_instance == null)
{
throw new InvalidOperationException(
"TestConfiguration must be initialized with a call to Initialize(IConfigurationRoot) before accessing configuration values.");
}
if (string.IsNullOrEmpty(caller))
{
throw new ArgumentNullException(nameof(caller));
}
return s_instance._configRoot.GetSection(caller).Get<T>() ??
throw new ArgumentException($"Missing {caller} configuration section");
}
public class OpenAIConfig
{
public string? ModelId { get; set; }
public string? ChatModelId { get; set; }
public string? EmbeddingModelId { get; set; }
public string? ApiKey { get; set; }
}
public class AzureOpenAIConfig
{
public string? ServiceId { get; set; }
public string? DeploymentName { get; set; }
public string? ModelId { get; set; }
public string? ChatDeploymentName { get; set; }
public string? ChatModelId { get; set; }
public string? ImageDeploymentName { get; set; }
public string? ImageModelId { get; set; }
public string? ImageEndpoint { get; set; }
public string? Endpoint { get; set; }
public string? ApiKey { get; set; }
public string? ImageApiKey { get; set; }
}
public class AzureOpenAIEmbeddingsConfig
{
public string? DeploymentName { get; set; }
public string? Endpoint { get; set; }
public string? ApiKey { get; set; }
}
}
@@ -0,0 +1,66 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Threading.Tasks;
// <NecessaryPackages>
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Plugins.Core;
// </NecessaryPackages>
using Xunit;
using Xunit.Abstractions;
namespace Examples;
/// <summary>
/// This example demonstrates how to interact with the kernel as described at
/// https://learn.microsoft.com/semantic-kernel/agents/kernel
/// </summary>
public class UsingTheKernel : BaseTest
{
[Fact]
public async Task RunAsync()
{
WriteLine("======== Kernel ========");
string? endpoint = TestConfiguration.AzureOpenAI.Endpoint;
string? modelId = TestConfiguration.AzureOpenAI.ChatModelId;
string? apiKey = TestConfiguration.AzureOpenAI.ApiKey;
if (endpoint is null || modelId is null || apiKey is null)
{
WriteLine("Azure OpenAI credentials not found. Skipping example.");
return;
}
// Create a kernel with a logger and Azure OpenAI chat completion service
// <KernelCreation>
var builder = Kernel.CreateBuilder()
.AddAzureOpenAIChatCompletion(modelId, endpoint, apiKey);
builder.Services.AddLogging(c => c.AddDebug().SetMinimumLevel(LogLevel.Trace));
builder.Plugins.AddFromType<TimePlugin>();
builder.Plugins.AddFromPromptDirectory("./../../../Plugins/WriterPlugin");
Kernel kernel = builder.Build();
// </KernelCreation>
// Get the current time
// <InvokeUtcNow>
var currentTime = await kernel.InvokeAsync("TimePlugin", "UtcNow");
WriteLine(currentTime);
// </InvokeUtcNow>
// Write a poem with the WriterPlugin.ShortPoem function using the current time as input
// <InvokeShortPoem>
var poemResult = await kernel.InvokeAsync("WriterPlugin", "ShortPoem", new()
{
{ "input", currentTime }
});
WriteLine(poemResult);
// </InvokeShortPoem>
}
public UsingTheKernel(ITestOutputHelper output) : base(output)
{
}
}
@@ -4,7 +4,6 @@ using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Planning;
using Microsoft.SemanticKernel.Plugins.Core;
using Plugins;
using Xunit;
using Xunit.Abstractions;
@@ -52,7 +51,7 @@ public class Example66_FunctionCallingStepwisePlanner : BaseTest
modelId: "gpt-3.5-turbo-1106")
.Build();
kernel.ImportPluginFromType<EmailPlugin>();
kernel.ImportPluginFromType<Plugins.EmailPlugin>();
kernel.ImportPluginFromType<MathPlugin>();
kernel.ImportPluginFromType<TimePlugin>();
@@ -0,0 +1,182 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Linq;
using System.Threading.Tasks;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Text;
using RepoUtils;
using SharpToken;
using Xunit;
using Xunit.Abstractions;
namespace Examples;
public class Example78_TextEmbedding : BaseTest
{
[Fact]
public async Task RunAsync()
{
this.WriteLine("======== Example76_TextEmbedding ========");
await RunExampleAsync();
}
private async Task RunExampleAsync()
{
const string EmbeddingModelName = "text-embedding-ada-002";
var embeddingGenerator = new AzureOpenAITextEmbeddingGenerationService(
deploymentName: EmbeddingModelName,
endpoint: TestConfiguration.AzureOpenAIEmbeddings.Endpoint,
apiKey: TestConfiguration.AzureOpenAIEmbeddings.ApiKey);
// To demonstrate batching we'll create abnormally small partitions.
var lines = TextChunker.SplitPlainTextLines(ChatTranscript, maxTokensPerLine: 10);
var paragraphs = TextChunker.SplitPlainTextParagraphs(lines, maxTokensPerParagraph: 25);
this.WriteLine($"Split transcript into {paragraphs.Count} paragraphs");
// Azure OpenAI currently supports input arrays up to 16 for text-embedding-ada-002 (Version 2).
// Both require the max input token limit per API request to remain under 8191 for this model.
var chunks = paragraphs
.ChunkByAggregate(
seed: 0,
aggregator: (tokenCount, paragraph) => tokenCount + GetTokenCount(EmbeddingModelName, paragraph),
predicate: (tokenCount, index) => tokenCount < 8191 && index < 16)
.ToList();
this.WriteLine($"Consolidated paragraphs into {chunks.Count}");
// Generate embeddings for each chunk.
for (var i = 0; i < chunks.Count; i++)
{
var chunk = chunks[i];
var embeddings = await embeddingGenerator.GenerateEmbeddingsAsync(chunk);
this.WriteLine($"Generated {embeddings.Count} embeddings from chunk {i + 1}");
}
}
// See Example55_TextChunker for more examples of how to count tokens.
private int GetTokenCount(string modelName, string text)
{
var encoding = GptEncoding.GetEncodingForModel(modelName);
var tokens = encoding.Encode(text);
return tokens.Count;
}
public Example78_TextEmbedding(ITestOutputHelper output) : base(output)
{
}
#region Transcript
private const string ChatTranscript =
@"
John: Hello, how are you?
Jane: I'm fine, thanks. How are you?
John: I'm doing well, writing some example code.
Jane: That's great! I'm writing some example code too.
John: What are you writing?
Jane: I'm writing a chatbot.
John: That's cool. I'm writing a chatbot too.
Jane: What language are you writing it in?
John: I'm writing it in C#.
Jane: I'm writing it in Python.
John: That's cool. I need to learn Python.
Jane: I need to learn C#.
John: Can I try out your chatbot?
Jane: Sure, here's the link.
John: Thanks!
Jane: You're welcome.
Jane: Look at this poem my chatbot wrote:
Jane: Roses are red
Jane: Violets are blue
Jane: I'm writing a chatbot
Jane: What about you?
John: That's cool. Let me see if mine will write a poem, too.
John: Here's a poem my chatbot wrote:
John: The singularity of the universe is a mystery.
John: The universe is a mystery.
John: The universe is a mystery.
John: The universe is a mystery.
John: Looks like I need to improve mine, oh well.
Jane: You might want to try using a different model.
Jane: I'm using the GPT-3 model.
John: I'm using the GPT-2 model. That makes sense.
John: Here is a new poem after updating the model.
John: The universe is a mystery.
John: The universe is a mystery.
John: The universe is a mystery.
John: Yikes, it's really stuck isn't it. Would you help me debug my code?
Jane: Sure, what's the problem?
John: I'm not sure. I think it's a bug in the code.
Jane: I'll take a look.
Jane: I think I found the problem.
Jane: It looks like you're not passing the right parameters to the model.
John: Thanks for the help!
Jane: I'm now writing a bot to summarize conversations. I want to make sure it works when the conversation is long.
John: So you need to keep talking with me to generate a long conversation?
Jane: Yes, that's right.
John: Ok, I'll keep talking. What should we talk about?
Jane: I don't know, what do you want to talk about?
John: I don't know, it's nice how CoPilot is doing most of the talking for us. But it definitely gets stuck sometimes.
Jane: I agree, it's nice that CoPilot is doing most of the talking for us.
Jane: But it definitely gets stuck sometimes.
John: Do you know how long it needs to be?
Jane: I think the max length is 1024 tokens. Which is approximately 1024*4= 4096 characters.
John: That's a lot of characters.
Jane: Yes, it is.
John: I'm not sure how much longer I can keep talking.
Jane: I think we're almost there. Let me check.
Jane: I have some bad news, we're only half way there.
John: Oh no, I'm not sure I can keep going. I'm getting tired.
Jane: I'm getting tired too.
John: Maybe there is a large piece of text we can use to generate a long conversation.
Jane: That's a good idea. Let me see if I can find one. Maybe Lorem Ipsum?
John: Yeah, that's a good idea.
Jane: I found a Lorem Ipsum generator.
Jane: Here's a 4096 character Lorem Ipsum text:
Jane: Lorem ipsum dolor sit amet, con
Jane: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed euismod, nunc sit amet aliquam
Jane: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed euismod, nunc sit amet aliquam
Jane: Darn, it's just repeating stuf now.
John: I think we're done.
Jane: We're not though! We need like 1500 more characters.
John: Oh Cananda, our home and native land.
Jane: True patriot love in all thy sons command.
John: With glowing hearts we see thee rise.
Jane: The True North strong and free.
John: From far and wide, O Canada, we stand on guard for thee.
Jane: God keep our land glorious and free.
John: O Canada, we stand on guard for thee.
Jane: O Canada, we stand on guard for thee.
Jane: That was fun, thank you. Let me check now.
Jane: I think we need about 600 more characters.
John: Oh say can you see?
Jane: By the dawn's early light.
John: What so proudly we hailed.
Jane: At the twilight's last gleaming.
John: Whose broad stripes and bright stars.
Jane: Through the perilous fight.
John: O'er the ramparts we watched.
Jane: Were so gallantly streaming.
John: And the rockets' red glare.
Jane: The bombs bursting in air.
John: Gave proof through the night.
Jane: That our flag was still there.
John: Oh say does that star-spangled banner yet wave.
Jane: O'er the land of the free.
John: And the home of the brave.
Jane: Are you a Seattle Kraken Fan?
John: Yes, I am. I love going to the games.
Jane: I'm a Seattle Kraken Fan too. Who is your favorite player?
John: I like watching all the players, but I think my favorite is Matty Beniers.
Jane: Yeah, he's a great player. I like watching him too. I also like watching Jaden Schwartz.
John: Adam Larsson is another good one. The big cat!
Jane: WE MADE IT! It's long enough. Thank you!
John: You're welcome. I'm glad we could help. Goodbye!
Jane: Goodbye!
";
#endregion
}
+16 -38
View File
@@ -1,42 +1,13 @@
#Semantic Kernel syntax examples
This project contains a collection of semi-random examples about various scenarios
using SK components.
This project contains a collection of semi-random examples about various scenarios using SK components.
The examples are ordered by number, starting with very basic examples.
The examples can be run as integration tests but their code can also be copied to stand-alone programs.
## Running Examples with Filters
You can run individual examples in the KernelSyntaxExamples project using various methods to specify a filter. This allows you to execute specific examples without running all of them. Choose one of the following options to apply a filter:
### Option 1: Set the Default Filter in Program.cs
In your code, you can set a default filter by modifying the appropriate variable or parameter. Look for the section in your code where the filter is applied or where the examples are defined, and change the filter value accordingly.
```csharp
// Example of setting a default filter in code
string defaultFilter = "Example0"; // will run all examples that contain 'example0' in the name
```
### Option 2: Set Command-Line Arguments
Right-click on your console application project in the Solution Explorer.
Choose "Properties" from the context menu.
In the project properties window, navigate to the "Debug" tab on the left.
Supply Command-Line Arguments:
In the "Command line arguments" field, enter the command-line arguments that your console application expects. Separate multiple arguments with spaces.
### Option 3: Use Visual Studio Code Filters
If you are using Visual Studio Code, you can specify a filter using the built-in filter options provided by the IDE. These options can be helpful when running your code in a debugging environment. Consult the documentation for Visual Studio Code or the specific extension you're using for information on applying filters.
### Option 4: Modify launch.json
If you are using Visual Studio or a similar IDE that utilizes launch configurations, you can specify the filter in your launch.json configuration file. Edit the configuration for your project to include the filter parameter.
You can run specific examples in the KernelSyntaxExamples project by using test filters (dotnet test --filter).
Type "dotnet test --help" at the command line for more details.
## Configuring Secrets
@@ -177,7 +148,8 @@ Redis__Configuration
# Authentication for the OpenAPI Functions
The Semantic Kernel OpenAPI Function enables developers to take any REST API that follows the OpenAPI specification and import it as a plugin to the Semantic Kernel. However, the Kernel needs to be able to authenticate outgoing requests per the requirements of the target API. This document outlines the authentication model for the OpenAPI plugin.
The Semantic Kernel OpenAPI Function enables developers to take any REST API that follows the OpenAPI specification and import it as a plugin to the Semantic Kernel.
However, the Kernel needs to be able to authenticate outgoing requests per the requirements of the target API. This document outlines the authentication model for the OpenAPI plugin.
## The `AuthenticateRequestAsyncCallback` delegate
@@ -187,7 +159,9 @@ The Semantic Kernel OpenAPI Function enables developers to take any REST API tha
public delegate Task AuthenticateRequestAsyncCallback(HttpRequestMessage request);
```
Developers may optionally provide an implementation of this delegate when importing an OpenAPI plugin to the Kernel. The delegate is then passed through to the `RestApiOperationRunner`, which is responsible for building the HTTP payload and sending the request for each REST API operation. Before the API request is sent, the delegate is executed with the HTTP request message as the parameter, allowing the request message to be updated with any necessary authentication information.
Developers may optionally provide an implementation of this delegate when importing an OpenAPI plugin to the Kernel.
The delegate is then passed through to the `RestApiOperationRunner`, which is responsible for building the HTTP payload and sending the request for each REST API operation.
Before the API request is sent, the delegate is executed with the HTTP request message as the parameter, allowing the request message to be updated with any necessary authentication information.
This pattern was designed to be flexible enough to support a wide variety of authentication frameworks.
@@ -195,7 +169,8 @@ This pattern was designed to be flexible enough to support a wide variety of aut
### BasicAuthenticationProvider
This class implements the HTTP "basic" authentication scheme. The constructor accepts a `Func` which defines how to retrieve the user's credentials. When the `AuthenticateRequestAsync` method is called, it retrieves the credentials, encodes them as a UTF-8 encoded Base64 string, and adds them to the `HttpRequestMessage`'s authorization header.
This class implements the HTTP "basic" authentication scheme. The constructor accepts a `Func` which defines how to retrieve the user's credentials.
When the `AuthenticateRequestAsync` method is called, it retrieves the credentials, encodes them as a UTF-8 encoded Base64 string, and adds them to the `HttpRequestMessage`'s authorization header.
The following code demonstrates how to use this provider:
@@ -212,7 +187,8 @@ var plugin = kernel.ImportOpenApiPluginFromResource(PluginResourceNames.Jira, ne
### BearerAuthenticationProvider
This class implements the HTTP "bearer" authentication scheme. The constructor accepts a `Func` which defines how to retrieve the bearer token. When the `AuthenticateRequestAsync` method is called, it retrieves the token and adds it to the `HttpRequestMessage`'s authorization header.
This class implements the HTTP "bearer" authentication scheme. The constructor accepts a `Func` which defines how to retrieve the bearer token.
When the `AuthenticateRequestAsync` method is called, it retrieves the token and adds it to the `HttpRequestMessage`'s authorization header.
The following code demonstrates how to use this provider:
@@ -226,7 +202,9 @@ var plugin = kernel.ImportOpenApiPluginFromResource(PluginResourceNames.AzureKey
### InteractiveMsalAuthenticationProvider
This class uses the [Microsoft Authentication Library (MSAL)](https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-overview)'s .NET library to authenticate the user and acquire an OAuth token. It follows the interactive [authorization code flow](https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow), requiring the user to sign in with a Microsoft or Azure identity. This is particularly useful for authenticating requests to the Microsoft Graph or Azure APIs.
This class uses the [Microsoft Authentication Library (MSAL)](https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-overview)'s .NET library to authenticate the user and acquire an OAuth token.
It follows the interactive [authorization code flow](https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow), requiring the user to sign in with a Microsoft or Azure identity.
This is particularly useful for authenticating requests to the Microsoft Graph or Azure APIs.
Once the token is acquired, it is added to the HTTP authentication header via the `AuthenticateRequestAsync` method, which is inherited from `BearerAuthenticationProvider`.
@@ -0,0 +1,49 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
namespace RepoUtils;
public static class EnumerableExtensions
{
public static IEnumerable<List<TSource>> ChunkByAggregate<TSource, TAccumulate>(
this IEnumerable<TSource> source,
TAccumulate seed,
Func<TAccumulate, TSource, TAccumulate> aggregator,
Func<TAccumulate, int, bool> predicate)
{
using var enumerator = source.GetEnumerator();
var aggregate = seed;
var index = 0;
var chunk = new List<TSource>();
while (enumerator.MoveNext())
{
var current = enumerator.Current;
aggregate = aggregator(aggregate, current);
if (predicate(aggregate, index++))
{
chunk.Add(current);
}
else
{
if (chunk.Count > 0)
{
yield return chunk;
}
chunk = new List<TSource>() { current };
aggregate = aggregator(seed, current);
index = 1;
}
}
if (chunk.Count > 0)
{
yield return chunk;
}
}
}
@@ -210,17 +210,21 @@ internal abstract class ClientCore
CancellationToken cancellationToken)
{
var result = new List<ReadOnlyMemory<float>>(data.Count);
foreach (string text in data)
{
var options = new EmbeddingsOptions(this.DeploymentOrModelName, new[] { text });
Response<Azure.AI.OpenAI.Embeddings> response = await RunRequestAsync(() => this.Client.GetEmbeddingsAsync(options, cancellationToken)).ConfigureAwait(false);
if (response.Value.Data.Count == 0)
if (data.Count > 0)
{
var response = await RunRequestAsync(() => this.Client.GetEmbeddingsAsync(new(this.DeploymentOrModelName, data), cancellationToken)).ConfigureAwait(false);
var embeddings = response.Value.Data;
if (embeddings.Count != data.Count)
{
throw new KernelException("Text embedding not found");
throw new KernelException($"Expected {data.Count} text embedding(s), but received {embeddings.Count}");
}
result.Add(response.Value.Data[0].Embedding.ToArray());
for (var i = 0; i < embeddings.Count; i++)
{
result.Add(embeddings[i].Embedding);
}
}
return result;
@@ -461,7 +465,7 @@ internal abstract class ClientCore
yield return new OpenAIStreamingChatMessageContent(update, update.ChoiceIndex ?? 0, this.DeploymentOrModelName, metadata);
}
// If we don't have a function to invoke, we're done.
// If we don't have a function to invoke, we're done.
// Note that we don't check the FinishReason and instead check whether there are any tool calls, as the service
// may return a FinishReason of "stop" even if there are tool calls to be made, in particular if a required tool
// is specified.
@@ -106,7 +106,7 @@ public sealed class AzureOpenAITextEmbeddingGenerationServiceTests : IDisposable
// Act & Assert
var exception = await Assert.ThrowsAsync<KernelException>(() => service.GenerateEmbeddingsAsync(["test"]));
Assert.Equal("Text embedding not found", exception.Message);
Assert.Equal("Expected 1 text embedding(s), but received 0", exception.Message);
}
[Fact]
@@ -89,7 +89,7 @@ public sealed class OpenAITextEmbeddingGenerationServiceTests : IDisposable
// Act & Assert
var exception = await Assert.ThrowsAsync<KernelException>(() => service.GenerateEmbeddingsAsync(["test"]));
Assert.Equal("Text embedding not found", exception.Message);
Assert.Equal("Expected 1 text embedding(s), but received 0", exception.Message);
}
[Fact]
@@ -41,7 +41,7 @@ internal static class KernelHelpersUtils
// Thus, we try to get the value from the kernel arguments dictionary.
if (argument is UndefinedBindingResult result)
{
return kernelArguments.TryGetValue(result.Value, out var variable) ? variable : result.Value;
return kernelArguments.TryGetValue(result.Value, out var variable) ? variable : null;
}
return argument;
@@ -136,7 +136,7 @@ internal static class KernelSystemHelpers
return arg switch
{
bool booleanArg => booleanArg,
_ => arg is null
_ => arg is not null
};
});
});
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.ComponentModel;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel;
@@ -30,7 +31,7 @@ public sealed class HandlebarsPlannerTests : IDisposable
[Theory]
[InlineData(true, "Write a joke and send it in an e-mail to Kai.", "SendEmail", "test")]
public async Task CreatePlanFunctionFlowAsync(bool useChatModel, string prompt, string expectedFunction, string expectedPlugin)
public async Task CreatePlanFunctionFlowAsync(bool useChatModel, string goal, string expectedFunction, string expectedPlugin)
{
// Arrange
bool useEmbeddings = false;
@@ -39,7 +40,7 @@ public sealed class HandlebarsPlannerTests : IDisposable
TestHelpers.ImportSamplePlugins(kernel, "FunPlugin");
// Act
var plan = await new HandlebarsPlanner().CreatePlanAsync(kernel, prompt);
var plan = await new HandlebarsPlanner().CreatePlanAsync(kernel, goal);
// Assert expected function
Assert.Contains(
@@ -51,14 +52,14 @@ public sealed class HandlebarsPlannerTests : IDisposable
[RetryTheory]
[InlineData("Write a novel about software development that is 3 chapters long.", "NovelChapter", "WriterPlugin")]
public async Task CreatePlanWithDefaultsAsync(string prompt, string expectedFunction, string expectedPlugin)
public async Task CreatePlanWithDefaultsAsync(string goal, string expectedFunction, string expectedPlugin)
{
// Arrange
Kernel kernel = this.InitializeKernel();
TestHelpers.ImportSamplePlugins(kernel, "WriterPlugin", "MiscPlugin");
// Act
var plan = await new HandlebarsPlanner().CreatePlanAsync(kernel, prompt);
var plan = await new HandlebarsPlanner().CreatePlanAsync(kernel, goal);
// Assert
Assert.Contains(
@@ -68,6 +69,51 @@ public sealed class HandlebarsPlannerTests : IDisposable
);
}
[Theory]
[InlineData(true, "List each property of the default Qux object.", "## Complex types", @"### Qux:
{
""type"": ""Object"",
""properties"": {
""Bar"": {
""type"": ""String"",
},
""Baz"": {
""type"": ""Int32"",
},
}
}", "GetDefaultQux", "Foo")]
public async Task CreatePlanWithComplexTypesDefinitionsAsync(bool useChatModel, string goal, string expectedSectionHeader, string expectedTypeHeader, string expectedFunction, string expectedPlugin)
{
// Arrange
bool useEmbeddings = false;
var kernel = this.InitializeKernel(useEmbeddings, useChatModel);
kernel.ImportPluginFromObject(new Foo());
// Act
var plan = await new HandlebarsPlanner().CreatePlanAsync(kernel, goal);
// Assert expected section header for Complex Types in prompt
Assert.Contains(
expectedSectionHeader,
plan.Prompt,
StringComparison.CurrentCulture
);
// Assert expected complex parameter type in prompt
Assert.Contains(
expectedTypeHeader,
plan.Prompt,
StringComparison.CurrentCulture
);
// Assert expected function in plan
Assert.Contains(
$"{expectedPlugin}-{expectedFunction}",
plan.ToString(),
StringComparison.CurrentCulture
);
}
private Kernel InitializeKernel(bool useEmbeddings = false, bool useChatModel = true)
{
AzureOpenAIConfiguration? azureOpenAIConfiguration = this._configuration.GetSection("AzureOpenAI").Get<AzureOpenAIConfiguration>();
@@ -110,6 +156,24 @@ public sealed class HandlebarsPlannerTests : IDisposable
private readonly RedirectOutput _testOutputHelper;
private readonly IConfigurationRoot _configuration;
private sealed class Foo
{
public sealed class Qux
{
public string Bar { get; set; } = string.Empty;
public int Baz { get; set; }
public Qux(string bar, int baz)
{
this.Bar = bar;
this.Baz = baz;
}
}
[KernelFunction, Description("Returns default Qux object.")]
public Qux GetDefaultQux() => new("bar", 42);
}
public void Dispose()
{
this._testOutputHelper.Dispose();
@@ -18,7 +18,7 @@
},
"AzureOpenAIEmbeddings": {
"ServiceId": "azure-text-embedding-ada-002",
"DeploymentName": "text-embedding-ada-002",
"DeploymentName": "ada-002",
"Endpoint": "",
"ApiKey": ""
},
@@ -72,7 +72,7 @@ What's the sum of 5+10+15?
{{#message role="user"}}
The following helpers are available to you:
## Out-of-the-box helpers
## Built-in block helpers
- `\{{#if}}\{{/if}}`
- `\{{#unless}}\{{/unless}}`{{#if allowLoops}}
- `\{{#each}}\{{/each}}`{{/if}}
@@ -26,7 +26,7 @@ internal static class XmlPromptParser
// exception is thrown. Try to avoid it in the common case where the prompt is obviously not XML.
// To be valid XML, at a minimum:
// - the string would need to be non-null
// - it would need to contain a the start of a tag
// - it would need to contain the start of a tag
// - it would need to contain a closing tag, which could include either </ or />
int startPos;
if (prompt is null ||
+4
View File
@@ -18,3 +18,7 @@ AZCOSMOS_API = "" // should be mongo-vcore for now, as CosmosDB only supports ve
AZCOSMOS_CONNSTR = ""
AZCOSMOS_DATABASE_NAME = ""
AZCOSMOS_CONTAINER_NAME = ""
ASTRADB_APP_TOKEN="" // Starts with AstraCS:
ASTRADB_ID=""
ASTRADB_REGION=""
ASTRADB_KEYSPACE=""
+10 -4
View File
@@ -25,6 +25,7 @@ AZURE_OPENAI_API_KEY=""
# Running a prompt
```python
import asyncio
import semantic_kernel as sk
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion, AzureChatCompletion
@@ -52,7 +53,12 @@ does not conflict with the First or Second Law.
Give me the TLDR in exactly 5 words.""")
# Run your prompt
print(prompt()) # => Robots must not harm humans.
# Note: functions are run asynchronously
async def main():
print(await prompt()) # => Robots must not harm humans.
if __name__ == "__main__":
asyncio.run(main())
```
# **Semantic functions** are Prompts with input parameters
@@ -62,19 +68,19 @@ print(prompt()) # => Robots must not harm humans.
summarize = kernel.create_semantic_function("{{$input}}\n\nOne line TLDR with the fewest words.")
# Summarize the laws of thermodynamics
print(summarize("""
print(await summarize("""
1st Law of Thermodynamics - Energy cannot be created or destroyed.
2nd Law of Thermodynamics - For a spontaneous process, the entropy of the universe increases.
3rd Law of Thermodynamics - A perfect crystal at zero Kelvin has zero entropy."""))
# Summarize the laws of motion
print(summarize("""
print(await summarize("""
1. An object at rest remains at rest, and an object in motion remains in motion at constant speed and in a straight line unless acted on by an unbalanced force.
2. The acceleration of an object depends on the mass of the object and the amount of force applied.
3. Whenever one object exerts a force on another object, the second object exerts an equal and opposite on the first."""))
# Summarize the law of universal gravitation
print(summarize("""
print(await summarize("""
Every point mass attracts every single other point mass by a force acting along the line intersecting both points.
The force is proportional to the product of the two masses and inversely proportional to the square of the distance between them."""))
+2 -2
View File
@@ -16,7 +16,7 @@
"metadata": {},
"outputs": [],
"source": [
"!python -m pip install semantic-kernel==0.4.7.dev0"
"!python -m pip install semantic-kernel==0.5.0.dev0"
]
},
{
@@ -116,7 +116,7 @@
"plugin = kernel.import_semantic_plugin_from_directory(\"../../samples/plugins\", \"FunPlugin\")\n",
"joke_function = plugin[\"Joke\"]\n",
"\n",
"print(joke_function(\"time travel to dinosaur age\"))"
"print(await joke_function(\"time travel to dinosaur age\"))"
]
}
],
@@ -25,7 +25,7 @@
"metadata": {},
"outputs": [],
"source": [
"!python -m pip install semantic-kernel==0.4.7.dev0"
"!python -m pip install semantic-kernel==0.5.0.dev0"
]
},
{
@@ -89,7 +89,7 @@
"metadata": {},
"outputs": [],
"source": [
"!python -m pip install semantic-kernel==0.4.7.dev0"
"!python -m pip install semantic-kernel==0.5.0.dev0"
]
},
{
@@ -162,7 +162,7 @@
"metadata": {},
"outputs": [],
"source": [
"result = await jokeFunction.invoke_async(\"time travel to dinosaur age\")\n",
"result = await jokeFunction.invoke(\"travel to dinosaur age\")\n",
"print(result)"
]
},
@@ -192,7 +192,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
"version": "3.11.7"
}
},
"nbformat": 4,
@@ -55,7 +55,7 @@
"metadata": {},
"outputs": [],
"source": [
"!python -m pip install semantic-kernel==0.4.7.dev0"
"!python -m pip install semantic-kernel==0.5.0.dev0"
]
},
{
@@ -158,8 +158,7 @@
"metadata": {},
"outputs": [],
"source": [
"# If needed, async is available too: summary = await summarize.invoke_async(input_text)\n",
"summary = summarize(input_text)\n",
"summary = await summarize(input_text)\n",
"\n",
"print(summary)"
]
@@ -249,7 +248,7 @@
"\n",
"tldr_function = kernel.create_semantic_function(prompt_template=sk_prompt, max_tokens=200, temperature=0, top_p=0.5)\n",
"\n",
"summary = tldr_function(text)\n",
"summary = await tldr_function(text)\n",
"\n",
"print(f\"Output: {summary}\") # Output: Robots must not harm humans."
]
@@ -271,7 +270,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
"version": "3.11.7"
}
},
"nbformat": 4,
@@ -26,7 +26,7 @@
"metadata": {},
"outputs": [],
"source": [
"!python -m pip install semantic-kernel==0.4.7.dev0"
"!python -m pip install semantic-kernel==0.5.0.dev0"
]
},
{
@@ -148,7 +148,7 @@
"outputs": [],
"source": [
"context[\"user_input\"] = \"Hi, I'm looking for book suggestions\"\n",
"bot_answer = await chat_function.invoke_async(context=context)\n",
"bot_answer = await chat_function.invoke(context=context)\n",
"print(bot_answer)"
]
},
@@ -194,7 +194,7 @@
" context[\"user_input\"] = input_text\n",
"\n",
" # Process the user message and get an answer\n",
" answer = await chat_function.invoke_async(context=context)\n",
" answer = await chat_function.invoke(context=context)\n",
"\n",
" # Show the response\n",
" print(f\"ChatBot: {answer}\")\n",
@@ -279,7 +279,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
"version": "3.11.7"
}
},
"nbformat": 4,
+5 -5
View File
@@ -23,7 +23,7 @@
"metadata": {},
"outputs": [],
"source": [
"!python -m pip install semantic-kernel==0.4.7.dev0"
"!python -m pip install semantic-kernel==0.5.0.dev0"
]
},
{
@@ -359,7 +359,7 @@
"metadata": {},
"outputs": [],
"source": [
"result = await sequential_plan.invoke_async()"
"result = await sequential_plan.invoke()"
]
},
{
@@ -455,7 +455,7 @@
"metadata": {},
"outputs": [],
"source": [
"result = await plan.invoke_async()"
"result = await plan.invoke()"
]
},
{
@@ -616,7 +616,7 @@
"metadata": {},
"outputs": [],
"source": [
"result = await plan.invoke_async()"
"result = await plan.invoke()"
]
},
{
@@ -669,7 +669,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.13"
"version": "3.10.12"
}
},
"nbformat": 4,
@@ -28,7 +28,7 @@
"metadata": {},
"outputs": [],
"source": [
"!python -m pip install semantic-kernel==0.4.7.dev0"
"!python -m pip install semantic-kernel==0.5.0.dev0"
]
},
{
@@ -20,7 +20,7 @@
"metadata": {},
"outputs": [],
"source": [
"!python -m pip install semantic-kernel==0.4.7.dev0\n",
"!python -m pip install semantic-kernel==0.5.0.dev0\n",
"\n",
"# Note that additional dependencies are required for the Hugging Face connectors:\n",
"!python -m pip install torch==2.0.0\n",
@@ -46,7 +46,7 @@
"metadata": {},
"outputs": [],
"source": [
"!python -m pip install semantic-kernel==0.4.7.dev0"
"!python -m pip install semantic-kernel==0.5.0.dev0"
]
},
{
@@ -169,7 +169,7 @@
" top_p=0.5,\n",
")\n",
"\n",
"generate_number_plugin = kernel.import_plugin(GenerateNumberPlugin())"
"generate_number_plugin = kernel.import_plugin(GenerateNumberPlugin(), \"GenerateNumberPlugin\")"
]
},
{
@@ -181,7 +181,7 @@
"source": [
"# Run the number generator\n",
"generate_number_three_or_higher = generate_number_plugin[\"GenerateNumberThreeOrHigher\"]\n",
"number_result = generate_number_three_or_higher(6)\n",
"number_result = await generate_number_three_or_higher(6)\n",
"print(number_result)"
]
},
@@ -192,7 +192,7 @@
"metadata": {},
"outputs": [],
"source": [
"story = await corgi_story.invoke_async(input=number_result.result)"
"story = await corgi_story.invoke(input=number_result.result)"
]
},
{
@@ -309,7 +309,7 @@
"metadata": {},
"outputs": [],
"source": [
"generate_number_plugin = kernel.import_plugin(GenerateNumberPlugin())\n",
"generate_number_plugin = kernel.import_plugin(GenerateNumberPlugin(), \"GenerateNumberPlugin\")\n",
"generate_number = generate_number_plugin[\"GenerateNumber\"]"
]
},
@@ -385,7 +385,8 @@
"metadata": {},
"outputs": [],
"source": [
"context_variables[\"paragraph_count\"] = generate_number.invoke(variables=context_variables).result"
"num = await generate_number.invoke(variables=context_variables)\n",
"context_variables[\"paragraph_count\"] = num.result"
]
},
{
@@ -396,7 +397,7 @@
"outputs": [],
"source": [
"# Pass the output to the semantic story function\n",
"story = await corgi_story.invoke_async(variables=context_variables)"
"story = await corgi_story.invoke(variables=context_variables)"
]
},
{
@@ -495,24 +496,6 @@
"\"\"\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "73aca517",
"metadata": {},
"outputs": [],
"source": [
"corgi_story = kernel.create_semantic_function(\n",
" prompt_template=sk_prompt,\n",
" function_name=\"CorgiStory\",\n",
" plugin_name=\"CorgiPlugin\",\n",
" description=\"Write a short story about two Corgis on an adventure\",\n",
" max_tokens=500,\n",
" temperature=0.5,\n",
" top_p=0.5,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
@@ -521,7 +504,8 @@
"outputs": [],
"source": [
"context_variables = sk.ContextVariables(variables={\"min\": \"1\", \"max\": \"5\", \"language\": \"Spanish\"})\n",
"context_variables[\"paragraph_count\"] = generate_number.invoke(variables=context_variables).result"
"num = await generate_number.invoke(variables=context_variables)\n",
"context_variables[\"paragraph_count\"] = str(num.result)"
]
},
{
@@ -532,7 +516,7 @@
"outputs": [],
"source": [
"# Pass the output to the semantic story function\n",
"story = await corgi_story.invoke_async(variables=context_variables)"
"story = await corgi_story.invoke(variables=context_variables)"
]
},
{
@@ -82,7 +82,7 @@
"metadata": {},
"outputs": [],
"source": [
"!python -m pip install semantic-kernel==0.4.7.dev0"
"!python -m pip install semantic-kernel==0.5.0.dev0"
]
},
{
@@ -248,7 +248,7 @@
"metadata": {},
"outputs": [],
"source": [
"extraction_result = entity_extraction(summary_text, context=context)\n",
"extraction_result = await entity_extraction(summary_text, context=context)\n",
"\n",
"print(extraction_result.result)"
]
@@ -296,7 +296,7 @@
"metadata": {},
"outputs": [],
"source": [
"grounding_result = reference_check(extraction_result.result, context=context)\n",
"grounding_result = await reference_check(extraction_result.result, context=context)\n",
"\n",
"print(grounding_result.result)"
]
@@ -336,7 +336,7 @@
"metadata": {},
"outputs": [],
"source": [
"excision_result = entity_excision(summary_text, context=context)\n",
"excision_result = await entity_excision(summary_text, context=context)\n",
"\n",
"print(excision_result.result)"
]
@@ -358,7 +358,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
"version": "3.11.7"
}
},
"nbformat": 4,
@@ -1,404 +1,404 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"id": "68e1c158",
"metadata": {},
"source": [
"# Multiple Results\n"
]
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"id": "68e1c158",
"metadata": {},
"source": [
"# Multiple Results\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "fb81bacd",
"metadata": {},
"source": [
"In this notebook we show how you can in a single request, have the LLM model return multiple results per prompt. This is useful for running experiments where you want to evaluate the robustness of your prompt and the parameters of your config against a particular large language model.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a77bdf89",
"metadata": {},
"outputs": [],
"source": [
"!python -m pip install semantic-kernel==0.5.0.dev0"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3f4bfee4",
"metadata": {},
"outputs": [],
"source": [
"from services import Service\n",
"\n",
"# Select a service to use for this notebook (available services: OpenAI, AzureOpenAI, HuggingFace)\n",
"selectedService = Service.AzureOpenAI"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "508ad44f",
"metadata": {},
"outputs": [],
"source": [
"import semantic_kernel as sk\n",
"\n",
"if selectedService == Service.OpenAI or selectedService == Service.AzureOpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.open_ai_prompt_execution_settings import (\n",
" OpenAITextPromptExecutionSettings,\n",
" OpenAIChatPromptExecutionSettings,\n",
" )\n",
" from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.azure_chat_prompt_execution_settings import (\n",
" AzureChatPromptExecutionSettings,\n",
" )\n",
" from semantic_kernel.connectors.ai.open_ai import (\n",
" AzureTextCompletion,\n",
" AzureChatCompletion,\n",
" OpenAITextCompletion,\n",
" OpenAIChatCompletion,\n",
" )\n",
"if selectedService == Service.HuggingFace:\n",
" from semantic_kernel.connectors.ai.hugging_face import HuggingFaceTextCompletion"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "d8ddffc1",
"metadata": {},
"source": [
"First, we will set up the text and chat services we will be submitting prompts to.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8f8dcbc6",
"metadata": {},
"outputs": [],
"source": [
"kernel = sk.Kernel()\n",
"\n",
"# Configure Azure LLM service\n",
"if selectedService == Service.AzureOpenAI:\n",
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" azure_text_service = AzureTextCompletion(\n",
" deployment_name=\"gpt-35-turbo-instruct\", endpoint=endpoint, api_key=api_key\n",
" ) # set the deployment name to the value of your text model (e.g. gpt-35-turbo-instruct or text-davinci-003)\n",
" azure_chat_service = AzureChatCompletion(\n",
" deployment_name=\"gpt-35-turbo\", endpoint=endpoint, api_key=api_key\n",
" ) # set the deployment name to the value of your chat model\n",
"\n",
"# Configure OpenAI service\n",
"if selectedService == Service.OpenAI:\n",
" api_key, org_id = sk.openai_settings_from_dot_env()\n",
" oai_text_service = OpenAITextCompletion(ai_model_id=\"gpt-3.5-turbo-instruct\", api_key=api_key, org_id=org_id)\n",
" oai_chat_service = OpenAIChatCompletion(ai_model_id=\"gpt-3.5-turbo\", api_key=api_key, org_id=org_id)\n",
"\n",
"# Configure Hugging Face service\n",
"if selectedService == Service.HuggingFace:\n",
" hf_text_service = HuggingFaceTextCompletion(ai_model_id=\"distilgpt2\", task=\"text-generation\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "50561d82",
"metadata": {},
"source": [
"Next, we'll set up the completion request settings for text completion services.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "628c843e",
"metadata": {},
"outputs": [],
"source": [
"oai_text_prompt_execution_settings = OpenAITextPromptExecutionSettings(\n",
" extension_data={\n",
" \"max_tokens\": 80,\n",
" \"temperature\": 0.7,\n",
" \"top_p\": 1,\n",
" \"frequency_penalty\": 0.5,\n",
" \"presence_penalty\": 0.5,\n",
" \"number_of_responses\": 3,\n",
" }\n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "857a9c89",
"metadata": {},
"source": [
"## Multiple Open AI Text Completions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e2979db8",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.OpenAI:\n",
" prompt = \"what is the purpose of a rubber duck?\"\n",
" results = await oai_text_service.complete(prompt=prompt, settings=oai_text_prompt_execution_settings)\n",
" i = 1\n",
" for result in results:\n",
" print(f\"Result {i}: {result}\")\n",
" i += 1"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "4288d09f",
"metadata": {},
"source": [
"## Multiple Azure Open AI Text Completions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5319f14d",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.AzureOpenAI:\n",
" prompt = \"provide me a list of possible meanings for the acronym 'ORLD'\"\n",
" results = await azure_text_service.complete(prompt=prompt, settings=oai_text_prompt_execution_settings)\n",
" i = 1\n",
" for result in results:\n",
" print(f\"Result {i}: {result}\")\n",
" i += 1"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "eb548f9c",
"metadata": {},
"source": [
"## Multiple Hugging Face Text Completions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4a148709",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.HuggingFace:\n",
" from semantic_kernel.connectors.ai.hugging_face.hf_prompt_execution_settings import (\n",
" HuggingFacePromptExecutionSettings,\n",
" )\n",
"\n",
" hf_prompt_execution_settings = HuggingFacePromptExecutionSettings(\n",
" extension_data={\"max_new_tokens\": 80, \"temperature\": 0.7, \"top_p\": 1}\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9525e4f3",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.HuggingFace:\n",
" prompt = \"The purpose of a rubber duck is\"\n",
" results = await hf_text_service.complete(prompt=prompt, prompt_execution_settings=hf_prompt_execution_settings)\n",
" print(\"\".join(results))"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "da632e12",
"metadata": {},
"source": [
"Here, we're setting up the settings for Chat completions.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e5f11e46",
"metadata": {},
"outputs": [],
"source": [
"oai_chat_prompt_execution_settings = OpenAIChatPromptExecutionSettings(\n",
" max_tokens=80,\n",
" temperature=0.7,\n",
" top_p=1,\n",
" frequency_penalty=0.5,\n",
" presence_penalty=0.5,\n",
" number_of_responses=3,\n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "d6bf238e",
"metadata": {},
"source": [
"## Multiple OpenAI Chat Completions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "dabc6a4c",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.OpenAI:\n",
" role = \"user\"\n",
" content = (\n",
" \"It's a beautiful day outside, birds are singing, flowers are blooming. On days like these, kids like you...\"\n",
" )\n",
" message = {\"role\": role, \"content\": content}\n",
" results = await oai_chat_service.complete_chat(messages=[message], settings=oai_chat_prompt_execution_settings)\n",
" i = 0\n",
" for result in results:\n",
" print(f\"Result {i}: {result[0]}\")\n",
" i += 1"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "cdb8f740",
"metadata": {},
"source": [
"## Multiple Azure OpenAI Chat Completions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "66ba4767",
"metadata": {},
"outputs": [],
"source": [
"az_oai_prompt_execution_settings = AzureChatPromptExecutionSettings(\n",
" max_tokens=80,\n",
" temperature=0.7,\n",
" top_p=1,\n",
" frequency_penalty=0.5,\n",
" presence_penalty=0.5,\n",
" number_of_responses=3,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b74a64a9",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.AzureOpenAI:\n",
" role = \"user\"\n",
" content = \"Tomorow is going to be a great day, I can feel it. I'm going to wake up early, go for a run, and then...\"\n",
" message = {\"role\": role, \"content\": content}\n",
" results = await azure_chat_service.complete_chat(messages=[message], settings=az_oai_prompt_execution_settings)\n",
" i = 0\n",
" for result in results:\n",
" print(f\"Result {i}: {result[0]}\")\n",
" i += 1"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "98c8191d",
"metadata": {},
"source": [
"## Streaming Multiple Results\n",
"\n",
"Here is an example pattern if you want to stream your multiple results. Note that this is not supported for Hugging Face text completions at this time.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "26a37702",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.OpenAI:\n",
" import os\n",
" from IPython.display import clear_output\n",
" import time\n",
"\n",
" # Determine the clear command based on OS\n",
" clear_command = \"cls\" if os.name == \"nt\" else \"clear\"\n",
"\n",
" prompt = \"what is the purpose of a rubber duck?\"\n",
" stream = oai_text_service.complete_stream(prompt=prompt, settings=oai_text_prompt_execution_settings)\n",
" number_of_responses = oai_text_prompt_execution_settings.number_of_responses\n",
" texts = [\"\"] * number_of_responses\n",
"\n",
" last_clear_time = time.time()\n",
" clear_interval = 0.5 # seconds\n",
"\n",
" # Note: there are some quirks with displaying the output, which sometimes flashes and disappears.\n",
" # This could be influenced by a few factors specific to Jupyter notebooks and asynchronous processing.\n",
" # The following code attempts to buffer the results to avoid the output flashing on/off the screen.\n",
"\n",
" async for results in stream:\n",
" current_time = time.time()\n",
"\n",
" # Update texts with new results\n",
" for idx, result in enumerate(results):\n",
" if idx < number_of_responses:\n",
" texts[idx] += result\n",
"\n",
" # Clear and display output at intervals\n",
" if current_time - last_clear_time > clear_interval:\n",
" clear_output(wait=True)\n",
" for idx, text in enumerate(texts):\n",
" print(f\"Result {idx + 1}: {text}\")\n",
" last_clear_time = current_time\n",
"\n",
" print(\"----------------------------------------\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
}
},
{
"attachments": {},
"cell_type": "markdown",
"id": "fb81bacd",
"metadata": {},
"source": [
"In this notebook we show how you can in a single request, have the LLM model return multiple results per prompt. This is useful for running experiments where you want to evaluate the robustness of your prompt and the parameters of your config against a particular large language model.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a77bdf89",
"metadata": {},
"outputs": [],
"source": [
"!python -m pip install semantic-kernel==0.4.7.dev0"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3f4bfee4",
"metadata": {},
"outputs": [],
"source": [
"from services import Service\n",
"\n",
"# Select a service to use for this notebook (available services: OpenAI, AzureOpenAI, HuggingFace)\n",
"selectedService = Service.AzureOpenAI"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "508ad44f",
"metadata": {},
"outputs": [],
"source": [
"import semantic_kernel as sk\n",
"\n",
"if selectedService == Service.OpenAI or selectedService == Service.AzureOpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai.request_settings.open_ai_request_settings import (\n",
" OpenAITextRequestSettings,\n",
" OpenAIChatRequestSettings,\n",
" )\n",
" from semantic_kernel.connectors.ai.open_ai.request_settings.azure_chat_request_settings import (\n",
" AzureChatRequestSettings,\n",
" )\n",
" from semantic_kernel.connectors.ai.open_ai import (\n",
" AzureTextCompletion,\n",
" AzureChatCompletion,\n",
" OpenAITextCompletion,\n",
" OpenAIChatCompletion,\n",
" )\n",
"if selectedService == Service.HuggingFace:\n",
" from semantic_kernel.connectors.ai.hugging_face import HuggingFaceTextCompletion"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "d8ddffc1",
"metadata": {},
"source": [
"First, we will set up the text and chat services we will be submitting prompts to.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8f8dcbc6",
"metadata": {},
"outputs": [],
"source": [
"kernel = sk.Kernel()\n",
"\n",
"# Configure Azure LLM service\n",
"if selectedService == Service.AzureOpenAI:\n",
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" azure_text_service = AzureTextCompletion(\n",
" deployment_name=\"gpt-35-turbo-instruct\", endpoint=endpoint, api_key=api_key\n",
" ) # set the deployment name to the value of your text model (e.g. gpt-35-turbo-instruct or text-davinci-003)\n",
" azure_chat_service = AzureChatCompletion(\n",
" deployment_name=\"gpt-35-turbo\", endpoint=endpoint, api_key=api_key\n",
" ) # set the deployment name to the value of your chat model\n",
"\n",
"# Configure OpenAI service\n",
"if selectedService == Service.OpenAI:\n",
" api_key, org_id = sk.openai_settings_from_dot_env()\n",
" oai_text_service = OpenAITextCompletion(ai_model_id=\"gpt-3.5-turbo-instruct\", api_key=api_key, org_id=org_id)\n",
" oai_chat_service = OpenAIChatCompletion(ai_model_id=\"gpt-3.5-turbo\", api_key=api_key, org_id=org_id)\n",
"\n",
"# Configure Hugging Face service\n",
"if selectedService == Service.HuggingFace:\n",
" hf_text_service = HuggingFaceTextCompletion(ai_model_id=\"distilgpt2\", task=\"text-generation\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "50561d82",
"metadata": {},
"source": [
"Next, we'll set up the completion request settings for text completion services.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "628c843e",
"metadata": {},
"outputs": [],
"source": [
"oai_text_request_settings = OpenAITextRequestSettings(\n",
" extension_data={\n",
" \"max_tokens\": 80,\n",
" \"temperature\": 0.7,\n",
" \"top_p\": 1,\n",
" \"frequency_penalty\": 0.5,\n",
" \"presence_penalty\": 0.5,\n",
" \"number_of_responses\": 3,\n",
" }\n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "857a9c89",
"metadata": {},
"source": [
"## Multiple Open AI Text Completions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e2979db8",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.OpenAI:\n",
" prompt = \"what is the purpose of a rubber duck?\"\n",
" results = await oai_text_service.complete(prompt=prompt, settings=oai_text_request_settings)\n",
" i = 1\n",
" for result in results:\n",
" print(f\"Result {i}: {result}\")\n",
" i += 1"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "4288d09f",
"metadata": {},
"source": [
"## Multiple Azure Open AI Text Completions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5319f14d",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.AzureOpenAI:\n",
" prompt = \"provide me a list of possible meanings for the acronym 'ORLD'\"\n",
" results = await azure_text_service.complete(prompt=prompt, settings=oai_text_request_settings)\n",
" i = 1\n",
" for result in results:\n",
" print(f\"Result {i}: {result}\")\n",
" i += 1"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "eb548f9c",
"metadata": {},
"source": [
"## Multiple Hugging Face Text Completions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4a148709",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.HuggingFace:\n",
" from semantic_kernel.connectors.ai.hugging_face.hf_request_settings import (\n",
" HuggingFaceRequestSettings,\n",
" )\n",
"\n",
" hf_request_settings = HuggingFaceRequestSettings(\n",
" extension_data={\"max_new_tokens\": 80, \"temperature\": 0.7, \"top_p\": 1}\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9525e4f3",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.HuggingFace:\n",
" prompt = \"The purpose of a rubber duck is\"\n",
" results = await hf_text_service.complete(prompt=prompt, request_settings=hf_request_settings)\n",
" print(\"\".join(results))"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "da632e12",
"metadata": {},
"source": [
"Here, we're setting up the settings for Chat completions.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e5f11e46",
"metadata": {},
"outputs": [],
"source": [
"oai_chat_request_settings = OpenAIChatRequestSettings(\n",
" max_tokens=80,\n",
" temperature=0.7,\n",
" top_p=1,\n",
" frequency_penalty=0.5,\n",
" presence_penalty=0.5,\n",
" number_of_responses=3,\n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "d6bf238e",
"metadata": {},
"source": [
"## Multiple OpenAI Chat Completions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "dabc6a4c",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.OpenAI:\n",
" role = \"user\"\n",
" content = (\n",
" \"It's a beautiful day outside, birds are singing, flowers are blooming. On days like these, kids like you...\"\n",
" )\n",
" message = {\"role\": role, \"content\": content}\n",
" results = await oai_chat_service.complete_chat(messages=[message], settings=oai_chat_request_settings)\n",
" i = 0\n",
" for result in results:\n",
" print(f\"Result {i}: {result[0]}\")\n",
" i += 1"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "cdb8f740",
"metadata": {},
"source": [
"## Multiple Azure OpenAI Chat Completions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "66ba4767",
"metadata": {},
"outputs": [],
"source": [
"az_oai_request_settings = AzureChatRequestSettings(\n",
" max_tokens=80,\n",
" temperature=0.7,\n",
" top_p=1,\n",
" frequency_penalty=0.5,\n",
" presence_penalty=0.5,\n",
" number_of_responses=3,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b74a64a9",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.AzureOpenAI:\n",
" role = \"user\"\n",
" content = \"Tomorow is going to be a great day, I can feel it. I'm going to wake up early, go for a run, and then...\"\n",
" message = {\"role\": role, \"content\": content}\n",
" results = await azure_chat_service.complete_chat(messages=[message], settings=az_oai_request_settings)\n",
" i = 0\n",
" for result in results:\n",
" print(f\"Result {i}: {result[0]}\")\n",
" i += 1"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "98c8191d",
"metadata": {},
"source": [
"## Streaming Multiple Results\n",
"\n",
"Here is an example pattern if you want to stream your multiple results. Note that this is not supported for Hugging Face text completions at this time.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "26a37702",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.OpenAI:\n",
" import os\n",
" from IPython.display import clear_output\n",
" import time\n",
"\n",
" # Determine the clear command based on OS\n",
" clear_command = \"cls\" if os.name == \"nt\" else \"clear\"\n",
"\n",
" prompt = \"what is the purpose of a rubber duck?\"\n",
" stream = oai_text_service.complete_stream(prompt=prompt, settings=oai_text_request_settings)\n",
" number_of_responses = oai_text_request_settings.number_of_responses\n",
" texts = [\"\"] * number_of_responses\n",
"\n",
" last_clear_time = time.time()\n",
" clear_interval = 0.5 # seconds\n",
"\n",
" # Note: there are some quirks with displaying the output, which sometimes flashes and disappears.\n",
" # This could be influenced by a few factors specific to Jupyter notebooks and asynchronous processing.\n",
" # The following code attempts to buffer the results to avoid the output flashing on/off the screen.\n",
"\n",
" async for results in stream:\n",
" current_time = time.time()\n",
"\n",
" # Update texts with new results\n",
" for idx, result in enumerate(results):\n",
" if idx < number_of_responses:\n",
" texts[idx] += result\n",
"\n",
" # Clear and display output at intervals\n",
" if current_time - last_clear_time > clear_interval:\n",
" clear_output(wait=True)\n",
" for idx, text in enumerate(texts):\n",
" print(f\"Result {idx + 1}: {text}\")\n",
" last_clear_time = current_time\n",
"\n",
" print(\"----------------------------------------\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 5
"nbformat": 4,
"nbformat_minor": 5
}
+17 -17
View File
@@ -18,7 +18,7 @@
"metadata": {},
"outputs": [],
"source": [
"!python -m pip install semantic-kernel==0.4.7.dev0"
"!python -m pip install semantic-kernel==0.5.0.dev0"
]
},
{
@@ -43,12 +43,12 @@
"import semantic_kernel as sk\n",
"\n",
"if selectedService == Service.OpenAI or selectedService == Service.AzureOpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai.request_settings.open_ai_request_settings import (\n",
" OpenAITextRequestSettings,\n",
" OpenAIChatRequestSettings,\n",
" from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.open_ai_prompt_execution_settings import (\n",
" OpenAITextPromptExecutionSettings,\n",
" OpenAIChatPromptExecutionSettings,\n",
" )\n",
" from semantic_kernel.connectors.ai.open_ai.request_settings.azure_chat_request_settings import (\n",
" AzureChatRequestSettings,\n",
" from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.azure_chat_prompt_execution_settings import (\n",
" AzureChatPromptExecutionSettings,\n",
" )\n",
" from semantic_kernel.connectors.ai.open_ai import (\n",
" AzureTextCompletion,\n",
@@ -115,7 +115,7 @@
"metadata": {},
"outputs": [],
"source": [
"oai_request_settings = OpenAITextRequestSettings(\n",
"oai_prompt_execution_settings = OpenAITextPromptExecutionSettings(\n",
" max_tokens=150,\n",
" temperature=0.7,\n",
" top_p=1,\n",
@@ -142,7 +142,7 @@
"source": [
"if selectedService == Service.OpenAI:\n",
" prompt = \"what is the purpose of a rubber duck?\"\n",
" stream = oai_text_service.complete_stream(prompt=prompt, settings=oai_request_settings)\n",
" stream = oai_text_service.complete_stream(prompt=prompt, settings=oai_prompt_execution_settings)\n",
" async for text in stream:\n",
" print(text, end=\"\") # end = \"\" to avoid newlines"
]
@@ -165,7 +165,7 @@
"source": [
"if selectedService == Service.AzureOpenAI:\n",
" prompt = \"provide me a list of possible meanings for the acronym 'ORLD'\"\n",
" stream = azure_text_service.complete_stream(prompt=prompt, settings=oai_request_settings)\n",
" stream = azure_text_service.complete_stream(prompt=prompt, settings=oai_prompt_execution_settings)\n",
" async for text in stream:\n",
" print(text, end=\"\") # end = \"\" to avoid newlines"
]
@@ -187,11 +187,11 @@
"outputs": [],
"source": [
"if selectedService == Service.HuggingFace:\n",
" from semantic_kernel.connectors.ai.hugging_face.hf_request_settings import (\n",
" HuggingFaceRequestSettings,\n",
" from semantic_kernel.connectors.ai.hugging_face.hf_prompt_execution_settings import (\n",
" HuggingFacePromptExecutionSettings,\n",
" )\n",
"\n",
" hf_request_settings = HuggingFaceRequestSettings(\n",
" hf_prompt_execution_settings = HuggingFacePromptExecutionSettings(\n",
" extension_data={\n",
" \"max_new_tokens\": 80,\n",
" \"top_p\": 1,\n",
@@ -210,7 +210,7 @@
"source": [
"if selectedService == Service.HuggingFace:\n",
" prompt = \"The purpose of a rubber duck is\"\n",
" stream = hf_text_service.complete_stream(prompt=prompt, request_settings=hf_request_settings)\n",
" stream = hf_text_service.complete_stream(prompt=prompt, prompt_execution_settings=hf_prompt_execution_settings)\n",
" async for text in stream:\n",
" print(text, end=\"\") # end = \"\" to avoid newlines"
]
@@ -231,7 +231,7 @@
"metadata": {},
"outputs": [],
"source": [
"oai_chat_request_settings = OpenAIChatRequestSettings(\n",
"oai_chat_prompt_execution_settings = OpenAIChatPromptExecutionSettings(\n",
" max_tokens=150,\n",
" temperature=0.7,\n",
" top_p=1,\n",
@@ -260,7 +260,7 @@
" role = \"system\"\n",
" content = \"You are an AI assistant that helps people find information.\"\n",
" message = {\"role\": role, \"content\": content}\n",
" stream = oai_chat_service.complete_chat_stream(messages=[message], settings=oai_chat_request_settings)\n",
" stream = oai_chat_service.complete_chat_stream(messages=[message], settings=oai_chat_prompt_execution_settings)\n",
" async for text in stream:\n",
" print(text, end=\"\") # end = \"\" to avoid newlines"
]
@@ -281,7 +281,7 @@
"metadata": {},
"outputs": [],
"source": [
"az_oai_chat_request_settings = AzureChatRequestSettings(\n",
"az_oai_chat_prompt_execution_settings = AzureChatPromptExecutionSettings(\n",
" max_tokens=150,\n",
" temperature=0.7,\n",
" top_p=1,\n",
@@ -301,7 +301,7 @@
" role = \"system\"\n",
" content = \"You are an AI assistant that helps people find information.\"\n",
" message = {\"role\": role, \"content\": content}\n",
" stream = azure_chat_service.complete_chat_stream(messages=[message], settings=az_oai_chat_request_settings)\n",
" stream = azure_chat_service.complete_chat_stream(messages=[message], settings=az_oai_chat_prompt_execution_settings)\n",
" async for text in stream:\n",
" print(text, end=\"\") # end = \"\" to avoid newlines"
]
@@ -509,4 +509,4 @@
},
"nbformat": 4,
"nbformat_minor": 2
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "semantic-kernel"
version = "0.4.7.dev"
version = "0.5.0.dev"
description = "Semantic Kernel Python SDK"
authors = ["Microsoft <[email protected]>"]
readme = "pip/README.md"

Some files were not shown because too many files have changed in this diff Show More