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

This commit is contained in:
wehub-resource-sync
2026-07-13 13:21:23 +08:00
commit b957a53def
5423 changed files with 863745 additions and 0 deletions
+159
View File
@@ -0,0 +1,159 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Before starting we need to setup some configuration, like which AI backend to use.\n",
"\n",
"When using the kernel for AI requests, the kernel needs some settings like URL and\n",
"credentials to the AI models. The SDK currently supports OpenAI and Azure OpenAI,\n",
"other services will be added over time. If you need an Azure OpenAI key, go\n",
"[here](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/quickstart?pivots=rest-api)."
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"The following code will ask a few questions and save the settings to a local\n",
"`settings.json` configuration file, under the [config](config) folder. You can\n",
"also edit the file manually if you prefer. **Please keep the file safe.**\n",
"\n",
"## Step 1\n",
"\n",
"First step: choose whether you want to use the notebooks with Azure OpenAI or OpenAI,\n",
"setting the `useAzureOpenAI` boolean below."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"bool useAzureOpenAI = false;"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Step 2\n",
"\n",
"Run the following code. If you need to find the value and copy and paste, you can\n",
"re-run the code and continue from where you left off."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"#!import config/Settings.cs\n",
"\n",
"await Settings.AskAzureEndpoint(useAzureOpenAI);\n",
"await Settings.AskModel(useAzureOpenAI);\n",
"await Settings.AskApiKey(useAzureOpenAI);\n",
"\n",
"// Uncomment this if you're using OpenAI and need to set the Org Id\n",
"// await Settings.AskOrg(useAzureOpenAI);"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"If the code above doesn't show any error, you're good to go and run the other notebooks.\n",
"\n",
"## Resetting the configuration\n",
"\n",
"If you want to reset the configuration and start again, please uncomment and run the code below.\n",
"You can also edit the [config/settings.json](config/settings.json) manually if you prefer."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"#!import config/Settings.cs\n",
"\n",
"// Uncomment this line to reset your settings and delete the file from disk.\n",
"// Settings.Reset();"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Now that your environment is all set up, let's dive into\n",
"[how to do basic loading of the Semantic Kernel](01-basic-loading-the-kernel.ipynb)."
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".NET (C#)",
"language": "C#",
"name": ".net-csharp"
},
"language_info": {
"file_extension": ".cs",
"mimetype": "text/x-csharp",
"name": "C#",
"pygments_lexer": "csharp",
"version": "11.0"
},
"polyglot_notebook": {
"kernelInfo": {
"defaultKernelName": "csharp",
"items": [
{
"aliases": [],
"name": "csharp"
}
]
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}
+180
View File
@@ -0,0 +1,180 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Watch the Getting Started Quick Start [Video](https://aka.ms/SK-Getting-Started-Notebook)\n",
"\n",
"> [!IMPORTANT]\n",
"> You will need an [.NET 10 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/10.0) and [Polyglot](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode) to get started with this notebook using .NET Interactive."
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"**Step 1**: Configure your AI service credentials\n",
"\n",
"Use [this notebook](0-AI-settings.ipynb) first, to choose whether to run these notebooks with OpenAI or Azure OpenAI,\n",
"and to save your credentials in the configuration file."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"// Load some helper functions, e.g. to load values from settings.json\n",
"#!import config/Settings.cs "
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"**Step 2**: Import Semantic Kernel SDK from NuGet"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"// Import Semantic Kernel\n",
"#r \"nuget: Microsoft.SemanticKernel, 1.23.0\""
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"**Step 3**: Instantiate the Kernel"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"using Microsoft.SemanticKernel;\n",
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
"\n",
"//Create Kernel builder\n",
"var builder = Kernel.CreateBuilder();"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"// Configure AI service credentials used by the kernel\n",
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
"\n",
"if (useAzureOpenAI)\n",
" builder.AddAzureOpenAIChatCompletion(model, azureEndpoint, apiKey);\n",
"else\n",
" builder.AddOpenAIChatCompletion(model, apiKey, orgId);\n",
"\n",
"var kernel = builder.Build();"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"**Step 4**: Load and Run a Plugin"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"// FunPlugin directory path\n",
"var funPluginDirectoryPath = Path.Combine(System.IO.Directory.GetCurrentDirectory(), \"..\", \"..\", \"prompt_template_samples\", \"FunPlugin\");\n",
"\n",
"// Load the FunPlugin from the Plugins Directory\n",
"var funPluginFunctions = kernel.ImportPluginFromPromptDirectory(funPluginDirectoryPath);\n",
"\n",
"// Construct arguments\n",
"var arguments = new KernelArguments() { [\"input\"] = \"time travel to dinosaur age\" };\n",
"\n",
"// Run the Function called Joke\n",
"var result = await kernel.InvokeAsync(funPluginFunctions[\"Joke\"], arguments);\n",
"\n",
"// Return the result to the Notebook\n",
"Console.WriteLine(result);"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".NET (C#)",
"language": "C#",
"name": ".net-csharp"
},
"language_info": {
"name": "polyglot-notebook"
},
"polyglot_notebook": {
"kernelInfo": {
"defaultKernelName": "csharp",
"items": [
{
"aliases": [],
"name": "csharp"
}
]
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}
@@ -0,0 +1,169 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Basic Loading of the Kernel"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"The Semantic Kernel SDK can be imported from the following nuget feed:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"#r \"nuget: Microsoft.SemanticKernel, 1.23.0\""
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"After adding the nuget package, you can instantiate the kernel:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"using Microsoft.SemanticKernel;\n",
"using Microsoft.Extensions.Logging;\n",
"using Microsoft.Extensions.Logging.Abstractions;\n",
"using Microsoft.Extensions.DependencyInjection;\n",
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
"\n",
"// Inject your logger \n",
"// see Microsoft.Extensions.Logging.ILogger @ https://learn.microsoft.com/dotnet/core/extensions/logging\n",
"ILoggerFactory myLoggerFactory = NullLoggerFactory.Instance;\n",
"\n",
"var builder = Kernel.CreateBuilder();\n",
"builder.Services.AddSingleton(myLoggerFactory);\n",
"\n",
"var kernel = builder.Build();"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"When using the kernel for AI requests, the kernel needs some settings like URL and credentials to the AI models.\n",
"\n",
"The SDK currently supports OpenAI, Azure OpenAI and HuggingFace. It's also possible to create your own connector and use AI provider of your choice.\n",
"\n",
"If you need an Azure OpenAI key, go [here](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/quickstart?pivots=rest-api)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"Kernel.CreateBuilder()\n",
".AddAzureOpenAIChatCompletion(\n",
" \"my-finetuned-model\", // Azure OpenAI *Deployment Name*\n",
" \"https://contoso.openai.azure.com/\", // Azure OpenAI *Endpoint*\n",
" \"...your Azure OpenAI Key...\", // Azure OpenAI *Key*\n",
" serviceId: \"Azure_curie\" // alias used in the prompt templates' config.json\n",
")\n",
".AddOpenAIChatCompletion(\n",
" \"gpt-4o-mini\", // OpenAI Model Name\n",
" \"...your OpenAI API Key...\", // OpenAI API key\n",
" \"...your OpenAI Org ID...\", // *optional* OpenAI Organization ID\n",
" serviceId: \"OpenAI_davinci\" // alias used in the prompt templates' config.json\n",
");"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"When working with multiple backends and multiple models, the **first backend** defined\n",
"is also the \"**default**\" used in these scenarios:\n",
"\n",
"* a prompt configuration doesn't specify which AI backend to use\n",
"* a prompt configuration requires a backend unknown to the kernel"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Great, now that you're familiar with setting up the Semantic Kernel, let's see [how we can use it to run prompts](02-running-prompts-from-file.ipynb)."
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".NET (C#)",
"language": "C#",
"name": ".net-csharp"
},
"language_info": {
"file_extension": ".cs",
"mimetype": "text/x-csharp",
"name": "C#",
"pygments_lexer": "csharp",
"version": "11.0"
},
"polyglot_notebook": {
"kernelInfo": {
"defaultKernelName": "csharp",
"items": [
{
"aliases": [],
"name": "csharp"
}
]
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}
@@ -0,0 +1,207 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# How to run a semantic plugins from file\n",
"Now that you're familiar with Kernel basics, let's see how the kernel allows you to run Semantic Plugins and Semantic Functions stored on disk. \n",
"\n",
"A Semantic Plugin is a collection of Semantic Functions, where each function is defined with natural language that can be provided with a text file. \n",
"\n",
"Refer to our [glossary](../../docs/GLOSSARY.md) for an in-depth guide to the terms.\n",
"\n",
"The repository includes some examples under the [samples](https://github.com/microsoft/semantic-kernel/tree/main/samples) folder.\n",
"\n",
"For instance, [this](../../samples/plugins/FunPlugin/Joke/skprompt.txt) is the **Joke function** part of the **FunPlugin plugin**:"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"```\n",
"WRITE EXACTLY ONE JOKE or HUMOROUS STORY ABOUT THE TOPIC BELOW.\n",
"JOKE MUST BE:\n",
"- G RATED\n",
"- WORKPLACE/FAMILY SAFE\n",
"NO SEXISM, RACISM OR OTHER BIAS/BIGOTRY.\n",
"BE CREATIVE AND FUNNY. I WANT TO LAUGH.\n",
"+++++\n",
"{{$input}}\n",
"+++++\n",
"```"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Note the special **`{{$input}}`** token, which is a variable that is automatically passed when invoking the function, commonly referred to as a \"function parameter\". \n",
"\n",
"We'll explore later how functions can accept multiple variables, as well as invoke other functions."
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"In the same folder you'll notice a second [config.json](../../samples/plugins/FunPlugin/Joke/config.json) file. The file is optional, and is used to set some parameters for large language models like Temperature, TopP, Stop Sequences, etc.\n",
"\n",
"```\n",
"{\n",
" \"schema\": 1,\n",
" \"description\": \"Generate a funny joke\",\n",
" \"execution_settings\": [\n",
" {\n",
" \"max_tokens\": 1000,\n",
" \"temperature\": 0.9,\n",
" \"top_p\": 0.0,\n",
" \"presence_penalty\": 0.0,\n",
" \"frequency_penalty\": 0.0\n",
" }\n",
" ]\n",
"}\n",
"```"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Given a semantic function defined by these files, this is how to load and use a file based semantic function.\n",
"\n",
"Configure and create the kernel, as usual, loading also the AI backend settings defined in the [Setup notebook](0-AI-settings.ipynb):"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"#r \"nuget: Microsoft.SemanticKernel, 1.23.0\"\n",
"\n",
"#!import config/Settings.cs\n",
"\n",
"using Microsoft.SemanticKernel;\n",
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
"\n",
"var builder = Kernel.CreateBuilder();\n",
"\n",
"// Configure AI backend used by the kernel\n",
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
"\n",
"if (useAzureOpenAI)\n",
" builder.AddAzureOpenAIChatCompletion(model, azureEndpoint, apiKey);\n",
"else\n",
" builder.AddOpenAIChatCompletion(model, apiKey, orgId);\n",
"\n",
"var kernel = builder.Build();"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Import the plugin and all its functions:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"// FunPlugin directory path\n",
"var funPluginDirectoryPath = Path.Combine(System.IO.Directory.GetCurrentDirectory(), \"..\", \"..\", \"prompt_template_samples\", \"FunPlugin\");\n",
"\n",
"// Load the FunPlugin from the Plugins Directory\n",
"var funPluginFunctions = kernel.ImportPluginFromPromptDirectory(funPluginDirectoryPath);"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"How to use the plugin functions, e.g. generate a joke about \"*time travel to dinosaur age*\":"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"// Construct arguments\n",
"var arguments = new KernelArguments() { [\"input\"] = \"time travel to dinosaur age\" };\n",
"\n",
"// Run the Function called Joke\n",
"var result = await kernel.InvokeAsync(funPluginFunctions[\"Joke\"], arguments);\n",
"\n",
"// Return the result to the Notebook\n",
"Console.WriteLine(result);"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Great, now that you know how to load a plugin from disk, let's show how you can [create and run a semantic function inline.](./03-semantic-function-inline.ipynb)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".NET (C#)",
"language": "C#",
"name": ".net-csharp"
},
"language_info": {
"name": "polyglot-notebook"
},
"polyglot_notebook": {
"kernelInfo": {
"defaultKernelName": "csharp",
"items": [
{
"aliases": [],
"name": "csharp"
}
]
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}
@@ -0,0 +1,356 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Running Semantic Functions Inline"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"The [previous notebook](./02-running-prompts-from-file.ipynb)\n",
"showed how to define a semantic function using a prompt template stored on a file.\n",
"\n",
"In this notebook, we'll show how to use the Semantic Kernel to define functions inline with your C# code. This can be useful in a few scenarios:\n",
"\n",
"* Dynamically generating the prompt using complex rules at runtime\n",
"* Writing prompts by editing C# code instead of TXT files. \n",
"* Easily creating demos, like this document\n",
"\n",
"Prompt templates are defined using the SK template language, which allows to reference variables and functions. Read [this doc](https://aka.ms/sk/howto/configurefunction) to learn more about the design decisions for prompt templating. \n",
"\n",
"For now we'll use only the `{{$input}}` variable, and see more complex templates later.\n",
"\n",
"Almost all semantic function prompts have a reference to `{{$input}}`, which is the default way\n",
"a user can import content from the kernel arguments."
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Prepare a semantic kernel instance first, loading also the AI backend settings defined in the [Setup notebook](0-AI-settings.ipynb):"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"#r \"nuget: Microsoft.SemanticKernel, 1.23.0\"\n",
"\n",
"#!import config/Settings.cs\n",
"\n",
"using Microsoft.SemanticKernel;\n",
"using Microsoft.SemanticKernel.Connectors.OpenAI;\n",
"using Microsoft.SemanticKernel.TemplateEngine;\n",
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
"\n",
"var builder = Kernel.CreateBuilder();\n",
"\n",
"// Configure AI service credentials used by the kernel\n",
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
"\n",
"if (useAzureOpenAI)\n",
" builder.AddAzureOpenAIChatCompletion(model, azureEndpoint, apiKey);\n",
"else\n",
" builder.AddOpenAIChatCompletion(model, apiKey, orgId);\n",
"\n",
"var kernel = builder.Build();"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's create a semantic function used to summarize content:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"string skPrompt = \"\"\"\n",
"{{$input}}\n",
"\n",
"Summarize the content above.\n",
"\"\"\";"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's configure the prompt, e.g. allowing for some creativity and a sufficient number of tokens."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"var executionSettings = new OpenAIPromptExecutionSettings \n",
"{\n",
" MaxTokens = 2000,\n",
" Temperature = 0.2,\n",
" TopP = 0.5\n",
"};"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"The following code prepares an instance of the template, passing in the TXT and configuration above, \n",
"and a couple of other parameters (how to render the TXT and how the template can access other functions).\n",
"\n",
"This allows to see the prompt before it's sent to AI."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"var promptTemplateConfig = new PromptTemplateConfig(skPrompt);\n",
"\n",
"var promptTemplateFactory = new KernelPromptTemplateFactory();\n",
"var promptTemplate = promptTemplateFactory.Create(promptTemplateConfig);\n",
"\n",
"var renderedPrompt = await promptTemplate.RenderAsync(kernel);\n",
"\n",
"Console.WriteLine(renderedPrompt);"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's transform the prompt template into a function that the kernel can execute:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"var summaryFunction = kernel.CreateFunctionFromPrompt(skPrompt, executionSettings);"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Set up some content to summarize, here's an extract about Demo, an ancient Greek poet, taken from [Wikipedia](https://en.wikipedia.org/wiki/Demo_(ancient_Greek_poet))."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"var input = \"\"\"\n",
"Demo (ancient Greek poet)\n",
"From Wikipedia, the free encyclopedia\n",
"Demo or Damo (Greek: Δεμώ, Δαμώ; fl.c.AD 200) was a Greek woman of the Roman period, known for a single epigram, engraved upon the Colossus of Memnon, which bears her name. She speaks of herself therein as a lyric poetess dedicated to the Muses, but nothing is known of her life.[1]\n",
"Identity\n",
"Demo was evidently Greek, as her name, a traditional epithet of Demeter, signifies. The name was relatively common in the Hellenistic world, in Egypt and elsewhere, and she cannot be further identified. The date of her visit to the Colossus of Memnon cannot be established with certainty, but internal evidence on the left leg suggests her poem was inscribed there at some point in or after AD 196.[2]\n",
"Epigram\n",
"There are a number of graffiti inscriptions on the Colossus of Memnon. Following three epigrams by Julia Balbilla, a fourth epigram, in elegiac couplets, entitled and presumably authored by \"Demo\" or \"Damo\" (the Greek inscription is difficult to read), is a dedication to the Muses.[2] The poem is traditionally published with the works of Balbilla, though the internal evidence suggests a different author.[1]\n",
"In the poem, Demo explains that Memnon has shown her special respect. In return, Demo offers the gift for poetry, as a gift to the hero. At the end of this epigram, she addresses Memnon, highlighting his divine status by recalling his strength and holiness.[2]\n",
"Demo, like Julia Balbilla, writes in the artificial and poetic Aeolic dialect. The language indicates she was knowledgeable in Homeric poetry—'bearing a pleasant gift', for example, alludes to the use of that phrase throughout the Iliad and Odyssey.[a][2] \n",
"\"\"\";"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"...and run the summary function:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"var summaryResult = await kernel.InvokeAsync(summaryFunction, new() { [\"input\"] = input });\n",
"\n",
"Console.WriteLine(summaryResult);"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"The code above shows all the steps, to understand how the function is composed step by step. However, the kernel\n",
"includes also some helpers to achieve the same more concisely.\n",
"\n",
"The same function above can be executed with less code:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"string skPrompt = \"\"\"\n",
"{{$input}}\n",
"\n",
"Summarize the content above.\n",
"\"\"\";\n",
"\n",
"var result = await kernel.InvokePromptAsync(skPrompt, new() { [\"input\"] = input });\n",
"\n",
"Console.WriteLine(result);"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Here's one more example of how to write an inline Semantic Function that gives a TLDR for a piece of text.\n",
"\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"tags": []
},
"outputs": [],
"source": [
"string skPrompt = @\"\n",
"{{$input}}\n",
"\n",
"Give me the TLDR in 5 words.\n",
"\";\n",
"\n",
"var textToSummarize = @\"\n",
" 1) A robot may not injure a human being or, through inaction,\n",
" allow a human being to come to harm.\n",
"\n",
" 2) A robot must obey orders given it by human beings except where\n",
" such orders would conflict with the First Law.\n",
"\n",
" 3) A robot must protect its own existence as long as such protection\n",
" does not conflict with the First or Second Law.\n",
"\";\n",
"\n",
"var result = await kernel.InvokePromptAsync(skPrompt, new() { [\"input\"] = textToSummarize });\n",
"\n",
"Console.WriteLine(result);"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".NET (C#)",
"language": "C#",
"name": ".net-csharp"
},
"language_info": {
"name": "polyglot-notebook"
},
"polyglot_notebook": {
"kernelInfo": {
"defaultKernelName": "csharp",
"items": [
{
"aliases": [],
"name": "csharp"
}
]
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}
@@ -0,0 +1,384 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Creating a basic chat experience with kernel arguments\n",
"\n",
"In this example, we show how you can build a simple chat bot by sending and updating arguments with your requests. \n",
"\n",
"We introduce the Kernel Arguments object which in this demo functions similarly as a key-value store that you can use when running the kernel. \n",
"\n",
"In this chat scenario, as the user talks back and forth with the bot, the arguments get populated with the history of the conversation. During each new run of the kernel, the arguments will be provided to the AI with content. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"#r \"nuget: Microsoft.SemanticKernel, 1.23.0\"\n",
"#!import config/Settings.cs\n",
"\n",
"using Microsoft.SemanticKernel;\n",
"using Microsoft.SemanticKernel.Connectors.OpenAI;\n",
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
"\n",
"var builder = Kernel.CreateBuilder();\n",
"\n",
"// Configure AI service credentials used by the kernel\n",
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
"\n",
"if (useAzureOpenAI)\n",
" builder.AddAzureOpenAIChatCompletion(model, azureEndpoint, apiKey);\n",
"else\n",
" builder.AddOpenAIChatCompletion(model, apiKey, orgId);\n",
"\n",
"var kernel = builder.Build();"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's define a prompt outlining a dialogue chat bot."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"const string skPrompt = @\"\n",
"ChatBot can have a conversation with you about any topic.\n",
"It can give explicit instructions or say 'I don't know' if it does not have an answer.\n",
"\n",
"{{$history}}\n",
"User: {{$userInput}}\n",
"ChatBot:\";\n",
"\n",
"var executionSettings = new OpenAIPromptExecutionSettings \n",
"{\n",
" MaxTokens = 2000,\n",
" Temperature = 0.7,\n",
" TopP = 0.5\n",
"};"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Register your semantic function"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"var chatFunction = kernel.CreateFunctionFromPrompt(skPrompt, executionSettings);"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Initialize your arguments"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"var history = \"\";\n",
"var arguments = new KernelArguments()\n",
"{\n",
" [\"history\"] = history\n",
"};"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Chat with the Bot"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"var userInput = \"Hi, I'm looking for book suggestions\";\n",
"arguments[\"userInput\"] = userInput;\n",
"\n",
"var bot_answer = await chatFunction.InvokeAsync(kernel, arguments);"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Update the history with the output and set this as the new input value for the next request"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"history += $\"\\nUser: {userInput}\\nAI: {bot_answer}\\n\";\n",
"arguments[\"history\"] = history;\n",
"\n",
"Console.WriteLine(history);"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Keep Chatting!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"Func<string, Task> Chat = async (string input) => {\n",
" // Save new message in the arguments\n",
" arguments[\"userInput\"] = input;\n",
"\n",
" // Process the user message and get an answer\n",
" var answer = await chatFunction.InvokeAsync(kernel, arguments);\n",
"\n",
" // Append the new interaction to the chat history\n",
" var result = $\"\\nUser: {input}\\nAI: {answer}\\n\";\n",
" history += result;\n",
"\n",
" arguments[\"history\"] = history;\n",
" \n",
" // Show the response\n",
" Console.WriteLine(result);\n",
"};"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"await Chat(\"I would like a non-fiction book suggestion about Greece history. Please only list one book.\");"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"await Chat(\"that sounds interesting, what are some of the topics I will learn about?\");"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"await Chat(\"Which topic from the ones you listed do you think most people find interesting?\");"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"await Chat(\"could you list some more books I could read about the topic(s) you mentioned?\");"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"After chatting for a while, we have built a growing history, which we are attaching to each prompt and which contains the full conversation. Let's take a look!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"Console.WriteLine(history);"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".NET (C#)",
"language": "C#",
"name": ".net-csharp"
},
"language_info": {
"file_extension": ".cs",
"mimetype": "text/x-csharp",
"name": "C#",
"pygments_lexer": "csharp",
"version": "11.0"
},
"polyglot_notebook": {
"kernelInfo": {
"defaultKernelName": "csharp",
"items": [
{
"aliases": [],
"name": "csharp"
}
]
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}
@@ -0,0 +1,219 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Introduction to the Function Calling\n",
"\n",
"The most powerful feature of chat completion is the ability to call functions from the model. This allows you to create a chat bot that can interact with your existing code, making it possible to automate business processes, create code snippets, and more.\n",
"\n",
"With Semantic Kernel, we simplify the process of using function calling by automatically describing your functions and their parameters to the model and then handling the back-and-forth communication between the model and your code.\n",
"\n",
"Read more about it [here](https://learn.microsoft.com/en-us/semantic-kernel/concepts/ai-services/chat-completion/function-calling)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"#r \"nuget: Microsoft.SemanticKernel, 1.23.0\"\n",
"\n",
"#!import config/Settings.cs\n",
"#!import config/Utils.cs\n",
"\n",
"using Microsoft.SemanticKernel;\n",
"using Microsoft.SemanticKernel.Connectors.OpenAI;\n",
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
"\n",
"var builder = Kernel.CreateBuilder();\n",
"\n",
"// Configure AI backend used by the kernel\n",
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
"\n",
"if (useAzureOpenAI)\n",
" builder.AddAzureOpenAIChatCompletion(model, azureEndpoint, apiKey);\n",
"else\n",
" builder.AddOpenAIChatCompletion(model, apiKey, orgId);\n",
"\n",
"var kernel = builder.Build();"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Setting Up Execution Settings"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Using `FunctionChoiceBehavior.Auto()` will enable automatic function calling. There are also other options like `Required` or `None` which allow to control function calling behavior. More information about it can be found [here](https://learn.microsoft.com/en-gb/semantic-kernel/concepts/ai-services/chat-completion/function-calling/function-choice-behaviors?pivots=programming-language-csharp)."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"#pragma warning disable SKEXP0001\n",
"\n",
"OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new() \n",
"{\n",
" FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()\n",
"};"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Providing plugins to the Kernel\n",
"Function calling needs an information about available plugins/functions. Here we'll import the `SummarizePlugin` and `WriterPlugin` we have defined on disk."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"var pluginsDirectory = Path.Combine(System.IO.Directory.GetCurrentDirectory(), \"..\", \"..\", \"prompt_template_samples\");\n",
"\n",
"kernel.ImportPluginFromPromptDirectory(Path.Combine(pluginsDirectory, \"SummarizePlugin\"));\n",
"kernel.ImportPluginFromPromptDirectory(Path.Combine(pluginsDirectory, \"WriterPlugin\"));"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Define your ASK. What do you want the Kernel to do?"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"var ask = \"Tomorrow is Valentine's day. I need to come up with a few date ideas. My significant other likes poems so write them in the form of a poem.\";"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Since we imported available plugins to Kernel and defined the ask, we can now invoke a prompt with all the provided information. \n",
"\n",
"We can run function calling with Kernel, if we are interested in result only."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"var result = await kernel.InvokePromptAsync(ask, new(openAIPromptExecutionSettings));\n",
"\n",
"Console.WriteLine(result);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"But we can also run it with `IChatCompletionService` to have an access to `ChatHistory` object, which allows us to see which functions were called as part of a function calling process. Note that passing a Kernel as a parameter to `GetChatMessageContentAsync` method is required, since Kernel holds an information about available plugins."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"using Microsoft.SemanticKernel.ChatCompletion;\n",
"\n",
"var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();\n",
"\n",
"var chatHistory = new ChatHistory();\n",
"\n",
"chatHistory.AddUserMessage(ask);\n",
"\n",
"var chatCompletionResult = await chatCompletionService.GetChatMessageContentAsync(chatHistory, openAIPromptExecutionSettings, kernel);\n",
"\n",
"Console.WriteLine($\"Result: {chatCompletionResult}\\n\");\n",
"Console.WriteLine($\"Chat history: {JsonSerializer.Serialize(chatHistory)}\\n\");"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".NET (C#)",
"language": "C#",
"name": ".net-csharp"
},
"language_info": {
"name": "polyglot-notebook"
},
"polyglot_notebook": {
"kernelInfo": {
"defaultKernelName": "csharp",
"items": [
{
"aliases": [],
"name": "csharp"
}
]
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}
@@ -0,0 +1,494 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Vector Stores and Embeddings\n",
"\n",
"So far, we've mostly been treating the kernel as a stateless orchestration engine.\n",
"We send text into a model API and receive text out. \n",
"\n",
"In a [previous notebook](04-kernel-arguments-chat.ipynb), we used `kernel arguments` to pass in additional\n",
"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 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 `Vector Stores` in the Semantic Kernel.\n",
"\n",
"More information can be found [here](https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"#r \"nuget: Microsoft.SemanticKernel, 1.24.1\"\n",
"#r \"nuget: Microsoft.SemanticKernel.Connectors.InMemory, 1.24.1-preview\"\n",
"#r \"nuget: Microsoft.Extensions.VectorData.Abstractions, 9.0.0-preview.1.24518.1\"\n",
"#r \"nuget: System.Linq.Async, 6.0.1\"\n",
"\n",
"#!import config/Settings.cs\n",
"\n",
"using Microsoft.SemanticKernel;\n",
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
"\n",
"#pragma warning disable SKEXP0010\n",
"\n",
"var builder = Kernel.CreateBuilder();\n",
"\n",
"// Configure AI service credentials used by the kernel\n",
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
"\n",
"if (useAzureOpenAI)\n",
"{\n",
" builder.AddAzureOpenAITextEmbeddingGeneration(\"text-embedding-ada-002\", azureEndpoint, apiKey);\n",
"}\n",
"else\n",
"{\n",
" builder.AddOpenAITextEmbeddingGeneration(\"text-embedding-ada-002\", apiKey, orgId);\n",
"}\n",
"\n",
"var kernel = builder.Build();"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Package `Microsoft.Extensions.VectorData.Abstractions`, which we downloaded in a previous code snippet, contains all necessary abstractions to work with vector stores. \n",
"\n",
"Together with abstractions, we also need to use an implementation of a concrete database connector, such as Azure AI Search, Azure CosmosDB, Qdrant, Redis and so on. A list of supported connectors can be found [here](https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/out-of-the-box-connectors/).\n",
"\n",
"In this example, we are going to use the in-memory connector for demonstration purposes - `Microsoft.SemanticKernel.Connectors.InMemory`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Define your model\n",
"\n",
"It all starts from defining your data model. In abstractions, there are three main data model property types:\n",
"\n",
"1. Key\n",
"2. Data\n",
"3. Vector\n",
"\n",
"In most cases, a data model contains one key property, multiple data and vector properties, but some connectors may have restrictions, for example when only one vector property is supported. \n",
"\n",
"Also, each connector supports a different set of property types. For more information about supported property types in each connector, visit the connector's page, which can be found [here](https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/out-of-the-box-connectors/).\n",
"\n",
"There are two ways how to define your data model - using attributes (declarative way) or record definition (imperative way).\n",
"\n",
"Here is how a data model could look like with attributes:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"using Microsoft.Extensions.VectorData;\n",
"\n",
"public sealed class Glossary\n",
"{\n",
" [VectorStoreRecordKey]\n",
" public ulong Key { get; set; }\n",
"\n",
" [VectorStoreRecordData]\n",
" public string Term { get; set; }\n",
"\n",
" [VectorStoreRecordData]\n",
" public string Definition { get; set; }\n",
"\n",
" [VectorStoreRecordVector(Dimensions: 1536)]\n",
" public ReadOnlyMemory<float> DefinitionEmbedding { get; set; }\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"More information about each attribute and its properties can be found [here](https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/defining-your-data-model#attributes)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There could be a case when you can't modify the existing class with attributes. In this case, you can define a separate record definition with all the information about your properties. Note that the defined data model class is still required in this case:"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"public sealed class GlossaryWithoutAttributes\n",
"{\n",
" public ulong Key { get; set; }\n",
"\n",
" public string Term { get; set; }\n",
"\n",
" public string Definition { get; set; }\n",
"\n",
" public ReadOnlyMemory<float> DefinitionEmbedding { get; set; }\n",
"}\n",
"\n",
"var recordDefinition = new VectorStoreRecordDefinition()\n",
"{\n",
" Properties = new List<VectorStoreRecordProperty>()\n",
" {\n",
" new VectorStoreRecordKeyProperty(\"Key\", typeof(ulong)),\n",
" new VectorStoreRecordDataProperty(\"Term\", typeof(string)),\n",
" new VectorStoreRecordDataProperty(\"Definition\", typeof(string)),\n",
" new VectorStoreRecordVectorProperty(\"DefinitionEmbedding\", typeof(ReadOnlyMemory<float>)) { Dimensions = 1536 }\n",
" }\n",
"};"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Define main components\n",
"\n",
"As soon as you define your data model with either attributes or the record definition approach, you can start using it with your database of choice. \n",
"\n",
"There are a couple of abstractions that allow you to work with your database and collections:\n",
"\n",
"1. `IVectorStoreRecordCollection<TKey, TRecord>` - represents a collection. This collection may or may not exist, and the interface provides methods to check if the collection exists, create it or delete it. The interface also provides methods to upsert, get and delete records. Finally, the interface inherits from `IVectorizedSearch<TRecord>` providing vector search capabilities.\n",
"2. `IVectorStore` - contains operations that spans across all collections in the vector store, e.g. `ListCollectionNames`. It also provides the ability to get `IVectorStoreRecordCollection<TKey, TRecord>` instances.\n",
"\n",
"Each connector has extension methods to register your vector store and collection using DI - `services.AddInMemoryVectorStore()` or `services.AddInMemoryVectorStoreRecordCollection(\"collection-name\")`. \n",
"\n",
"It's also possible to initialize these instances directly, which we are going to do in this notebook for simplicity:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"using Microsoft.SemanticKernel.Connectors.InMemory;\n",
"\n",
"#pragma warning disable SKEXP0020\n",
"\n",
"// Define vector store\n",
"var vectorStore = new InMemoryVectorStore();\n",
"\n",
"// Get a collection instance using vector store\n",
"var collection = vectorStore.GetCollection<ulong, Glossary>(\"skglossary\");\n",
"\n",
"// Get a collection instance by initializing it directly\n",
"var collection2 = new InMemoryVectorStoreRecordCollection<ulong, Glossary>(\"skglossary\");"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Initializing a collection instance will allow you to work with your collection and data, but it doesn't mean that this collection already exists in a database. To ensure you are working with existing collection, you can create it if it doesn't exist:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"await collection.CreateCollectionIfNotExistsAsync();"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, since we just created a new collection, it is empty, so we want to insert some records using the data model we defined above:"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"var glossaryEntries = new List<Glossary>()\n",
"{\n",
" new Glossary() \n",
" {\n",
" Key = 1,\n",
" Term = \"API\",\n",
" Definition = \"Application Programming Interface. A set of rules and specifications that allow software components to communicate and exchange data.\"\n",
" },\n",
" new Glossary() \n",
" {\n",
" Key = 2,\n",
" Term = \"Connectors\",\n",
" Definition = \"Connectors allow you to integrate with various services provide AI capabilities, including LLM, AudioToText, TextToAudio, Embedding generation, etc.\"\n",
" },\n",
" new Glossary() \n",
" {\n",
" Key = 3,\n",
" Term = \"RAG\",\n",
" Definition = \"Retrieval Augmented Generation - a term that refers to the process of retrieving additional data to provide as context to an LLM to use when generating a response (completion) to a user's question (prompt).\"\n",
" }\n",
"};"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If we want to perform a vector search on our records in the database, initializing just the key and data properties is not enough, we also need to generate and initialize vector properties. For that, we can use `ITextEmbeddingGenerationService` which we already registered above.\n",
"\n",
"The line `#pragma warning disable SKEXP0001` is required because `ITextEmbeddingGenerationService` interface is experimental and may change in the future."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"using Microsoft.SemanticKernel.Embeddings;\n",
"\n",
"#pragma warning disable SKEXP0001\n",
"\n",
"var textEmbeddingGenerationService = kernel.GetRequiredService<ITextEmbeddingGenerationService>();\n",
"\n",
"var tasks = glossaryEntries.Select(entry => Task.Run(async () =>\n",
"{\n",
" entry.DefinitionEmbedding = await textEmbeddingGenerationService.GenerateEmbeddingAsync(entry.Definition);\n",
"}));\n",
"\n",
"await Task.WhenAll(tasks);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Upsert records\n",
"\n",
"Now our glossary records are ready to be inserted into the database. For that, we can use `collection.UpsertAsync` or `collection.UpsertBatchAsync` methods. Note that this operation is idempotent - if a record with a specific key doesn't exist, it will be inserted. If it already exists, it will be updated. As a result, we should receive the keys of the upserted records:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"await foreach (var key in collection.UpsertBatchAsync(glossaryEntries))\n",
"{\n",
" Console.WriteLine(key);\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Get records by key\n",
"\n",
"In order to ensure our records were upserted correctly, we can get these records by a key with `collection.GetAsync` or `collection.GetBatchAsync` methods. \n",
"\n",
"Both methods accept `GetRecordOptions` class as a parameter, where you can specify if you want to include vector properties in your response or not. Taking into account that the vector dimension value can be high, if you don't need to work with vectors in your code, it's recommended to not fetch them from the database. That's why `GetRecordOptions.IncludeVectors` property is `false` by default. \n",
"\n",
"In this example, we want to include vectors in the result to ensure that our data was upserted correctly:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"var options = new GetRecordOptions() { IncludeVectors = true };\n",
"\n",
"await foreach (var record in collection.GetBatchAsync(keys: [1, 2, 3], options))\n",
"{\n",
" Console.WriteLine($\"Key: {record.Key}\");\n",
" Console.WriteLine($\"Term: {record.Term}\");\n",
" Console.WriteLine($\"Definition: {record.Definition}\");\n",
" Console.WriteLine($\"Definition Embedding: {JsonSerializer.Serialize(record.DefinitionEmbedding)}\");\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Perform a search\n",
"\n",
"Since we ensured that our records are already in the database, we can perform a vector search with `collection.VectorizedSearchAsync` method. \n",
"\n",
"This method accepts the `VectorSearchOptions` class as a parameter, which allows configuration of the vector search operation - specify the maximum number of records to return, the number of results to skip before returning results, a search filter to use before doing the vector search and so on. More information about it can be found [here](https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/vector-search#vector-search-options).\n",
"\n",
"To perform a vector search, we need a vector generated from our query string:"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"#pragma warning disable SKEXP0001\n",
"\n",
"var searchString = \"I want to learn more about Connectors\";\n",
"var searchVector = await textEmbeddingGenerationService.GenerateEmbeddingAsync(searchString);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As soon as we have our search vector, we can perform a search operation. The result of the `collection.VectorizedSearchAsync` method will be a collection of records from the database with their search scores:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"var searchResult = await collection.VectorizedSearchAsync(searchVector);\n",
"\n",
"await foreach (var result in searchResult.Results)\n",
"{\n",
" Console.WriteLine($\"Search score: {result.Score}\");\n",
" Console.WriteLine($\"Key: {result.Record.Key}\");\n",
" Console.WriteLine($\"Term: {result.Record.Term}\");\n",
" Console.WriteLine($\"Definition: {result.Record.Definition}\");\n",
" Console.WriteLine(\"=========\");\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Additional information\n",
"\n",
"There are more concepts related to the vector stores that will allow you to extend the capabilities. Each of them is described in more detail on the Microsoft Learn portal:\n",
"\n",
"1. [Generic data model](https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/generic-data-model) - allows to store and search data without a concrete data model type, using the generic data model instead.\n",
"2. [Custom mapper](https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/how-to/vector-store-custom-mapper) - define a custom mapper for a specific connector, when the default mapping logic is not enough to work with a database.\n",
"3. [Code samples](https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/code-samples) - end-to-end RAG sample, supporting multiple vectors in the same record, vector search with paging, interoperability with Langchain and more."
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".NET (C#)",
"language": "C#",
"name": ".net-csharp"
},
"language_info": {
"name": "polyglot-notebook"
},
"polyglot_notebook": {
"kernelInfo": {
"defaultKernelName": "csharp",
"items": [
{
"aliases": [],
"name": "csharp"
}
]
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}
+242
View File
@@ -0,0 +1,242 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Generating images with AI\n",
"\n",
"This notebook demonstrates how to use OpenAI DALL-E 3 to generate images, in combination with other LLM features like text and embedding generation.\n",
"\n",
"Here, we use Chat Completion to generate a random image description and DALL-E 3 to create an image from that description, showing the image inline.\n",
"\n",
"Lastly, the notebook asks the user to describe the image. The embedding of the user's description is compared to the original description, using Cosine Similarity, and returning a score from 0 to 1, where 1 means exact match."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"tags": [],
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"// Usual setup: importing Semantic Kernel SDK and SkiaSharp, used to display images inline.\n",
"\n",
"#r \"nuget: Microsoft.SemanticKernel, 1.23.0\"\n",
"#r \"nuget: System.Numerics.Tensors, 8.0.0\"\n",
"#r \"nuget: SkiaSharp, 2.88.3\"\n",
"\n",
"#!import config/Settings.cs\n",
"#!import config/Utils.cs\n",
"#!import config/SkiaUtils.cs\n",
"\n",
"using Microsoft.SemanticKernel;\n",
"using Microsoft.SemanticKernel.TextToImage;\n",
"using Microsoft.SemanticKernel.Embeddings;\n",
"using Microsoft.SemanticKernel.Connectors.OpenAI;\n",
"using System.Numerics.Tensors;"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Setup, using three AI services: images, text, embedding\n",
"\n",
"The notebook uses:\n",
"\n",
"* **OpenAI Dall-E 3** to transform the image description into an image\n",
"* **text-embedding-ada-002** to compare your guess against the real image description\n",
"\n",
"**Note:**: For Azure OpenAI, your endpoint should have DALL-E API enabled."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
"\n",
"#pragma warning disable SKEXP0001, SKEXP0010\n",
"\n",
"// Load OpenAI credentials from config/settings.json\n",
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
"\n",
"// Configure the three AI features: text embedding (using Ada), chat completion, image generation (DALL-E 3)\n",
"var builder = Kernel.CreateBuilder();\n",
"\n",
"if(useAzureOpenAI)\n",
"{\n",
" builder.AddAzureOpenAITextEmbeddingGeneration(\"text-embedding-ada-002\", azureEndpoint, apiKey);\n",
" builder.AddAzureOpenAIChatCompletion(model, azureEndpoint, apiKey);\n",
" builder.AddAzureOpenAITextToImage(\"dall-e-3\", azureEndpoint, apiKey);\n",
"}\n",
"else\n",
"{\n",
" builder.AddOpenAITextEmbeddingGeneration(\"text-embedding-ada-002\", apiKey, orgId);\n",
" builder.AddOpenAIChatCompletion(model, apiKey, orgId);\n",
" builder.AddOpenAITextToImage(apiKey, orgId);\n",
"}\n",
" \n",
"var kernel = builder.Build();\n",
"\n",
"// Get AI service instance used to generate images\n",
"var dallE = kernel.GetRequiredService<ITextToImageService>();\n",
"\n",
"// Get AI service instance used to extract embedding from a text\n",
"var textEmbedding = kernel.GetRequiredService<ITextEmbeddingGenerationService>();"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Generate a (random) image with DALL-E 3\n",
"\n",
"**genImgDescription** is a Semantic Function used to generate a random image description. \n",
"The function takes in input a random number to increase the diversity of its output.\n",
"\n",
"The random image description is then given to **Dall-E 3** asking to create an image."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"tags": [],
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"#pragma warning disable SKEXP0001\n",
"\n",
"var prompt = @\"\n",
"Think about an artificial object correlated to number {{$input}}.\n",
"Describe the image with one detailed sentence. The description cannot contain numbers.\";\n",
"\n",
"var executionSettings = new OpenAIPromptExecutionSettings \n",
"{\n",
" MaxTokens = 256,\n",
" Temperature = 1\n",
"};\n",
"\n",
"// Create a semantic function that generate a random image description.\n",
"var genImgDescription = kernel.CreateFunctionFromPrompt(prompt, executionSettings);\n",
"\n",
"var random = new Random().Next(0, 200);\n",
"var imageDescriptionResult = await kernel.InvokeAsync(genImgDescription, new() { [\"input\"] = random });\n",
"var imageDescription = imageDescriptionResult.ToString();\n",
"\n",
"// Use DALL-E 3 to generate an image. OpenAI in this case returns a URL (though you can ask to return a base64 image)\n",
"var imageUrl = await dallE.GenerateImageAsync(imageDescription.Trim(), 1024, 1024);\n",
"\n",
"await SkiaUtils.ShowImage(imageUrl, 1024, 1024);"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Let's play a guessing game\n",
"\n",
"Try to guess what the image is about, describing the content.\n",
"\n",
"You'll get a score at the end 😉"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"tags": [],
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"// Prompt the user to guess what the image is\n",
"var guess = await InteractiveKernel.GetInputAsync(\"Describe the image in your words\");\n",
"\n",
"// Compare user guess with real description and calculate score\n",
"var origEmbedding = await textEmbedding.GenerateEmbeddingsAsync(new List<string> { imageDescription } );\n",
"var guessEmbedding = await textEmbedding.GenerateEmbeddingsAsync(new List<string> { guess } );\n",
"var similarity = TensorPrimitives.CosineSimilarity(origEmbedding.First().Span, guessEmbedding.First().Span);\n",
"\n",
"Console.WriteLine($\"Your description:\\n{Utils.WordWrap(guess, 90)}\\n\");\n",
"Console.WriteLine($\"Real description:\\n{Utils.WordWrap(imageDescription.Trim(), 90)}\\n\");\n",
"Console.WriteLine($\"Score: {similarity:0.00}\\n\\n\");\n",
"\n",
"//Uncomment this line to see the URL provided by OpenAI\n",
"//Console.WriteLine(imageUrl);"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".NET (C#)",
"language": "C#",
"name": ".net-csharp"
},
"language_info": {
"file_extension": ".cs",
"mimetype": "text/x-csharp",
"name": "C#",
"pygments_lexer": "csharp",
"version": "11.0"
},
"polyglot_notebook": {
"kernelInfo": {
"defaultKernelName": "csharp",
"items": [
{
"aliases": [],
"name": "csharp"
}
]
}
}
},
"nbformat": 4,
"nbformat_minor": 4
}
@@ -0,0 +1,247 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Using ChatGPT with the Semantic Kernel featuring DALL-E 3\n",
"\n",
"This notebook shows how to make use of the new ChatCompletion API from OpenAI, popularized by ChatGPT. This API brings a new ChatML schema which is different from the TextCompletion API. While the text completion API expects input a prompt and returns a simple string, the chat completion API expects in input a Chat history and returns a new message:\n",
"\n",
"```\n",
"messages=[\n",
" { \"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n",
" { \"role\": \"user\", \"content\": \"Who won the world series in 2020?\"},\n",
" { \"role\": \"assistant\", \"content\": \"The Los Angeles Dodgers won the World Series in 2020.\"},\n",
" { \"role\": \"user\", \"content\": \"Where was it played?\"}\n",
"]\n",
"```\n",
"\n",
"Note that there are three message types:\n",
"\n",
"1. A System message is used to give instructions to the chat model, e.g. setting the context and the kind of conversation your app is offering.\n",
"2. User messages store the data received from the user of your app.\n",
"3. Assistant messages store the replies generated by the LLM model. \n",
"\n",
"Your app is responsible for adding information to the chat history and maintaining this object. The Chat Completion API is stateless, and returns only new messages, that your app can use, e.g. to execute commands, generate images, or simply continue the conversation."
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"When deciding between which one to use, know that ChatGPT models (i.e. gpt-3.5-turbo) are optimized for chat applications and have been fine-tuned for instruction-following and dialogue. As such, for creating semantic plugins with the Semantic Kernel, users may still find the TextCompletion model better suited for certain use cases.\n",
"\n",
"The code below shows how to setup SK with ChatGPT, how to manage the Chat history object, and to make things a little more interesting asks ChatGPT to reply with image descriptions instead so we can have a dialog using images, leveraging DALL-E 3 integration."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"tags": [],
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"// Usual setup: importing Semantic Kernel SDK and SkiaSharp, used to display images inline.\n",
"\n",
"#r \"nuget: Microsoft.SemanticKernel, 1.23.0\"\n",
"#r \"nuget: SkiaSharp, 2.88.3\"\n",
"\n",
"#!import config/Settings.cs\n",
"#!import config/Utils.cs\n",
"#!import config/SkiaUtils.cs\n",
"\n",
"using Microsoft.SemanticKernel;\n",
"using Microsoft.SemanticKernel.TextToImage;\n",
"using Microsoft.SemanticKernel.ChatCompletion;\n",
"using Microsoft.SemanticKernel.Connectors.OpenAI;"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"The notebook uses:\n",
"\n",
"* **OpenAI ChatGPT** to chat with the user\n",
"* **OpenAI Dall-E 3** to transform messages into images"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
"\n",
"#pragma warning disable SKEXP0001, SKEXP0010\n",
"\n",
"// Load OpenAI credentials from config/settings.json\n",
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
"\n",
"// Configure the two AI features: OpenAI Chat and DALL-E 3 for image generation\n",
"var builder = Kernel.CreateBuilder();\n",
"\n",
"if(useAzureOpenAI)\n",
"{\n",
" builder.AddAzureOpenAIChatCompletion(\"gpt-4o-mini\", azureEndpoint, apiKey);\n",
" builder.AddAzureOpenAITextToImage(\"dall-e-3\", azureEndpoint, apiKey);\n",
"}\n",
"else\n",
"{\n",
" builder.AddOpenAIChatCompletion(\"gpt-4o-mini\", apiKey, orgId);\n",
" builder.AddOpenAITextToImage(apiKey, orgId);\n",
"}\n",
"\n",
"var kernel = builder.Build();\n",
"\n",
"// Get AI service instance used to generate images\n",
"var dallE = kernel.GetRequiredService<ITextToImageService>();\n",
"\n",
"// Get AI service instance used to manage the user chat\n",
"var chatGPT = kernel.GetRequiredService<IChatCompletionService>();"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chat configuration\n",
"\n",
"Before starting the chat, we create a new chat object with some instructions, which are included in the chat history. \n",
"\n",
"The instructions tell OpenAI what kind of chat we want to have, in this case we ask to reply with \"image descriptions\", so that we can chain ChatGPT with DALL-E 3."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"tags": [],
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"var systemMessage = \"You're chatting with a user. Instead of replying directly to the user\"\n",
" + \" provide a description of a cartoonish image that expresses what you want to say.\"\n",
" + \" The user won't see your message, they will see only the image.\"\n",
" + \" Describe the image with details in one sentence.\";\n",
"\n",
"var chat = new ChatHistory(systemMessage);"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Let's chat\n",
"\n",
"Run the following code to start the chat. The chat consists of a loop with these main steps:\n",
"\n",
"1. Ask the user (you) for a message. The user enters a message. Add the user message into the Chat History object.\n",
"2. Send the chat object to AI asking to generate a response. Add the bot message into the Chat History object.\n",
"3. Show the answer to the user. In our case before showing the answer, generate an image and show that to the user too.\n",
"\n",
"*Note: to stop the chat in VS Code press ESC on the kyboard or the \"stop\" button on the left side.*"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"#pragma warning disable SKEXP0001\n",
"\n",
"while (true)\n",
"{\n",
" // 1. Ask the user for a message. The user enters a message. Add the user message into the Chat History object.\n",
" var userMessage = await InteractiveKernel.GetInputAsync(\"Your message\");\n",
" Console.WriteLine($\"User: {userMessage}\");\n",
" chat.AddUserMessage(userMessage);\n",
"\n",
" // 2. Send the chat object to AI asking to generate a response. Add the bot message into the Chat History object.\n",
" var assistantReply = await chatGPT.GetChatMessageContentAsync(chat, new OpenAIPromptExecutionSettings());\n",
" chat.AddAssistantMessage(assistantReply.Content);\n",
"\n",
" // 3. Show the reply as an image\n",
" Console.WriteLine($\"\\nBot:\");\n",
" var imageUrl = await dallE.GenerateImageAsync(assistantReply.Content, 1024, 1024);\n",
" await SkiaUtils.ShowImage(imageUrl, 1024, 1024);\n",
" Console.WriteLine($\"[{assistantReply}]\\n\");\n",
"}"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".NET (C#)",
"language": "C#",
"name": ".net-csharp"
},
"language_info": {
"file_extension": ".cs",
"mimetype": "text/x-csharp",
"name": "C#",
"pygments_lexer": "csharp",
"version": "11.0"
},
"polyglot_notebook": {
"kernelInfo": {
"defaultKernelName": "csharp",
"items": [
{
"aliases": [],
"name": "csharp"
}
]
}
}
},
"nbformat": 4,
"nbformat_minor": 4
}
@@ -0,0 +1,314 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# RAG with BingSearch \n",
"\n",
"This notebook explains how to integrate Bing Search with the Semantic Kernel to get the latest information from the internet.\n",
"\n",
"To use Bing Search you simply need a Bing Search API key. You can get the API key by creating a [Bing Search resource](https://learn.microsoft.com/en-us/bing/search-apis/bing-web-search/create-bing-search-service-resource) in Azure. \n",
"\n",
"To learn more read the following [documentation](https://learn.microsoft.com/en-us/bing/search-apis/bing-web-search/overview).\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Prepare a Semantic Kernel instance first, loading also the AI backend settings defined in the [Setup notebook](0-AI-settings.ipynb):"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"#r \"nuget: Microsoft.SemanticKernel, 1.23.0\"\n",
"#r \"nuget: Microsoft.SemanticKernel.Plugins.Web, 1.23.0-alpha\"\n",
"#r \"nuget: Microsoft.SemanticKernel.Plugins.Core, 1.23.0-alpha\"\n",
"#r \"nuget: Microsoft.SemanticKernel.PromptTemplates.Handlebars, 1.23.0-alpha\"\n",
"\n",
"#!import config/Settings.cs\n",
"#!import config/Utils.cs"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Enter your Bing Search Key in BING_KEY using `InteractiveKernel` method as introduced in [`.NET Interactive`](https://github.com/dotnet/interactive/blob/main/docs/kernels-overview.md)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"using InteractiveKernel = Microsoft.DotNet.Interactive.Kernel;\n",
"\n",
"string BING_KEY = (await InteractiveKernel.GetPasswordAsync(\"Please enter your Bing Search Key\")).GetClearTextPassword();"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Basic search plugin\n",
"\n",
"The sample below shows how to create a plugin named SearchPlugin from an instance of `BingTextSearch`. Using `CreateWithSearch` creates a new plugin with a single Search function that calls the underlying text search implementation. The SearchPlugin is added to the Kernel which makes it available to be called during prompt rendering. The prompt template includes a call to `{{SearchPlugin.Search $query}}` which will invoke the SearchPlugin to retrieve results related to the current query. The results are then inserted into the rendered prompt before it is sent to the AI model."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"using Microsoft.SemanticKernel;\n",
"using Microsoft.SemanticKernel.Data;\n",
"using Microsoft.SemanticKernel.Plugins.Web.Bing;\n",
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
"\n",
"// Create a kernel with OpenAI chat completion\n",
"var builder = Kernel.CreateBuilder();\n",
"\n",
"// Configure AI backend used by the kernel\n",
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
"if (useAzureOpenAI)\n",
" builder.AddAzureOpenAIChatCompletion(model, azureEndpoint, apiKey);\n",
"else\n",
" builder.AddOpenAIChatCompletion(model, apiKey, orgId);\n",
"var kernel = builder.Build();\n",
"\n",
"// Create a text search using Bing search\n",
"#pragma warning disable SKEXP0050\n",
"var textSearch = new BingTextSearch(apiKey: BING_KEY);\n",
"\n",
"// Build a text search plugin with Bing search and add to the kernel\n",
"var searchPlugin = textSearch.CreateWithSearch(\"SearchPlugin\");\n",
"kernel.Plugins.Add(searchPlugin);\n",
"\n",
"// Invoke prompt and use text search plugin to provide grounding information\n",
"var query = \"What is the Semantic Kernel?\";\n",
"var prompt = \"{{SearchPlugin.Search $query}}. {{$query}}\";\n",
"KernelArguments arguments = new() { { \"query\", query } };\n",
"Console.WriteLine(await kernel.InvokePromptAsync(prompt, arguments));"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Search plugin with citations\n",
"\n",
"The sample below repeats the pattern described in the previous section with a few notable changes:\n",
"\n",
"1. `CreateWithGetTextSearchResults` is used to create a `SearchPlugin` which calls the `GetTextSearchResults` method from the underlying text search implementation.\n",
"2. The prompt template uses Handlebars syntax. This allows the template to iterate over the search results and render the name, value and link for each result.\n",
"3. The prompt includes an instruction to include citations, so the AI model will do the work of adding citations to the response."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"using Microsoft.SemanticKernel;\n",
"using Microsoft.SemanticKernel.Data;\n",
"using Microsoft.SemanticKernel.Plugins.Web.Bing;\n",
"using Microsoft.SemanticKernel.PromptTemplates.Handlebars;\n",
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
"\n",
"// Create a kernel with OpenAI chat completion\n",
"var builder = Kernel.CreateBuilder();\n",
"\n",
"// Configure AI backend used by the kernel\n",
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
"if (useAzureOpenAI)\n",
" builder.AddAzureOpenAIChatCompletion(model, azureEndpoint, apiKey);\n",
"else\n",
" builder.AddOpenAIChatCompletion(model, apiKey, orgId);\n",
"var kernel = builder.Build();\n",
"\n",
"// Create a text search using Bing search\n",
"#pragma warning disable SKEXP0050\n",
"var textSearch = new BingTextSearch(apiKey: BING_KEY);\n",
"\n",
"// Build a text search plugin with Bing search and add to the kernel\n",
"var searchPlugin = textSearch.CreateWithGetTextSearchResults(\"SearchPlugin\");\n",
"kernel.Plugins.Add(searchPlugin);\n",
"\n",
"// Invoke prompt and use text search plugin to provide grounding information\n",
"var query = \"What is the Semantic Kernel?\";\n",
"string promptTemplate = \"\"\"\n",
"{{#with (SearchPlugin-GetTextSearchResults query)}} \n",
" {{#each this}} \n",
" Name: {{Name}}\n",
" Value: {{Value}}\n",
" Link: {{Link}}\n",
" -----------------\n",
" {{/each}} \n",
"{{/with}} \n",
"\n",
"{{query}}\n",
"\n",
"Include citations to the relevant information where it is referenced in the response.\n",
"\"\"\";\n",
"KernelArguments arguments = new() { { \"query\", query } };\n",
"HandlebarsPromptTemplateFactory promptTemplateFactory = new();\n",
"Console.WriteLine(await kernel.InvokePromptAsync(\n",
" promptTemplate,\n",
" arguments,\n",
" templateFormat: HandlebarsPromptTemplateFactory.HandlebarsTemplateFormat,\n",
" promptTemplateFactory: promptTemplateFactory\n",
"));"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Search plugin with a filter\n",
"\n",
"The samples shown so far will use the top ranked web search results to provide the grounding data. To provide more reliability in the data the web search can be restricted to only return results from a specified site.\n",
"\n",
"The sample below builds on the previous one to add filtering of the search results.\n",
"A `TextSearchFilter` with an equality clause is used to specify that only results from the Microsoft Developer Blogs site (`site == 'devblogs.microsoft.com'`) are to be included in the search results.\n",
"\n",
"The sample uses `KernelPluginFactory.CreateFromFunctions` to create the `SearchPlugin`.\n",
"A custom description is provided for the plugin.\n",
"The `ITextSearch.CreateGetTextSearchResults` extension method is used to create the `KernelFunction` which invokes the text search service."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"using Microsoft.SemanticKernel;\n",
"using Microsoft.SemanticKernel.Data;\n",
"using Microsoft.SemanticKernel.PromptTemplates.Handlebars;\n",
"using Microsoft.SemanticKernel.Plugins.Web.Bing;\n",
"\n",
"// Create a kernel with OpenAI chat completion\n",
"var builder = Kernel.CreateBuilder();\n",
"\n",
"// Configure AI backend used by the kernel\n",
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
"if (useAzureOpenAI)\n",
" builder.AddAzureOpenAIChatCompletion(model, azureEndpoint, apiKey);\n",
"else\n",
" builder.AddOpenAIChatCompletion(model, apiKey, orgId);\n",
"var kernel = builder.Build();\n",
"\n",
"// Create a text search using Bing search\n",
"#pragma warning disable SKEXP0050\n",
"var textSearch = new BingTextSearch(apiKey: BING_KEY);\n",
"\n",
"// Create a filter to search only the Microsoft Developer Blogs site\n",
"#pragma warning disable SKEXP0001\n",
"var filter = new TextSearchFilter().Equality(\"site\", \"devblogs.microsoft.com\");\n",
"var searchOptions = new TextSearchOptions() { Filter = filter };\n",
"\n",
"// Build a text search plugin with Bing search and add to the kernel\n",
"var searchPlugin = KernelPluginFactory.CreateFromFunctions(\n",
" \"SearchPlugin\", \"Search Microsoft Developer Blogs site only\",\n",
" [textSearch.CreateGetTextSearchResults(searchOptions: searchOptions)]);\n",
"kernel.Plugins.Add(searchPlugin);\n",
"\n",
"// Invoke prompt and use text search plugin to provide grounding information\n",
"var query = \"What is the Semantic Kernel?\";\n",
"string promptTemplate = \"\"\"\n",
"{{#with (SearchPlugin-GetTextSearchResults query)}} \n",
" {{#each this}} \n",
" Name: {{Name}}\n",
" Value: {{Value}}\n",
" Link: {{Link}}\n",
" -----------------\n",
" {{/each}} \n",
"{{/with}} \n",
"\n",
"{{query}}\n",
"\n",
"Include citations to the relevant information where it is referenced in the response.\n",
"\"\"\";\n",
"KernelArguments arguments = new() { { \"query\", query } };\n",
"HandlebarsPromptTemplateFactory promptTemplateFactory = new();\n",
"Console.WriteLine(await kernel.InvokePromptAsync(\n",
" promptTemplate,\n",
" arguments,\n",
" templateFormat: HandlebarsPromptTemplateFactory.HandlebarsTemplateFormat,\n",
" promptTemplateFactory: promptTemplateFactory\n",
"));\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".NET (C#)",
"language": "C#",
"name": ".net-csharp"
},
"language_info": {
"name": "polyglot-notebook"
},
"orig_nbformat": 4,
"polyglot_notebook": {
"kernelInfo": {
"defaultKernelName": "csharp",
"items": [
{
"aliases": [],
"name": "csharp"
}
]
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}
+120
View File
@@ -0,0 +1,120 @@
# Semantic Kernel C# Notebooks
The current folder contains a few C# Jupyter Notebooks that demonstrate how to get started with
the Semantic Kernel. The notebooks are organized in order of increasing complexity.
To run the notebooks, we recommend the following steps:
- [Install .NET 10](https://dotnet.microsoft.com/download/dotnet/10.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).
The steps above should be sufficient, you can now **open all the C# notebooks in VS Code**.
VS Code screenshot example:
![image](https://user-images.githubusercontent.com/371009/216761942-1861635c-b4b7-4059-8ecf-590d93fe6300.png)
## Set your OpenAI API key
To start using these notebooks, be sure to add the appropriate API keys to `config/settings.json`.
You can create the file manually or run [the Setup notebook](0-AI-settings.ipynb).
For Azure OpenAI:
```json
{
"type": "azure",
"model": "...", // Azure OpenAI Deployment Name
"endpoint": "...", // Azure OpenAI endpoint
"apikey": "..." // Azure OpenAI key
}
```
For OpenAI:
```json
{
"type": "openai",
"model": "gpt-3.5-turbo", // OpenAI model name
"apikey": "...", // OpenAI API Key
"org": "" // only for OpenAI accounts with multiple orgs
}
```
If you need an Azure OpenAI key, go [here](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/quickstart?pivots=rest-api).
If you need an OpenAI key, go [here](https://platform.openai.com/account/api-keys)
# Topics
Before starting, make sure you configured `config/settings.json`,
see the previous section.
For a quick dive, look at the [getting started notebook](00-getting-started.ipynb).
1. [Loading and configuring Semantic Kernel](01-basic-loading-the-kernel.ipynb)
2. [Running AI prompts from file](02-running-prompts-from-file.ipynb)
3. [Creating Semantic Functions at runtime (i.e. inline functions)](03-semantic-function-inline.ipynb)
4. [Using Kernel Arguments to Build a Chat Experience](04-kernel-arguments-chat.ipynb)
5. [Introduction to the Function Calling](05-using-function-calling.ipynb)
6. [Vector Stores and Embeddings](06-vector-stores-and-embeddings.ipynb)
7. [Creating images with DALL-E 3](07-DALL-E-3.ipynb)
8. [Chatting with ChatGPT and Images](08-chatGPT-with-DALL-E-3.ipynb)
9. [BingSearch using Kernel](09-RAG-with-BingSearch.ipynb)
# Run notebooks in the browser with JupyterLab
You can run the notebooks also in the browser with JupyterLab. These steps
should be sufficient to start:
Install Python 3, Pip and .NET 10 in your system, then:
pip install jupyterlab
dotnet tool install -g Microsoft.dotnet-interactive
dotnet tool update -g Microsoft.dotnet-interactive
dotnet interactive jupyter install
This command will confirm that Jupyter now supports C# notebooks:
jupyter kernelspec list
Enter the notebooks folder, and run this to launch the browser interface:
jupyter-lab
![image](https://user-images.githubusercontent.com/371009/216756924-41657aa0-5574-4bc9-9bdb-ead3db7bf93a.png)
# Troubleshooting
## Nuget
If you are unable to get the Nuget package, first list your Nuget sources:
```sh
dotnet nuget list source
```
If you see `No sources found.`, add the NuGet official package source:
```sh
dotnet nuget add source "https://api.nuget.org/v3/index.json" --name "nuget.org"
```
Run `dotnet nuget list source` again to verify the source was added.
## Polyglot Notebooks
If somehow the notebooks don't work, run these commands:
- Install .NET Interactive: `dotnet tool install -g Microsoft.dotnet-interactive`
- Register .NET kernels into Jupyter: `dotnet interactive jupyter install` (this might return some errors, ignore them)
- If you are still stuck, read the following pages:
- https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode
- https://devblogs.microsoft.com/dotnet/net-core-with-juypter-notebooks-is-here-preview-1/
- https://docs.servicestack.net/jupyter-notebooks-csharp
- https://developers.refinitiv.com/en/article-catalog/article/using--net-core-in-jupyter-notebook
Note: ["Polyglot Notebooks" used to be called ".NET Interactive Notebooks"](https://devblogs.microsoft.com/dotnet/dotnet-interactive-notebooks-is-now-polyglot-notebooks/),
so you might find online some documentation referencing the old name.
+9
View File
@@ -0,0 +1,9 @@
*.json
bin
obj
.ipy*
*.csproj
*.ini
*.cache
*.log
*.tmp
+252
View File
@@ -0,0 +1,252 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.DotNet.Interactive;
using InteractiveKernel = Microsoft.DotNet.Interactive.Kernel;
// ReSharper disable InconsistentNaming
public static class Settings
{
private const string DefaultConfigFile = "config/settings.json";
private const string TypeKey = "type";
private const string ModelKey = "model";
private const string EndpointKey = "endpoint";
private const string SecretKey = "apikey";
private const string OrgKey = "org";
private const bool StoreConfigOnFile = true;
// Prompt user for Azure Endpoint URL
public static async Task<string> AskAzureEndpoint(bool _useAzureOpenAI = true, string configFile = DefaultConfigFile)
{
var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = ReadSettings(_useAzureOpenAI, configFile);
// If needed prompt user for Azure endpoint
if (useAzureOpenAI && string.IsNullOrWhiteSpace(azureEndpoint))
{
azureEndpoint = await InteractiveKernel.GetInputAsync("Please enter your Azure OpenAI endpoint");
}
WriteSettings(configFile, useAzureOpenAI, model, azureEndpoint, apiKey, orgId);
// Print report
if (useAzureOpenAI)
{
Console.WriteLine("Settings: " + (string.IsNullOrWhiteSpace(azureEndpoint)
? "ERROR: Azure OpenAI endpoint is empty"
: $"OK: Azure OpenAI endpoint configured [{configFile}]"));
}
return azureEndpoint;
}
// Prompt user for OpenAI model name / Azure OpenAI deployment name
public static async Task<string> AskModel(bool _useAzureOpenAI = true, string configFile = DefaultConfigFile)
{
var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = ReadSettings(_useAzureOpenAI, configFile);
// If needed prompt user for model name / deployment name
if (string.IsNullOrWhiteSpace(model))
{
if (useAzureOpenAI)
{
model = await InteractiveKernel.GetInputAsync("Please enter your Azure OpenAI deployment name");
}
else
{
// Use the best model by default, and reduce the setup friction, particularly in VS Studio.
model = "gpt-4o-mini";
}
}
WriteSettings(configFile, useAzureOpenAI, model, azureEndpoint, apiKey, orgId);
// Print report
if (useAzureOpenAI)
{
Console.WriteLine("Settings: " + (string.IsNullOrWhiteSpace(model)
? "ERROR: deployment name is empty"
: $"OK: deployment name configured [{configFile}]"));
}
else
{
Console.WriteLine("Settings: " + (string.IsNullOrWhiteSpace(model)
? "ERROR: model name is empty"
: $"OK: AI model configured [{configFile}]"));
}
return model;
}
// Prompt user for API Key
public static async Task<string> AskApiKey(bool _useAzureOpenAI = true, string configFile = DefaultConfigFile)
{
var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = ReadSettings(_useAzureOpenAI, configFile);
// If needed prompt user for API key
if (string.IsNullOrWhiteSpace(apiKey))
{
if (useAzureOpenAI)
{
apiKey = (await InteractiveKernel.GetPasswordAsync("Please enter your Azure OpenAI API key")).GetClearTextPassword();
orgId = "";
}
else
{
apiKey = (await InteractiveKernel.GetPasswordAsync("Please enter your OpenAI API key")).GetClearTextPassword();
}
}
WriteSettings(configFile, useAzureOpenAI, model, azureEndpoint, apiKey, orgId);
// Print report
Console.WriteLine("Settings: " + (string.IsNullOrWhiteSpace(apiKey)
? "ERROR: API key is empty"
: $"OK: API key configured [{configFile}]"));
return apiKey;
}
// Prompt user for OpenAI Organization Id
public static async Task<string> AskOrg(bool _useAzureOpenAI = true, string configFile = DefaultConfigFile)
{
var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = ReadSettings(_useAzureOpenAI, configFile);
// If needed prompt user for OpenAI Org Id
if (!useAzureOpenAI && string.IsNullOrWhiteSpace(orgId))
{
orgId = await InteractiveKernel.GetInputAsync("Please enter your OpenAI Organization Id (enter 'NONE' to skip)");
}
WriteSettings(configFile, useAzureOpenAI, model, azureEndpoint, apiKey, orgId);
return orgId;
}
// Load settings from file
public static (bool useAzureOpenAI, string model, string azureEndpoint, string apiKey, string orgId)
LoadFromFile(string configFile = DefaultConfigFile)
{
if (!File.Exists(configFile))
{
Console.WriteLine("Configuration not found: " + configFile);
Console.WriteLine("\nPlease run the Setup Notebook (0-AI-settings.ipynb) to configure your AI backend first.\n");
throw new Exception("Configuration not found, please setup the notebooks first using notebook 0-AI-settings.pynb");
}
try
{
var config = JsonSerializer.Deserialize<Dictionary<string, string>>(File.ReadAllText(configFile));
bool useAzureOpenAI = config[TypeKey] == "azure";
string model = config[ModelKey];
string azureEndpoint = config[EndpointKey];
string apiKey = config[SecretKey];
string orgId = config[OrgKey];
if (orgId == "none") { orgId = ""; }
return (useAzureOpenAI, model, azureEndpoint, apiKey, orgId);
}
catch (Exception e)
{
Console.WriteLine("Something went wrong: " + e.Message);
return (true, "", "", "", "");
}
}
// Delete settings file
public static void Reset(string configFile = DefaultConfigFile)
{
if (!File.Exists(configFile)) { return; }
try
{
File.Delete(configFile);
Console.WriteLine("Settings deleted. Run the notebook again to configure your AI backend.");
}
catch (Exception e)
{
Console.WriteLine("Something went wrong: " + e.Message);
}
}
// Read and return settings from file
private static (bool useAzureOpenAI, string model, string azureEndpoint, string apiKey, string orgId)
ReadSettings(bool _useAzureOpenAI, string configFile)
{
// Save the preference set in the notebook
bool useAzureOpenAI = _useAzureOpenAI;
string model = "";
string azureEndpoint = "";
string apiKey = "";
string orgId = "";
try
{
if (File.Exists(configFile))
{
(useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = LoadFromFile(configFile);
}
}
catch (Exception e)
{
Console.WriteLine("Something went wrong: " + e.Message);
}
// If the preference in the notebook is different from the value on file, then reset
if (useAzureOpenAI != _useAzureOpenAI)
{
Reset(configFile);
useAzureOpenAI = _useAzureOpenAI;
model = "";
azureEndpoint = "";
apiKey = "";
orgId = "";
}
return (useAzureOpenAI, model, azureEndpoint, apiKey, orgId);
}
// Write settings to file
private static void WriteSettings(
string configFile, bool useAzureOpenAI, string model, string azureEndpoint, string apiKey, string orgId)
{
try
{
if (StoreConfigOnFile)
{
var data = new Dictionary<string, string>
{
{ TypeKey, useAzureOpenAI ? "azure" : "openai" },
{ ModelKey, model },
{ EndpointKey, azureEndpoint },
{ SecretKey, apiKey },
{ OrgKey, orgId },
};
var options = new JsonSerializerOptions { WriteIndented = true };
File.WriteAllText(configFile, JsonSerializer.Serialize(data, options));
}
}
catch (Exception e)
{
Console.WriteLine("Something went wrong: " + e.Message);
}
// If asked then delete the credentials stored on disk
if (!StoreConfigOnFile && File.Exists(configFile))
{
try
{
File.Delete(configFile);
}
catch (Exception e)
{
Console.WriteLine("Something went wrong: " + e.Message);
}
}
}
}
+28
View File
@@ -0,0 +1,28 @@
// Copyright (c) Microsoft. All rights reserved.
using SkiaSharp;
using System.Net.Http;
// ReSharper disable InconsistentNaming
public static class SkiaUtils
{
// Function used to display images in the notebook
public static async Task ShowImage(string url, int width, int height)
{
SKImageInfo info = new SKImageInfo(width, height);
SKSurface surface = SKSurface.Create(info);
SKCanvas canvas = surface.Canvas;
canvas.Clear(SKColors.White);
var httpClient = new HttpClient();
using (Stream stream = await httpClient.GetStreamAsync(url))
using (MemoryStream memStream = new MemoryStream())
{
await stream.CopyToAsync(memStream);
memStream.Seek(0, SeekOrigin.Begin);
SKBitmap webBitmap = SKBitmap.Decode(memStream);
canvas.DrawBitmap(webBitmap, 0, 0, null);
surface.Draw(canvas, 0, 0, null);
};
surface.Snapshot().Display();
}
}
+25
View File
@@ -0,0 +1,25 @@
// Copyright (c) Microsoft. All rights reserved.
// ReSharper disable InconsistentNaming
public static class Utils
{
// Function used to wrap long lines of text
public static string WordWrap(string text, int maxLineLength)
{
var result = new StringBuilder();
int i;
var last = 0;
var space = new[] { ' ', '\r', '\n', '\t' };
do
{
i = last + maxLineLength > text.Length
? text.Length
: (text.LastIndexOfAny(new[] { ' ', ',', '.', '?', '!', ':', ';', '-', '\n', '\r', '\t' }, Math.Min(text.Length - 1, last + maxLineLength)) + 1);
if (i <= last) i = Math.Min(last + maxLineLength, text.Length);
result.AppendLine(text.Substring(last, i - last).Trim(space));
last = i;
} while (i < text.Length);
return result.ToString();
}
}
@@ -0,0 +1,7 @@
{
"type": "azure",
"model": "... your Azure OpenAI deployment name ...",
"endpoint": "https:// ... your endpoint ... .openai.azure.com/",
"apikey": "... your Azure OpenAI key ...",
"org": "" // it's not used for azure, but the parser requires it so do not delete
}
@@ -0,0 +1,7 @@
{
"type": "openai",
"endpoint": "NOT-USED-BUT-REQUIRED-FOR-PARSER",
"model": "gpt-4o-mini",
"apikey": "... your OpenAI key ...",
"org": ""
}