mirror of
https://github.com/microsoft/semantic-kernel.git
synced 2026-10-12 02:19:06 +00:00
Fixing logic in custom or HB helper (#4804)
### Motivation and Context <!-- Thank you for your contribution to the semantic-kernel repo! Please help reviewers and future users, providing the following information: 1. Why is this change required? 2. What problem does it solve? 3. What scenario does it contribute to? 4. If it fixes an open issue, please link to the issue here. --> The custom `or` helper had a bug where it was checking for null values, rather than checking for non-null values. This PR changes the condition to `not null`. Also, renamed a heading in the CreatePlan prompt to better guide the model in identifying Handlebars built-in _block_ helpers. ### Description Additional changes to the `GetArgumentValue` method in `KernelHelperUtils.cs` to return null if variable isn't defined. Before, if the template referenced a variable that wasn't defined in `KernelArguments`, the method would just return the key as a string. Now, it just returns null (as expected). > I had it returning a string before because I thought Handlebars would default the argument to its string representation since it templates regular text, but I double checked [HB docs](https://handlebarsjs.com/guide/expressions.html#helpers-with-multiple-parameters), and generally, string literals are surrounded by quotes when a raw value is passed as an argument. Should resolve these issues: - https://github.com/microsoft/semantic-kernel/issues/4661 - https://github.com/microsoft/semantic-kernel/issues/4732 ### Contribution Checklist <!-- Before submitting this PR, please make sure: --> - [x] The code builds clean without any errors or warnings - [x] The PR follows the [SK Contribution Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts) raises no violations - [x] All unit tests pass, and I have added new tests where possible - [x] I didn't break anyone 😄 --------- Co-authored-by: Gina Triolo <[email protected]>
This commit is contained in:
co-authored by
Gina Triolo
parent
d9800fe7f7
commit
52e317a796
@@ -41,7 +41,7 @@ internal static class KernelHelpersUtils
|
||||
// Thus, we try to get the value from the kernel arguments dictionary.
|
||||
if (argument is UndefinedBindingResult result)
|
||||
{
|
||||
return kernelArguments.TryGetValue(result.Value, out var variable) ? variable : result.Value;
|
||||
return kernelArguments.TryGetValue(result.Value, out var variable) ? variable : null;
|
||||
}
|
||||
|
||||
return argument;
|
||||
|
||||
+1
-1
@@ -136,7 +136,7 @@ internal static class KernelSystemHelpers
|
||||
return arg switch
|
||||
{
|
||||
bool booleanArg => booleanArg,
|
||||
_ => arg is null
|
||||
_ => arg is not null
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.SemanticKernel;
|
||||
@@ -30,7 +31,7 @@ public sealed class HandlebarsPlannerTests : IDisposable
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, "Write a joke and send it in an e-mail to Kai.", "SendEmail", "test")]
|
||||
public async Task CreatePlanFunctionFlowAsync(bool useChatModel, string prompt, string expectedFunction, string expectedPlugin)
|
||||
public async Task CreatePlanFunctionFlowAsync(bool useChatModel, string goal, string expectedFunction, string expectedPlugin)
|
||||
{
|
||||
// Arrange
|
||||
bool useEmbeddings = false;
|
||||
@@ -39,7 +40,7 @@ public sealed class HandlebarsPlannerTests : IDisposable
|
||||
TestHelpers.ImportSamplePlugins(kernel, "FunPlugin");
|
||||
|
||||
// Act
|
||||
var plan = await new HandlebarsPlanner().CreatePlanAsync(kernel, prompt);
|
||||
var plan = await new HandlebarsPlanner().CreatePlanAsync(kernel, goal);
|
||||
|
||||
// Assert expected function
|
||||
Assert.Contains(
|
||||
@@ -51,14 +52,14 @@ public sealed class HandlebarsPlannerTests : IDisposable
|
||||
|
||||
[RetryTheory]
|
||||
[InlineData("Write a novel about software development that is 3 chapters long.", "NovelChapter", "WriterPlugin")]
|
||||
public async Task CreatePlanWithDefaultsAsync(string prompt, string expectedFunction, string expectedPlugin)
|
||||
public async Task CreatePlanWithDefaultsAsync(string goal, string expectedFunction, string expectedPlugin)
|
||||
{
|
||||
// Arrange
|
||||
Kernel kernel = this.InitializeKernel();
|
||||
TestHelpers.ImportSamplePlugins(kernel, "WriterPlugin", "MiscPlugin");
|
||||
|
||||
// Act
|
||||
var plan = await new HandlebarsPlanner().CreatePlanAsync(kernel, prompt);
|
||||
var plan = await new HandlebarsPlanner().CreatePlanAsync(kernel, goal);
|
||||
|
||||
// Assert
|
||||
Assert.Contains(
|
||||
@@ -68,6 +69,51 @@ public sealed class HandlebarsPlannerTests : IDisposable
|
||||
);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, "List each property of the default Qux object.", "## Complex types", @"### Qux:
|
||||
{
|
||||
""type"": ""Object"",
|
||||
""properties"": {
|
||||
""Bar"": {
|
||||
""type"": ""String"",
|
||||
},
|
||||
""Baz"": {
|
||||
""type"": ""Int32"",
|
||||
},
|
||||
}
|
||||
}", "GetDefaultQux", "Foo")]
|
||||
public async Task CreatePlanWithComplexTypesDefinitionsAsync(bool useChatModel, string goal, string expectedSectionHeader, string expectedTypeHeader, string expectedFunction, string expectedPlugin)
|
||||
{
|
||||
// Arrange
|
||||
bool useEmbeddings = false;
|
||||
var kernel = this.InitializeKernel(useEmbeddings, useChatModel);
|
||||
kernel.ImportPluginFromObject(new Foo());
|
||||
|
||||
// Act
|
||||
var plan = await new HandlebarsPlanner().CreatePlanAsync(kernel, goal);
|
||||
|
||||
// Assert expected section header for Complex Types in prompt
|
||||
Assert.Contains(
|
||||
expectedSectionHeader,
|
||||
plan.Prompt,
|
||||
StringComparison.CurrentCulture
|
||||
);
|
||||
|
||||
// Assert expected complex parameter type in prompt
|
||||
Assert.Contains(
|
||||
expectedTypeHeader,
|
||||
plan.Prompt,
|
||||
StringComparison.CurrentCulture
|
||||
);
|
||||
|
||||
// Assert expected function in plan
|
||||
Assert.Contains(
|
||||
$"{expectedPlugin}-{expectedFunction}",
|
||||
plan.ToString(),
|
||||
StringComparison.CurrentCulture
|
||||
);
|
||||
}
|
||||
|
||||
private Kernel InitializeKernel(bool useEmbeddings = false, bool useChatModel = true)
|
||||
{
|
||||
AzureOpenAIConfiguration? azureOpenAIConfiguration = this._configuration.GetSection("AzureOpenAI").Get<AzureOpenAIConfiguration>();
|
||||
@@ -110,6 +156,24 @@ public sealed class HandlebarsPlannerTests : IDisposable
|
||||
private readonly RedirectOutput _testOutputHelper;
|
||||
private readonly IConfigurationRoot _configuration;
|
||||
|
||||
private sealed class Foo
|
||||
{
|
||||
public sealed class Qux
|
||||
{
|
||||
public string Bar { get; set; } = string.Empty;
|
||||
public int Baz { get; set; }
|
||||
|
||||
public Qux(string bar, int baz)
|
||||
{
|
||||
this.Bar = bar;
|
||||
this.Baz = baz;
|
||||
}
|
||||
}
|
||||
|
||||
[KernelFunction, Description("Returns default Qux object.")]
|
||||
public Qux GetDefaultQux() => new("bar", 42);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this._testOutputHelper.Dispose();
|
||||
|
||||
@@ -72,7 +72,7 @@ What's the sum of 5+10+15?
|
||||
{{#message role="user"}}
|
||||
The following helpers are available to you:
|
||||
|
||||
## Out-of-the-box helpers
|
||||
## Built-in block helpers
|
||||
- `\{{#if}}\{{/if}}`
|
||||
- `\{{#unless}}\{{/unless}}`{{#if allowLoops}}
|
||||
- `\{{#each}}\{{/each}}`{{/if}}
|
||||
|
||||
Reference in New Issue
Block a user