From 51d811d8ff84386dd93b601991ccaddf3e10992f Mon Sep 17 00:00:00 2001 From: nikitareshetnik Date: Fri, 7 Mar 2025 00:19:12 +0200 Subject: [PATCH 1/4] Add functionality to create and import plugins with YAML functions --- .../Functions.Yaml/Functions.Yaml.csproj | 4 +- .../PromptYamlKernelExtensions.cs | 177 ++++++++++++++++++ 2 files changed, 179 insertions(+), 2 deletions(-) diff --git a/dotnet/src/Functions/Functions.Yaml/Functions.Yaml.csproj b/dotnet/src/Functions/Functions.Yaml/Functions.Yaml.csproj index f08cb186dac5..542289e6f6e5 100644 --- a/dotnet/src/Functions/Functions.Yaml/Functions.Yaml.csproj +++ b/dotnet/src/Functions/Functions.Yaml/Functions.Yaml.csproj @@ -18,7 +18,8 @@ - + + Semantic Kernel - Support for Yaml Function Definitions @@ -36,5 +37,4 @@ - diff --git a/dotnet/src/Functions/Functions.Yaml/PromptYamlKernelExtensions.cs b/dotnet/src/Functions/Functions.Yaml/PromptYamlKernelExtensions.cs index c290e3e01964..8a61cfc99ab5 100644 --- a/dotnet/src/Functions/Functions.Yaml/PromptYamlKernelExtensions.cs +++ b/dotnet/src/Functions/Functions.Yaml/PromptYamlKernelExtensions.cs @@ -1,5 +1,13 @@ // Copyright (c) Microsoft. All rights reserved. +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; + namespace Microsoft.SemanticKernel; /// @@ -7,6 +15,7 @@ namespace Microsoft.SemanticKernel; /// public static class PromptYamlKernelExtensions { + #region CreateFunctionFromPromptYaml /// /// Creates a instance for a prompt function using the specified YAML. /// @@ -24,4 +33,172 @@ public static KernelFunction CreateFunctionFromPromptYaml( { return KernelFunctionYaml.FromPromptYaml(text, promptTemplateFactory, kernel.LoggerFactory); } + #endregion + + #region CreatePluginFromDirectoryYaml + /// Creates a plugin containing one function per YAML file in the . + /// + /// + /// A plugin directory contains a set of YAML files, each representing a function in the form of a prompt. + /// This method accepts the path of the plugin directory. Each YAML file's name is used as the function name + /// and may contain only alphanumeric chars and underscores. + /// + /// + /// The following directory structure, with pluginDirectory = "D:\plugins\OfficePlugin", + /// will create a plugin with three functions: + /// D:\plugins\ + /// |__ OfficePlugin\ # pluginDirectory + /// |__ ScheduleMeeting.yaml # YAML function + /// |__ SummarizeEmailThread.yaml # YAML function + /// |__ MergeWordAndExcelDocs.yaml # YAML function + /// + /// + /// See https://github.com/microsoft/semantic-kernel/tree/main/prompt_template_samples for examples in the Semantic Kernel repository. + /// + /// + /// The containing services, plugins, and other state for use throughout the operation. + /// Path to the directory containing the plugin. + /// The name of the plugin. If null, the name is derived from the directory name. + /// + /// The to use when interpreting discovered prompts into s. + /// If null, a default factory will be used. + /// + /// A containing prompt functions created from the specified directory. + [RequiresUnreferencedCode("Uses reflection to handle various aspects of the function creation and invocation, making it incompatible with AOT scenarios.")] + [RequiresDynamicCode("Uses reflection to handle various aspects of the function creation and invocation, making it incompatible with AOT scenarios.")] + public static KernelPlugin CreatePluginFromPromptDirectoryYaml( + this Kernel kernel, + string pluginDirectory, + string? pluginName = null, + IPromptTemplateFactory? promptTemplateFactory = null) + { + Verify.NotNull(kernel); + + return CreatePluginFromPromptDirectoryYaml(pluginDirectory, pluginName, promptTemplateFactory, kernel.Services); + } + + /// Creates a plugin containing one function per YAML file in the . + [RequiresUnreferencedCode("Uses reflection to handle various aspects of the function creation and invocation, making it incompatible with AOT scenarios.")] + [RequiresDynamicCode("Uses reflection to handle various aspects of the function creation and invocation, making it incompatible with AOT scenarios.")] + private static KernelPlugin CreatePluginFromPromptDirectoryYaml( + string pluginDirectory, + string? pluginName = null, + IPromptTemplateFactory? promptTemplateFactory = null, + IServiceProvider? services = null) + { + const string YamlFilePattern = "*.yaml"; + + Verify.DirectoryExists(pluginDirectory); + pluginName ??= new DirectoryInfo(pluginDirectory).Name; + + ILoggerFactory loggerFactory = services?.GetService() ?? NullLoggerFactory.Instance; + + var functions = new List(); + ILogger logger = loggerFactory.CreateLogger(typeof(Kernel)) ?? NullLogger.Instance; + + foreach (string functionFile in Directory.GetFiles(pluginDirectory, YamlFilePattern)) + { + var functionName = Path.GetFileName(functionFile); + var functionYaml = File.ReadAllText(functionFile); + + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace("Registering function {0}.{1} loaded from {2}", pluginName, functionName, functionFile); + } + + functions.Add(KernelFunctionYaml.FromPromptYaml(functionYaml, promptTemplateFactory, loggerFactory)); + } + + return KernelPluginFactory.CreateFromFunctions(pluginName, null, functions); + } + #endregion + + #region ImportPlugin/AddFromPromptDirectoryYaml + /// Creates a plugin containing one function per YAML file in the . + /// and imports it into the 's plugin collection. + /// + /// + /// A plugin directory contains a set of YAML files, each representing a function in the form of a prompt. + /// This method accepts the path of the plugin directory. Each YAML file's name is used as the function name + /// and may contain only alphanumeric chars and underscores. + /// + /// + /// The following directory structure, with pluginDirectory = "D:\plugins\OfficePlugin", + /// will create a plugin with three functions: + /// D:\plugins\ + /// |__ OfficePlugin\ # pluginDirectory + /// |__ ScheduleMeeting.yaml # YAML function + /// |__ SummarizeEmailThread.yaml # YAML function + /// |__ MergeWordAndExcelDocs.yaml # YAML function + /// + /// + /// See https://github.com/microsoft/semantic-kernel/tree/main/prompt_template_samples for examples in the Semantic Kernel repository. + /// + /// + /// The containing services, plugins, and other state for use throughout the operation. + /// Path to the directory containing the plugin. + /// The name of the plugin. If null, the name is derived from the directory name. + /// + /// The to use when interpreting discovered prompts into s. + /// If null, a default factory will be used. + /// + /// A containing prompt functions created from the specified directory. + [RequiresUnreferencedCode("Uses reflection to handle various aspects of the function creation and invocation, making it incompatible with AOT scenarios.")] + [RequiresDynamicCode("Uses reflection to handle various aspects of the function creation and invocation, making it incompatible with AOT scenarios.")] + public static KernelPlugin ImportPluginFromPromptDirectoryYaml( + this Kernel kernel, + string pluginDirectory, + string? pluginName = null, + IPromptTemplateFactory? promptTemplateFactory = null) + { + KernelPlugin plugin = CreatePluginFromPromptDirectoryYaml(kernel, pluginDirectory, pluginName, promptTemplateFactory); + kernel.Plugins.Add(plugin); + return plugin; + } + #endregion + + /// Creates a plugin containing one function per YAML file in the . + /// and adds it into the plugin collection. + /// + /// + /// A plugin directory contains a set of YAML files, each representing a function in the form of a prompt. + /// This method accepts the path of the plugin directory. Each YAML file's name is used as the function name + /// and may contain only alphanumeric chars and underscores. + /// + /// + /// The following directory structure, with pluginDirectory = "D:\plugins\OfficePlugin", + /// will create a plugin with three functions: + /// D:\plugins\ + /// |__ OfficePlugin\ # pluginDirectory + /// |__ ScheduleMeeting.yaml # YAML function + /// |__ SummarizeEmailThread.yaml # YAML function + /// |__ MergeWordAndExcelDocs.yaml # YAML function + /// + /// + /// See https://github.com/microsoft/semantic-kernel/tree/main/prompt_template_samples for examples in the Semantic Kernel repository. + /// + /// + /// The plugin collection to which the new plugin should be added. + /// Path to the directory containing the plugin. + /// The name of the plugin. If null, the name is derived from the directory name. + /// + /// The to use when interpreting discovered prompts into s. + /// If null, a default factory will be used. + /// + /// A containing prompt functions created from the specified directory. + [RequiresUnreferencedCode("Uses reflection to handle various aspects of the function creation and invocation, making it incompatible with AOT scenarios.")] + [RequiresDynamicCode("Uses reflection to handle various aspects of the function creation and invocation, making it incompatible with AOT scenarios.")] + public static IKernelBuilderPlugins AddFromPromptDirectoryYaml( + this IKernelBuilderPlugins plugins, + string pluginDirectory, + string? pluginName = null, + IPromptTemplateFactory? promptTemplateFactory = null) + { + Verify.NotNull(plugins); + + plugins.Services.AddSingleton(services => + CreatePluginFromPromptDirectoryYaml(pluginDirectory, pluginName, promptTemplateFactory, services)); + + return plugins; + } } From 7cf7948f092f79e798faa8122119a566b06cd132 Mon Sep 17 00:00:00 2001 From: SergeyMenshykh Date: Fri, 7 Mar 2025 10:55:58 +0000 Subject: [PATCH 2/4] fix compilation error - RestApiOperationRunnerTests.cs(1841,48): error CS0433: The type 'HttpResponseStream' exists in both 'Microsoft.SemanticKernel.Plugins.OpenApi, Version=1.40.1.0, Culture=neutral, PublicKeyToken=null' and 'Microsoft.SemanticKernel.Yaml, Version=1.40.1.0, Culture=neutral, PublicKeyToken=null' [/home/runner/work/semantic-kernel/semantic-kernel/dotnet/src/Functions/Functions.UnitTests/Functions.UnitTests.csproj] --- .../src/Functions/Functions.Yaml/Functions.Yaml.csproj | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/dotnet/src/Functions/Functions.Yaml/Functions.Yaml.csproj b/dotnet/src/Functions/Functions.Yaml/Functions.Yaml.csproj index 542289e6f6e5..5dcc8189379f 100644 --- a/dotnet/src/Functions/Functions.Yaml/Functions.Yaml.csproj +++ b/dotnet/src/Functions/Functions.Yaml/Functions.Yaml.csproj @@ -18,8 +18,7 @@ - - + Semantic Kernel - Support for Yaml Function Definitions @@ -37,4 +36,10 @@ + + + + + + From c9981d793fa96345293daebd61484b61745a3a73 Mon Sep 17 00:00:00 2001 From: nikitareshetnik Date: Fri, 7 Mar 2025 23:07:15 +0200 Subject: [PATCH 3/4] Added unit tests for YAML plugin creation import and add functionality --- .../Yaml/Functions/KernelFunctionYamlTests.cs | 178 +++++++-------- .../Plugins/CreateKernelPluginYamlTests.cs | 213 ++++++++++++++++++ 2 files changed, 302 insertions(+), 89 deletions(-) create mode 100644 dotnet/src/Functions/Functions.UnitTests/Yaml/Plugins/CreateKernelPluginYamlTests.cs diff --git a/dotnet/src/Functions/Functions.UnitTests/Yaml/Functions/KernelFunctionYamlTests.cs b/dotnet/src/Functions/Functions.UnitTests/Yaml/Functions/KernelFunctionYamlTests.cs index 16f048955056..e9d62dc24b5a 100644 --- a/dotnet/src/Functions/Functions.UnitTests/Yaml/Functions/KernelFunctionYamlTests.cs +++ b/dotnet/src/Functions/Functions.UnitTests/Yaml/Functions/KernelFunctionYamlTests.cs @@ -193,97 +193,97 @@ string CreateYaml(object defaultValue) } private readonly string _yamlNoExecutionSettings = @" - template_format: semantic-kernel - template: Say hello world to {{$name}} in {{$language}} - description: Say hello to the specified person using the specified language - name: SayHello - input_variables: - - name: name - description: The name of the person to greet - default: John - - name: language - description: The language to generate the greeting in - default: English - "; + template_format: semantic-kernel + template: Say hello world to {{$name}} in {{$language}} + description: Say hello to the specified person using the specified language + name: SayHello + input_variables: + - name: name + description: The name of the person to greet + default: John + - name: language + description: The language to generate the greeting in + default: English + "; private readonly string _yaml = """ - template_format: semantic-kernel - template: Say hello world to {{$name}} in {{$language}} - description: Say hello to the specified person using the specified language - name: SayHello - input_variables: - - name: name - description: The name of the person to greet - default: John - - name: language - description: The language to generate the greeting in - default: English - execution_settings: - service1: - model_id: gpt-4 - temperature: 1.0 - top_p: 0.0 - presence_penalty: 0.0 - frequency_penalty: 0.0 - max_tokens: 256 - stop_sequences: [] - function_choice_behavior: - type: auto - functions: - - p1.f1 - service2: - model_id: gpt-3.5 - temperature: 1.0 - top_p: 0.0 - presence_penalty: 0.0 - frequency_penalty: 0.0 - max_tokens: 256 - stop_sequences: [ "foo", "bar", "baz" ] - function_choice_behavior: - type: required - functions: - - p2.f2 - service3: - model_id: gpt-3.5 - temperature: 1.0 - top_p: 0.0 - presence_penalty: 0.0 - frequency_penalty: 0.0 - max_tokens: 256 - stop_sequences: [ "foo", "bar", "baz" ] - function_choice_behavior: - type: none - functions: - - p3.f3 - """; + template_format: semantic-kernel + template: Say hello world to {{$name}} in {{$language}} + description: Say hello to the specified person using the specified language + name: SayHello + input_variables: + - name: name + description: The name of the person to greet + default: John + - name: language + description: The language to generate the greeting in + default: English + execution_settings: + service1: + model_id: gpt-4 + temperature: 1.0 + top_p: 0.0 + presence_penalty: 0.0 + frequency_penalty: 0.0 + max_tokens: 256 + stop_sequences: [] + function_choice_behavior: + type: auto + functions: + - p1.f1 + service2: + model_id: gpt-3.5 + temperature: 1.0 + top_p: 0.0 + presence_penalty: 0.0 + frequency_penalty: 0.0 + max_tokens: 256 + stop_sequences: [ "foo", "bar", "baz" ] + function_choice_behavior: + type: required + functions: + - p2.f2 + service3: + model_id: gpt-3.5 + temperature: 1.0 + top_p: 0.0 + presence_penalty: 0.0 + frequency_penalty: 0.0 + max_tokens: 256 + stop_sequences: [ "foo", "bar", "baz" ] + function_choice_behavior: + type: none + functions: + - p3.f3 + """; private readonly string _yamlWithCustomSettings = """ - template_format: semantic-kernel - template: Say hello world to {{$name}} in {{$language}} - description: Say hello to the specified person using the specified language - name: SayHello - input_variables: - - name: name - description: The name of the person to greet - default: John - - name: language - description: The language to generate the greeting in - default: English - execution_settings: - service1: - model_id: gpt-4 - temperature: 1.0 - top_p: 0.0 - presence_penalty: 0.0 - frequency_penalty: 0.0 - max_tokens: 256 - stop_sequences: [] - service2: - model_id: random-model - temperaturex: 1.0 - top_q: 0.0 - rando_penalty: 0.0 - max_token_count: 256 - stop_sequences: [ "foo", "bar", "baz" ] - """; + template_format: semantic-kernel + template: Say hello world to {{$name}} in {{$language}} + description: Say hello to the specified person using the specified language + name: SayHello + input_variables: + - name: name + description: The name of the person to greet + default: John + - name: language + description: The language to generate the greeting in + default: English + execution_settings: + service1: + model_id: gpt-4 + temperature: 1.0 + top_p: 0.0 + presence_penalty: 0.0 + frequency_penalty: 0.0 + max_tokens: 256 + stop_sequences: [] + service2: + model_id: random-model + temperaturex: 1.0 + top_q: 0.0 + rando_penalty: 0.0 + max_token_count: 256 + stop_sequences: [ "foo", "bar", "baz" ] + """; } diff --git a/dotnet/src/Functions/Functions.UnitTests/Yaml/Plugins/CreateKernelPluginYamlTests.cs b/dotnet/src/Functions/Functions.UnitTests/Yaml/Plugins/CreateKernelPluginYamlTests.cs new file mode 100644 index 000000000000..d84ddb80a738 --- /dev/null +++ b/dotnet/src/Functions/Functions.UnitTests/Yaml/Plugins/CreateKernelPluginYamlTests.cs @@ -0,0 +1,213 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.IO; +using System.Linq; +using Xunit; + +namespace Microsoft.SemanticKernel.Functions.UnitTests; + +public sealed class PromptYamlKernelExtensionsTests : IDisposable +{ + private readonly IKernelBuilder _kernelBuilder; + private readonly Kernel _kernel; + + private readonly string _pluginsDirectory; + + private readonly string _plugin1Name; + private readonly string _plugin2Name; + + public PromptYamlKernelExtensionsTests() + { + this._kernelBuilder = Kernel.CreateBuilder(); + this._kernel = this._kernelBuilder.Build(); + + this._pluginsDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + + this._plugin1Name = "Plugin1"; + this._plugin2Name = "Plugin2"; + string plugin1Directory = Path.Combine(this._pluginsDirectory, this._plugin1Name); + string plugin2Directory = Path.Combine(this._pluginsDirectory, this._plugin2Name); + + try + { + Directory.CreateDirectory(this._pluginsDirectory); + Directory.CreateDirectory(plugin1Directory); + Directory.CreateDirectory(plugin2Directory); + + string yamlFile1Path = Path.Combine(plugin1Directory, $"{nameof(YAML)}.yaml"); + string yamlFile2Path = Path.Combine(plugin1Directory, $"{nameof(YAMLWithCustomSettings)}.yaml"); + string yamlFile3Path = Path.Combine(plugin2Directory, $"{nameof(YAMLNoExecutionSettings)}.yaml"); + + File.WriteAllText(yamlFile1Path, YAML); + File.WriteAllText(yamlFile2Path, YAMLWithCustomSettings); + File.WriteAllText(yamlFile3Path, YAMLNoExecutionSettings); + } + catch (Exception) + { + Directory.Delete(this._pluginsDirectory, true); + throw; + } + } + + public void Dispose() + { + if (Directory.Exists(this._pluginsDirectory)) + { + Directory.Delete(this._pluginsDirectory, true); + } + } + + [Fact] + public void ItShouldCreatePluginFromPromptDirectoryYaml() + { + // Arrange + // Act + var plugins = Directory + .EnumerateDirectories(this._pluginsDirectory) + .Select(directory => this._kernel.CreatePluginFromPromptDirectoryYaml(directory)); + + this._kernel.Plugins.AddRange(plugins); + + // Assert + VerifyPluginCounts(this._kernel, this._plugin1Name, this._plugin2Name); + } + + [Fact] + public void ItShouldImportPluginFromPromptDirectoryYaml() + { + // Arrange + // Act + foreach (string directory in Directory.EnumerateDirectories(this._pluginsDirectory)) + { + this._kernel.ImportPluginFromPromptDirectoryYaml(directory); + } + + // Assert + VerifyPluginCounts(this._kernel, this._plugin1Name, this._plugin2Name); + } + + [Fact] + public void ItShouldAddFromPromptDirectoryYaml() + { + // Arrange + // Act + foreach (string directory in Directory.EnumerateDirectories(this._pluginsDirectory)) + { + this._kernelBuilder.Plugins.AddFromPromptDirectoryYaml(directory); + } + + var kernel = this._kernelBuilder.Build(); + + // Assert + VerifyPluginCounts(kernel, this._plugin1Name, this._plugin2Name); + } + + private static void VerifyPluginCounts(Kernel kernel, string expectedPlugin1, string expectedPlugin2) + { + Assert.NotNull(kernel.Plugins); + Assert.Equal(2, kernel.Plugins.Count); + + Assert.NotNull(kernel.Plugins[expectedPlugin1]); + Assert.NotNull(kernel.Plugins[expectedPlugin2]); + + Assert.Equal(2, kernel.Plugins[expectedPlugin1].Count()); + Assert.Single(kernel.Plugins[expectedPlugin2]); + } + + private const string YAML = """ + template_format: semantic-kernel + template: Say hello world to {{$name}} in {{$language}} + description: Say hello to the specified person using the specified language + name: SayHello + input_variables: + - name: name + description: The name of the person to greet + default: John + - name: language + description: The language to generate the greeting in + default: English + execution_settings: + service1: + model_id: gpt-4 + temperature: 1.0 + top_p: 0.0 + presence_penalty: 0.0 + frequency_penalty: 0.0 + max_tokens: 256 + stop_sequences: [] + function_choice_behavior: + type: auto + functions: + - p1.f1 + service2: + model_id: gpt-3.5 + temperature: 1.0 + top_p: 0.0 + presence_penalty: 0.0 + frequency_penalty: 0.0 + max_tokens: 256 + stop_sequences: [ "foo", "bar", "baz" ] + function_choice_behavior: + type: required + functions: + - p2.f2 + service3: + model_id: gpt-3.5 + temperature: 1.0 + top_p: 0.0 + presence_penalty: 0.0 + frequency_penalty: 0.0 + max_tokens: 256 + stop_sequences: [ "foo", "bar", "baz" ] + function_choice_behavior: + type: none + functions: + - p3.f3 + """; + private const string YAMLWithCustomSettings = """ + template_format: semantic-kernel + template: Say hello world to {{$name}} in {{$language}} + description: Say hello to the specified person using the specified language + name: SayHelloWithCustomSettings + input_variables: + - name: name + description: The name of the person to greet + default: John + - name: language + description: The language to generate the greeting in + default: English + execution_settings: + service1: + model_id: gpt-4 + temperature: 1.0 + top_p: 0.0 + presence_penalty: 0.0 + frequency_penalty: 0.0 + max_tokens: 256 + stop_sequences: [] + service2: + model_id: random-model + temperaturex: 1.0 + top_q: 0.0 + rando_penalty: 0.0 + max_token_count: 256 + stop_sequences: [ "foo", "bar", "baz" ] + """; + + private const string YAMLNoExecutionSettings = """ + + template_format: semantic-kernel + template: Say hello world to {{$name}} in {{$language}} + description: Say hello to the specified person using the specified language + name: SayHelloNoExecutionSettings + input_variables: + - name: name + description: The name of the person to greet + default: John + - name: language + description: The language to generate the greeting in + default: English + + """; +} From 4534e33c383501a184a1493e862c2802504688b6 Mon Sep 17 00:00:00 2001 From: nikitareshetnik Date: Fri, 7 Mar 2025 23:12:32 +0200 Subject: [PATCH 4/4] Refactored YAML test cases to use constants for YAML strings --- .../Yaml/Functions/KernelFunctionYamlTests.cs | 204 +++++++++--------- .../Plugins/CreateKernelPluginYamlTests.cs | 66 +++--- 2 files changed, 134 insertions(+), 136 deletions(-) diff --git a/dotnet/src/Functions/Functions.UnitTests/Yaml/Functions/KernelFunctionYamlTests.cs b/dotnet/src/Functions/Functions.UnitTests/Yaml/Functions/KernelFunctionYamlTests.cs index e9d62dc24b5a..bee7b3bb40f0 100644 --- a/dotnet/src/Functions/Functions.UnitTests/Yaml/Functions/KernelFunctionYamlTests.cs +++ b/dotnet/src/Functions/Functions.UnitTests/Yaml/Functions/KernelFunctionYamlTests.cs @@ -32,7 +32,7 @@ public void ItShouldCreateFunctionFromPromptYamlWithNoExecutionSettings() { // Arrange // Act - var function = KernelFunctionYaml.FromPromptYaml(this._yamlNoExecutionSettings); + var function = KernelFunctionYaml.FromPromptYaml(YAMLNoExecutionSettings); // Assert Assert.NotNull(function); @@ -47,7 +47,7 @@ public void ItShouldCreateFunctionFromPromptYaml() { // Arrange // Act - var function = KernelFunctionYaml.FromPromptYaml(this._yaml); + var function = KernelFunctionYaml.FromPromptYaml(YAML); // Assert Assert.NotNull(function); @@ -60,7 +60,7 @@ public void ItShouldCreateFunctionFromPromptYamlWithCustomExecutionSettings() { // Arrange // Act - var function = KernelFunctionYaml.FromPromptYaml(this._yamlWithCustomSettings); + var function = KernelFunctionYaml.FromPromptYaml(YAMLWithCustomSettings); // Assert Assert.NotNull(function); @@ -77,7 +77,7 @@ public void ItShouldSupportCreatingOpenAIExecutionSettings() .WithNamingConvention(UnderscoredNamingConvention.Instance) .WithTypeConverter(new PromptExecutionSettingsTypeConverter()) .Build(); - var promptFunctionModel = deserializer.Deserialize(this._yaml); + var promptFunctionModel = deserializer.Deserialize(YAML); // Act var executionSettings = OpenAIPromptExecutionSettings.FromExecutionSettings(promptFunctionModel.ExecutionSettings["service1"]); @@ -93,7 +93,7 @@ public void ItShouldSupportCreatingOpenAIExecutionSettings() public void ItShouldDeserializeAutoFunctionChoiceBehaviors() { // Act - var promptTemplateConfig = KernelFunctionYaml.ToPromptTemplateConfig(this._yaml); + var promptTemplateConfig = KernelFunctionYaml.ToPromptTemplateConfig(YAML); // Assert Assert.NotNull(promptTemplateConfig?.ExecutionSettings); @@ -114,7 +114,7 @@ public void ItShouldDeserializeAutoFunctionChoiceBehaviors() public void ItShouldDeserializeRequiredFunctionChoiceBehaviors() { // Act - var promptTemplateConfig = KernelFunctionYaml.ToPromptTemplateConfig(this._yaml); + var promptTemplateConfig = KernelFunctionYaml.ToPromptTemplateConfig(YAML); // Assert Assert.NotNull(promptTemplateConfig?.ExecutionSettings); @@ -135,7 +135,7 @@ public void ItShouldDeserializeRequiredFunctionChoiceBehaviors() public void ItShouldDeserializeNoneFunctionChoiceBehaviors() { // Act - var promptTemplateConfig = KernelFunctionYaml.ToPromptTemplateConfig(this._yaml); + var promptTemplateConfig = KernelFunctionYaml.ToPromptTemplateConfig(YAML); // Assert Assert.NotNull(promptTemplateConfig?.ExecutionSettings); @@ -156,7 +156,7 @@ public void ItShouldDeserializeNoneFunctionChoiceBehaviors() public void ItShouldCreateFunctionWithDefaultValueOfStringType() { // Act - var function = KernelFunctionYaml.FromPromptYaml(this._yamlWithCustomSettings); + var function = KernelFunctionYaml.FromPromptYaml(YAMLWithCustomSettings); // Assert Assert.NotNull(function?.Metadata?.Parameters); @@ -192,98 +192,98 @@ string CreateYaml(object defaultValue) Assert.Throws(() => KernelFunctionYaml.FromPromptYaml(CreateYaml(new { p1 = "v1" }))); } - private readonly string _yamlNoExecutionSettings = @" - template_format: semantic-kernel - template: Say hello world to {{$name}} in {{$language}} - description: Say hello to the specified person using the specified language - name: SayHello - input_variables: - - name: name - description: The name of the person to greet - default: John - - name: language - description: The language to generate the greeting in - default: English - "; - - private readonly string _yaml = """ - template_format: semantic-kernel - template: Say hello world to {{$name}} in {{$language}} - description: Say hello to the specified person using the specified language - name: SayHello - input_variables: - - name: name - description: The name of the person to greet - default: John - - name: language - description: The language to generate the greeting in - default: English - execution_settings: - service1: - model_id: gpt-4 - temperature: 1.0 - top_p: 0.0 - presence_penalty: 0.0 - frequency_penalty: 0.0 - max_tokens: 256 - stop_sequences: [] - function_choice_behavior: - type: auto - functions: - - p1.f1 - service2: - model_id: gpt-3.5 - temperature: 1.0 - top_p: 0.0 - presence_penalty: 0.0 - frequency_penalty: 0.0 - max_tokens: 256 - stop_sequences: [ "foo", "bar", "baz" ] - function_choice_behavior: - type: required - functions: - - p2.f2 - service3: - model_id: gpt-3.5 - temperature: 1.0 - top_p: 0.0 - presence_penalty: 0.0 - frequency_penalty: 0.0 - max_tokens: 256 - stop_sequences: [ "foo", "bar", "baz" ] - function_choice_behavior: - type: none - functions: - - p3.f3 - """; - - private readonly string _yamlWithCustomSettings = """ - template_format: semantic-kernel - template: Say hello world to {{$name}} in {{$language}} - description: Say hello to the specified person using the specified language - name: SayHello - input_variables: - - name: name - description: The name of the person to greet - default: John - - name: language - description: The language to generate the greeting in - default: English - execution_settings: - service1: - model_id: gpt-4 - temperature: 1.0 - top_p: 0.0 - presence_penalty: 0.0 - frequency_penalty: 0.0 - max_tokens: 256 - stop_sequences: [] - service2: - model_id: random-model - temperaturex: 1.0 - top_q: 0.0 - rando_penalty: 0.0 - max_token_count: 256 - stop_sequences: [ "foo", "bar", "baz" ] - """; + private const string YAMLNoExecutionSettings = """ + template_format: semantic-kernel + template: Say hello world to {{$name}} in {{$language}} + description: Say hello to the specified person using the specified language + name: SayHello + input_variables: + - name: name + description: The name of the person to greet + default: John + - name: language + description: The language to generate the greeting in + default: English + """; + + private const string YAML = """ + template_format: semantic-kernel + template: Say hello world to {{$name}} in {{$language}} + description: Say hello to the specified person using the specified language + name: SayHello + input_variables: + - name: name + description: The name of the person to greet + default: John + - name: language + description: The language to generate the greeting in + default: English + execution_settings: + service1: + model_id: gpt-4 + temperature: 1.0 + top_p: 0.0 + presence_penalty: 0.0 + frequency_penalty: 0.0 + max_tokens: 256 + stop_sequences: [] + function_choice_behavior: + type: auto + functions: + - p1.f1 + service2: + model_id: gpt-3.5 + temperature: 1.0 + top_p: 0.0 + presence_penalty: 0.0 + frequency_penalty: 0.0 + max_tokens: 256 + stop_sequences: [ "foo", "bar", "baz" ] + function_choice_behavior: + type: required + functions: + - p2.f2 + service3: + model_id: gpt-3.5 + temperature: 1.0 + top_p: 0.0 + presence_penalty: 0.0 + frequency_penalty: 0.0 + max_tokens: 256 + stop_sequences: [ "foo", "bar", "baz" ] + function_choice_behavior: + type: none + functions: + - p3.f3 + """; + + private const string YAMLWithCustomSettings = """ + template_format: semantic-kernel + template: Say hello world to {{$name}} in {{$language}} + description: Say hello to the specified person using the specified language + name: SayHello + input_variables: + - name: name + description: The name of the person to greet + default: John + - name: language + description: The language to generate the greeting in + default: English + execution_settings: + service1: + model_id: gpt-4 + temperature: 1.0 + top_p: 0.0 + presence_penalty: 0.0 + frequency_penalty: 0.0 + max_tokens: 256 + stop_sequences: [] + service2: + model_id: random-model + temperaturex: 1.0 + top_q: 0.0 + rando_penalty: 0.0 + max_token_count: 256 + stop_sequences: [ "foo", "bar", "baz" ] + """; } diff --git a/dotnet/src/Functions/Functions.UnitTests/Yaml/Plugins/CreateKernelPluginYamlTests.cs b/dotnet/src/Functions/Functions.UnitTests/Yaml/Plugins/CreateKernelPluginYamlTests.cs index d84ddb80a738..c8a636e3c7b9 100644 --- a/dotnet/src/Functions/Functions.UnitTests/Yaml/Plugins/CreateKernelPluginYamlTests.cs +++ b/dotnet/src/Functions/Functions.UnitTests/Yaml/Plugins/CreateKernelPluginYamlTests.cs @@ -166,48 +166,46 @@ private static void VerifyPluginCounts(Kernel kernel, string expectedPlugin1, st - p3.f3 """; private const string YAMLWithCustomSettings = """ + template_format: semantic-kernel + template: Say hello world to {{$name}} in {{$language}} + description: Say hello to the specified person using the specified language + name: SayHelloWithCustomSettings + input_variables: + - name: name + description: The name of the person to greet + default: John + - name: language + description: The language to generate the greeting in + default: English + execution_settings: + service1: + model_id: gpt-4 + temperature: 1.0 + top_p: 0.0 + presence_penalty: 0.0 + frequency_penalty: 0.0 + max_tokens: 256 + stop_sequences: [] + service2: + model_id: random-model + temperaturex: 1.0 + top_q: 0.0 + rando_penalty: 0.0 + max_token_count: 256 + stop_sequences: [ "foo", "bar", "baz" ] + """; + + private const string YAMLNoExecutionSettings = """ template_format: semantic-kernel template: Say hello world to {{$name}} in {{$language}} description: Say hello to the specified person using the specified language - name: SayHelloWithCustomSettings + name: SayHelloNoExecutionSettings input_variables: - name: name description: The name of the person to greet default: John - name: language description: The language to generate the greeting in - default: English - execution_settings: - service1: - model_id: gpt-4 - temperature: 1.0 - top_p: 0.0 - presence_penalty: 0.0 - frequency_penalty: 0.0 - max_tokens: 256 - stop_sequences: [] - service2: - model_id: random-model - temperaturex: 1.0 - top_q: 0.0 - rando_penalty: 0.0 - max_token_count: 256 - stop_sequences: [ "foo", "bar", "baz" ] - """; - - private const string YAMLNoExecutionSettings = """ - - template_format: semantic-kernel - template: Say hello world to {{$name}} in {{$language}} - description: Say hello to the specified person using the specified language - name: SayHelloNoExecutionSettings - input_variables: - - name: name - description: The name of the person to greet - default: John - - name: language - description: The language to generate the greeting in - default: English - + default: English """; }