From d4270406c77dd2b534e92427e7c2bc420cd8b1dc Mon Sep 17 00:00:00 2001 From: Roger Barreto <19890735+RogerBarreto@users.noreply.github.com> Date: Mon, 19 May 2025 13:56:01 +0100 Subject: [PATCH] .Net: Add missing directory Plugin in concepts + Warning fixes (#11858) ### Motivation and Context I noted that we don't actually have any Concept in `Plugins` folder showcasing a good and also long available feature from Semantic Kernel for importing File/Directory based Template Plugins. - Added some minor "message level" fixes to this change. --------- Co-authored-by: Mark Wallace <127216156+markwallace-microsoft@users.noreply.github.com> --- dotnet/Directory.Build.props | 1 + .../CreatePromptPluginFromDirectory.cs | 98 +++++++++++++++++++ dotnet/samples/Concepts/README.md | 1 + ...zureAISearchVectorStoreRecordCollection.cs | 6 +- .../PostgresSqlBuilder.cs | 2 +- .../QdrantVectorStoreRecordCollection.cs | 6 +- .../OllamaPromptExecutionSettingsTests.cs | 6 +- .../AzureAISearchVectorStoreFixture.cs | 6 +- .../CrossLanguage/KernelRequestTracer.cs | 8 +- .../src/System/IndexRange.cs | 6 ++ .../FunPlugin/Joke/config.json | 2 +- 11 files changed, 129 insertions(+), 13 deletions(-) create mode 100644 dotnet/samples/Concepts/Plugins/CreatePromptPluginFromDirectory.cs diff --git a/dotnet/Directory.Build.props b/dotnet/Directory.Build.props index 13e279d799..e59ea8169e 100644 --- a/dotnet/Directory.Build.props +++ b/dotnet/Directory.Build.props @@ -9,6 +9,7 @@ 12 enable disable + $(NoWarn);IDE0290;IDE0079 diff --git a/dotnet/samples/Concepts/Plugins/CreatePromptPluginFromDirectory.cs b/dotnet/samples/Concepts/Plugins/CreatePromptPluginFromDirectory.cs new file mode 100644 index 0000000000..21c1445cbc --- /dev/null +++ b/dotnet/samples/Concepts/Plugins/CreatePromptPluginFromDirectory.cs @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft. All rights reserved. + +using Microsoft.SemanticKernel; + +namespace Plugins; + +/// +/// This sample shows how to create templated plugins from file directories. +/// +public class CreatePromptPluginFromDirectory(ITestOutputHelper output) : BaseTest(output) +{ + [Fact] + public async Task ImportAndUsePromptPluginFromDirectoryWithOpenAI() + { + // Get the current directory of the application + var pluginDirectory = Path.Combine(AppContext.BaseDirectory, "Plugins", "FunPlugin"); + + var kernel = Kernel.CreateBuilder() + .AddOpenAIChatCompletion( + modelId: TestConfiguration.OpenAI.ChatModelId, + apiKey: TestConfiguration.OpenAI.ApiKey) + .Build(); + + CreateFileBasedPluginTemplate(pluginDirectory); + + var funPlugin = kernel.ImportPluginFromPromptDirectoryYaml(pluginDirectory, "FunPlugin"); + + // Invoke the plugin with a prompt + var result = await kernel.InvokeAsync(funPlugin["Joke"], new() + { + ["input"] = "Why did the chicken cross the road?", + ["style"] = "dad joke" + }); + + Console.WriteLine(result); + } + + /// + /// After running this method, a new importable plugin directory structure will be created at the application root. + /// + /// ./Plugins/FunPlugin/ + /// joke.yml + /// + /// Within the FunPlugin directory, any yml file will be imported as a distinct prompt function for the . + /// + private static void CreateFileBasedPluginTemplate(string pluginRootDirectory) + { + // Create the sub-directory for the plugin function "Joke" + var pluginRelativeDirectory = Path.Combine(pluginRootDirectory, "Joke"); + + const string PluginYmlFileContent = + """ + name: Joke + template: | + WRITE EXACTLY ONE JOKE or HUMOROUS STORY ABOUT THE TOPIC BELOW + + JOKE MUST BE: + - G RATED + - WORKPLACE/FAMILY SAFE + NO SEXISM, RACISM OR OTHER BIAS/BIGOTRY + + BE CREATIVE AND FUNNY. I WANT TO LAUGH. + Incorporate the style suggestion, if provided: {{$style}} + +++++ + + {{$input}} + +++++ + template_format: semantic-kernel + description: A function that generates a story about a topic. + input_variables: + - name: input + description: Joke subject. + is_required: true + - name: style + description: Give a hint about the desired joke style. + is_required: true + output_variable: + description: The generated funny joke. + execution_settings: + default: + temperature: 0.9 + max_tokens: 1000 + top_p: 0.0 + presence_penalty: 0.0 + frequency_penalty: 0.0 + """; + + // Create the directory structure + if (!Directory.Exists(pluginRootDirectory)) + { + Directory.CreateDirectory(pluginRootDirectory); + } + + // Create the config.json file if not exists + var ymlFilePath = Path.Combine(pluginRootDirectory, "joke.yml"); + File.WriteAllText(ymlFilePath, PluginYmlFileContent); + } +} diff --git a/dotnet/samples/Concepts/README.md b/dotnet/samples/Concepts/README.md index cd36158782..e999330038 100644 --- a/dotnet/samples/Concepts/README.md +++ b/dotnet/samples/Concepts/README.md @@ -170,6 +170,7 @@ dotnet test -l "console;verbosity=detailed" --filter "FullyQualifiedName=ChatCom - [CreatePluginFromOpenApiSpec_Jira](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/Concepts/Plugins/CreatePluginFromOpenApiSpec_Jira.cs) - [CreatePluginFromOpenApiSpec_Klarna](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/Concepts/Plugins/CreatePluginFromOpenApiSpec_Klarna.cs) - [CreatePluginFromOpenApiSpec_RepairService](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/Concepts/Plugins/CreatePluginFromOpenApiSpec_RepairService.cs) +- [CreatePromptPluginFromDirectory](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/Concepts/Plugins/CreatePromptPluginFromDirectory.cs) - [CrewAI_Plugin](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/Concepts/Plugins/CrewAI_Plugin.cs) - [OpenApiPlugin_PayloadHandling](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/Concepts/Plugins/OpenApiPlugin_PayloadHandling.cs) - [OpenApiPlugin_CustomHttpContentReader](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/Concepts/Plugins/OpenApiPlugin_CustomHttpContentReader.cs) diff --git a/dotnet/src/Connectors/Connectors.Memory.AzureAISearch/AzureAISearchVectorStoreRecordCollection.cs b/dotnet/src/Connectors/Connectors.Memory.AzureAISearch/AzureAISearchVectorStoreRecordCollection.cs index c3b5eefb21..b774f69de4 100644 --- a/dotnet/src/Connectors/Connectors.Memory.AzureAISearch/AzureAISearchVectorStoreRecordCollection.cs +++ b/dotnet/src/Connectors/Connectors.Memory.AzureAISearch/AzureAISearchVectorStoreRecordCollection.cs @@ -168,8 +168,10 @@ public sealed class AzureAISearchVectorStoreRecordCollection : } // Create the index. - var searchIndex = new SearchIndex(this._collectionName, searchFields); - searchIndex.VectorSearch = vectorSearchConfig; + var searchIndex = new SearchIndex(this._collectionName, searchFields) + { + VectorSearch = vectorSearchConfig + }; return this.RunOperationAsync( "CreateIndex", diff --git a/dotnet/src/Connectors/Connectors.Memory.Postgres/PostgresSqlBuilder.cs b/dotnet/src/Connectors/Connectors.Memory.Postgres/PostgresSqlBuilder.cs index e744d88efc..3d076393e6 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Postgres/PostgresSqlBuilder.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Postgres/PostgresSqlBuilder.cs @@ -174,7 +174,7 @@ DO UPDATE SET {updateColumnsWithParams}; var valuesRows = string.Join(", ", rows.Select((row, rowIndex) => $"({string.Join(", ", - columns.Select((c, colIndex) => $"${rowIndex * columns.Count + colIndex + 1}"))})")); + columns.Select((c, colIndex) => $"${(rowIndex * columns.Count) + colIndex + 1}"))})")); // Generate the update set clause var updateSetClause = string.Join(", ", columns.Where(c => c != keyColumn).Select(c => $"\"{c}\" = EXCLUDED.\"{c}\"")); diff --git a/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantVectorStoreRecordCollection.cs b/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantVectorStoreRecordCollection.cs index ea25349f7a..5b6ecc07ee 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantVectorStoreRecordCollection.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantVectorStoreRecordCollection.cs @@ -585,8 +585,10 @@ public sealed class QdrantVectorStoreRecordCollection : IVectorSt #pragma warning restore CS0618 // Type or member is obsolete // Specify whether to include vectors in the search results. - var vectorsSelector = new WithVectorsSelector(); - vectorsSelector.Enable = options.IncludeVectors; + var vectorsSelector = new WithVectorsSelector + { + Enable = options.IncludeVectors + }; var query = new Query { diff --git a/dotnet/src/Connectors/Connectors.Ollama.UnitTests/Settings/OllamaPromptExecutionSettingsTests.cs b/dotnet/src/Connectors/Connectors.Ollama.UnitTests/Settings/OllamaPromptExecutionSettingsTests.cs index 9956a8c01d..b6ebfa2f73 100644 --- a/dotnet/src/Connectors/Connectors.Ollama.UnitTests/Settings/OllamaPromptExecutionSettingsTests.cs +++ b/dotnet/src/Connectors/Connectors.Ollama.UnitTests/Settings/OllamaPromptExecutionSettingsTests.cs @@ -71,8 +71,10 @@ public class OllamaPromptExecutionSettingsTests // Arrange var functionChoiceBehavior = FunctionChoiceBehavior.Auto(); - var originalExecutionSettings = new PromptExecutionSettings(); - originalExecutionSettings.FunctionChoiceBehavior = functionChoiceBehavior; + var originalExecutionSettings = new PromptExecutionSettings + { + FunctionChoiceBehavior = functionChoiceBehavior + }; // Act var result = OllamaPromptExecutionSettings.FromExecutionSettings(originalExecutionSettings); diff --git a/dotnet/src/IntegrationTests/Connectors/Memory/AzureAISearch/AzureAISearchVectorStoreFixture.cs b/dotnet/src/IntegrationTests/Connectors/Memory/AzureAISearch/AzureAISearchVectorStoreFixture.cs index 9ca7fb0c58..160a5da818 100644 --- a/dotnet/src/IntegrationTests/Connectors/Memory/AzureAISearch/AzureAISearchVectorStoreFixture.cs +++ b/dotnet/src/IntegrationTests/Connectors/Memory/AzureAISearch/AzureAISearchVectorStoreFixture.cs @@ -172,8 +172,10 @@ public class AzureAISearchVectorStoreFixture : IAsyncLifetime searchFields.Add(new VectorSearchField("DescriptionEmbedding", 1536, "my-vector-profile")); // Create an index definition with a vectorizer to use when doing vector searches using text. - var definition = new SearchIndex(indexName, searchFields); - definition.VectorSearch = new VectorSearch(); + var definition = new SearchIndex(indexName, searchFields) + { + VectorSearch = new VectorSearch() + }; definition.VectorSearch.Vectorizers.Add(new AzureOpenAIVectorizer("text-embedding-vectorizer") { Parameters = new AzureOpenAIVectorizerParameters diff --git a/dotnet/src/IntegrationTests/CrossLanguage/KernelRequestTracer.cs b/dotnet/src/IntegrationTests/CrossLanguage/KernelRequestTracer.cs index 1621ffdfbf..3a04e428e1 100644 --- a/dotnet/src/IntegrationTests/CrossLanguage/KernelRequestTracer.cs +++ b/dotnet/src/IntegrationTests/CrossLanguage/KernelRequestTracer.cs @@ -143,10 +143,12 @@ internal sealed class KernelRequestTracer : IDisposable { this.DisposeHttpResources(); this._memoryDummyResponse = new MemoryStream(Encoding.UTF8.GetBytes(DummyResponse)); - this._httpMessageHandlerStub = new HttpMessageHandlerStub(); - this._httpMessageHandlerStub.ResponseToReturn = new HttpResponseMessage(HttpStatusCode.OK) + this._httpMessageHandlerStub = new HttpMessageHandlerStub { - Content = new StreamContent(this._memoryDummyResponse) + ResponseToReturn = new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StreamContent(this._memoryDummyResponse) + } }; this._httpClient = new HttpClient(this._httpMessageHandlerStub); } diff --git a/dotnet/src/InternalUtilities/src/System/IndexRange.cs b/dotnet/src/InternalUtilities/src/System/IndexRange.cs index 32c6c9c125..d27fa32aa5 100644 --- a/dotnet/src/InternalUtilities/src/System/IndexRange.cs +++ b/dotnet/src/InternalUtilities/src/System/IndexRange.cs @@ -9,9 +9,15 @@ #pragma warning disable RCS1168 #pragma warning disable RCS1211 +#pragma warning disable RCS1220 #pragma warning disable IDE0009 #pragma warning disable IDE0011 +#pragma warning disable IDE0038 +#pragma warning disable IDE0046 +#pragma warning disable IDE0048 #pragma warning disable IDE0090 +#pragma warning disable IDE0301 +#pragma warning disable CS8604 using System.Runtime.CompilerServices; diff --git a/prompt_template_samples/FunPlugin/Joke/config.json b/prompt_template_samples/FunPlugin/Joke/config.json index bca47badfc..4493fb37d8 100644 --- a/prompt_template_samples/FunPlugin/Joke/config.json +++ b/prompt_template_samples/FunPlugin/Joke/config.json @@ -22,4 +22,4 @@ "default": "" } ] -} +} \ No newline at end of file