### Motivation and Context
The dynamic payload creation was requested by a few internal teams. It
allows SK RestAPI functionality consumer code to only supply arguments
for parameters specified in the OpenAPI payload metadata for PUT and
POST operations, while the rest – payload constriction, data type
conversion, parameter namespacing, etc. – is handled by SK RestAPI
functionality.
### Description
#### Context
Today, the only way to pass a payload for a PUT or POST RestAPI
operation is by adding the `payload` argument to the context variables
collection before executing the operation. The `payload` argument, for
operations requiring an 'application/json' payload, is expected to be a
valid JSON with all the necessary properties initialized. This approach
is not convenient for SK consumers who only want to provide the required
payload parameters, similar to how they handle query string and header
parameters, without having to construct the payload themselves.
#### Dynamic creation of payload
By default, dynamic payload creation is disabled to ensure backward
compatibility. To enable it, the `BuildOperationPayloadDynamically`
property of the `OpenApiSkillExecutionParameters` execution parameters
should be set to `true` when importing the AI plugin:
```csharp
var plugin = await kernel.ImportAIPluginAsync("<skill name>", new Uri("<chatGPT-plugin>"), new OpenApiSkillExecutionParameters(httpClient) { BuildOperationPayloadDynamically = true });
```
So, if a RestAPI operation payload schema requires payload like the
following:
```json
{
"value": "secret-value",
"attributes": {
"enabled": true
}
}
```
To dynamically build it, the consumer code needs to register the
following arguments in the context variables collection:
```csharp
var contextVariables = new ContextVariables();
contextVariables.Set("value", "secret-value");
contextVariables.Set("enabled", true);
```
#### Payload parameter namespacing
When building payload dynamically, and a RestAPI operation requires the
'application/json' content type, it's possible that the content may have
properties with the identical names, see example below, at different
levels. In such cases, SK can't resolve their values unambiguously from
a flat list of arguments unless there's a namespacing mechanism that
allows the distinction of those properties from one another.
The namespacing mechanism relies on prefixing parameter names with their
parent parameter name, separated by dots. So, the 'namespaced' parameter
names should be used when adding arguments to the context variables
collection. For example, consider this JSON:
```json
{
"upn": "<sender upn>",
"receiver": {
"upn": "<receiver upn>"
},
"cc": {
"upn": "<cc upn>"
}
}
```
It contains `upn` properties at different levels. The argument
registration for the parameters (property values) will look like:
```csharp
var contextVariables = new ContextVariables();
contextVariables.Set("upn", "<sender-upn-value>");
contextVariables.Set("receiver.upn", "<receiver-upn-value>");
contextVariables.Set("cc.upn", "<cc-upn-value>");
```
The namespacing mechanism is disabled by default for backward
compatibility and can be easily enabled by setting the value `true` to
the `NamespacePayloadParameters` property of the
`OpenApiSkillExecutionParameters` execution parameters when importing
the AI plugin:
```csharp
var plugin = await kernel.ImportAIPluginAsync("<skill name>", new Uri("<chatGPT-plugin>"), new OpenApiSkillExecutionParameters(httpClient) { NamespacePayloadParameters = true });
```
#### Parameter arguments data type conversion
Currently, the SK context variables collection only supports string
variables.This means that in order to set or add a primitive or complex
type variable, it must first be converted or serialized into a string.
However, this behavior causes issues when the RestAPI functionality
sends HTTP requests with an 'application/json' payload. For instance, a
property like enabled might have the string value `"true"` instead of
the intended Boolean value `true`.
To mitigate this problem, the RestAPIOperationRunner class iterates over
the operation payload metadata. During this process, it converts all
string arguments to their corresponding types as specified in the
metadata. This ensures accurate type representation within the operation
payload.
### Contribution Checklist
<!-- Before submitting this PR, please make sure: -->
- [x] The code builds clean without any errors or warnings
- [x] The PR follows the [SK Contribution
Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
and the [pre-submission formatting
script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts)
raises no violations
- [x] All unit tests pass, and I have added new tests where possible
- [x] I didn't break anyone 😄
---------
Co-authored-by: Dmytro Struk <[email protected]>
Get Started with Semantic Kernel ⚡
OpenAI / Azure OpenAI API keys
To run the LLM prompts and semantic functions in the examples below, make sure you have an Open AI API Key or Azure Open AI service key.
Nuget package
Here is a quick example of how to use Semantic Kernel from a C# console app.
First, let's create a new project, targeting .NET 6 or newer, and add the
Microsoft.SemanticKernel nuget package to your project from the command prompt
in Visual Studio:
dotnet add package Microsoft.SemanticKernel --prerelease
Running prompts with input parameters
Copy and paste the following code into your project, with your Azure OpenAI key in hand:
using Microsoft.SemanticKernel;
var builder = new KernelBuilder();
builder.WithAzureChatCompletionService(
"gpt-35-turbo", // Azure OpenAI Deployment Name
"https://contoso.openai.azure.com/", // Azure OpenAI Endpoint
"...your Azure OpenAI Key..."); // Azure OpenAI Key
// Alternative using OpenAI
//builder.WithOpenAIChatCompletionService(
// "gpt-3.5-turbo", // OpenAI Model name
// "...your OpenAI API Key..."); // OpenAI API Key
var kernel = builder.Build();
var prompt = @"{{$input}}
One line TLDR with the fewest words.";
var summarize = kernel.CreateSemanticFunction(prompt, maxTokens: 100);
string text1 = @"
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.";
string text2 = @"
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.";
Console.WriteLine(await summarize.InvokeAsync(text1));
Console.WriteLine(await summarize.InvokeAsync(text2));
// Output:
// Energy conserved, entropy increases, zero entropy at 0K.
// Objects move in response to forces.
Prompt chaining
The previous code shows how to invoke individual semantic functions, but you can also chain functions (aka prompt chaining) to process the initial input with multiple operations.
The following code for example, translates an initial text to math symbols and then generates a summary:
string translationPrompt = @"{{$input}}
Translate the text to math.";
string summarizePrompt = @"{{$input}}
Give me a TLDR with the fewest words.";
var translator = kernel.CreateSemanticFunction(translationPrompt, maxTokens: 200);
var summarize = kernel.CreateSemanticFunction(summarizePrompt, maxTokens: 100);
string inputText = @"
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.";
// Run two prompts in sequence (prompt chaining)
var output = await kernel.RunAsync(inputText, translator, summarize);
Console.WriteLine(output);
// Output: ΔE = 0, ΔSuniv > 0, S = 0 at 0K.
Semantic Kernel Notebooks
The repository contains also a few C# Jupyter notebooks that demonstrates how to get started with the Semantic Kernel.
See here for the full list, with requirements and setup instructions.
- Getting started
- Loading and configuring Semantic Kernel
- Running AI prompts from file
- Creating Semantic Functions at runtime (i.e. inline functions)
- Using Context Variables to Build a Chat Experience
- Creating and Executing Plans
- Building Memory with Embeddings
- Creating images with DALL-E 2
- Chatting with ChatGPT and Images
Nuget packages
Semantic Kernel provides a set of nuget packages to allow extending the core with more features, such as connectors to services and Skills to perform specific actions. Unless you need to optimize which packages to include in your app, you will usually start by installing this meta-package first:
- Microsoft.SemanticKernel
This meta package includes core packages and OpenAI connectors, allowing to run most samples and build apps with OpenAI and Azure OpenAI.
Packages included in Microsoft.SemanticKernel:
- Microsoft.SemanticKernel.Abstractions: contains common interfaces and classes used by the core and other SK components.
- Microsoft.SemanticKernel.Core: contains the core logic of SK, such as prompt engineering, semantic memory and semantic functions definition and orchestration.
- Microsoft.SemanticKernel.Connectors.AI.OpenAI: connectors to OpenAI and Azure OpenAI, allowing to run semantic functions, chats, image generation with GPT3, GPT3.5, GPT4, DALL-E2. Includes also GPT tokenizers.
Other SK packages available at nuget.org:
- Microsoft.SemanticKernel.Connectors.Memory.Qdrant: Qdrant connector for skills and semantic memory.
- Microsoft.SemanticKernel.Connectors.Memory.Sqlite: SQLite connector for skills and semantic memory
- Microsoft.SemanticKernel.Skills.Document: Document Skill: Word processing, OpenXML, etc.
- Microsoft.SemanticKernel.Skills.MsGraph: Microsoft Graph Skill: access your tenant data, schedule meetings, send emails, etc.
- Microsoft.SemanticKernel.Skills.OpenAPI: OpenAPI skill.
- Microsoft.SemanticKernel.Skills.Web: Web Skill: search the web, download files, etc.