Skip to content

Commit a86b2b9

Browse files
.Net: Fix mapping of the KernelParameterMetadata.IsRequired property (#11309)
### Motivation, Context and Description This PR fixes the issue caused by accessing the `required` property value as if it were a boolean property, instead of accessing it as an array of strings. Additionally, it reads the `required` property from the object root instead of reading it from each individual property of the object because the `required` property is defined at the root level to specify which properties are mandatory for the entire object. So, when iterating over the object root level properties, the code now uses the `"required": ["options"]` property declared at the root level instead of the `"required": ["outputPath"]` declared at the `options` property level. ```json { "type": "object", "properties": { "options": { "type": "object", "description": "Code generation options", "properties": { "outputPath": { "type": "string", "description": "Directory path where generated tests will be saved (use absolute path)" }, "testNamePrefix": { "type": "string", "description": "Prefix to use for generated test names (default: \u0027GeneratedTest\u0027)" }, "includeComments": { "type": "boolean", "description": "Whether to include descriptive comments in generated tests" } }, "required": [ "outputPath" ] } }, "required": [ "options" ] } ``` JsonSpec: https://json-schema.org/understanding-json-schema/reference/object#required Fixes: #11292 Contributes to: #11212
1 parent 3b53b7f commit a86b2b9

File tree

3 files changed

+53
-2
lines changed

3 files changed

+53
-2
lines changed

dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/AIFunctionKernelFunction.cs

+24-1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ private static IReadOnlyList<KernelParameterMetadata> MapParameterMetadata(AIFun
6666
return Array.Empty<KernelParameterMetadata>();
6767
}
6868

69+
HashSet<string>? requiredParameters = GetRequiredParameterNames(aiFunction.JsonSchema);
70+
6971
List<KernelParameterMetadata> kernelParams = [];
7072
var parameterInfos = aiFunction.UnderlyingMethod?.GetParameters().ToDictionary(p => p.Name!, StringComparer.Ordinal);
7173
foreach (var param in properties.EnumerateObject())
@@ -76,7 +78,7 @@ private static IReadOnlyList<KernelParameterMetadata> MapParameterMetadata(AIFun
7678
{
7779
Description = param.Value.TryGetProperty("description", out JsonElement description) ? description.GetString() : null,
7880
DefaultValue = param.Value.TryGetProperty("default", out JsonElement defaultValue) ? defaultValue : null,
79-
IsRequired = param.Value.TryGetProperty("required", out JsonElement required) && required.GetBoolean(),
81+
IsRequired = requiredParameters?.Contains(param.Name) ?? false,
8082
ParameterType = paramInfo?.ParameterType,
8183
Schema = param.Value.TryGetProperty("schema", out JsonElement schema)
8284
? new KernelJsonSchema(schema)
@@ -86,4 +88,25 @@ private static IReadOnlyList<KernelParameterMetadata> MapParameterMetadata(AIFun
8688

8789
return kernelParams;
8890
}
91+
92+
/// <summary>
93+
/// Gets the names of the required parameters from the AI function's JSON schema.
94+
/// </summary>
95+
/// <param name="schema">The JSON schema of the AI function.</param>
96+
/// <returns>The names of the required parameters.</returns>
97+
private static HashSet<string>? GetRequiredParameterNames(JsonElement schema)
98+
{
99+
HashSet<string>? requiredParameterNames = null;
100+
101+
if (schema.TryGetProperty("required", out JsonElement requiredElement) && requiredElement.ValueKind == JsonValueKind.Array)
102+
{
103+
foreach (var node in requiredElement.EnumerateArray())
104+
{
105+
requiredParameterNames ??= [];
106+
requiredParameterNames.Add(node.GetString()!);
107+
}
108+
}
109+
110+
return requiredParameterNames;
111+
}
89112
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
using System.Linq;
4+
using Microsoft.Extensions.AI;
5+
using Microsoft.SemanticKernel;
6+
using Microsoft.SemanticKernel.ChatCompletion;
7+
using Xunit;
8+
9+
namespace SemanticKernel.UnitTests.AI.ChatCompletion;
10+
11+
public class AIFunctionKernelFunctionTests
12+
{
13+
[Fact]
14+
public void ShouldAssignIsRequiredParameterMetadataPropertyCorrectly()
15+
{
16+
// Arrange and Act
17+
AIFunction aiFunction = AIFunctionFactory.Create((string p1, int? p2 = null) => p1);
18+
19+
AIFunctionKernelFunction sut = new(aiFunction);
20+
21+
// Assert
22+
KernelParameterMetadata? p1Metadata = sut.Metadata.Parameters.FirstOrDefault(p => p.Name == "p1");
23+
Assert.True(p1Metadata?.IsRequired);
24+
25+
KernelParameterMetadata? p2Metadata = sut.Metadata.Parameters.FirstOrDefault(p => p.Name == "p2");
26+
Assert.False(p2Metadata?.IsRequired);
27+
}
28+
}

dotnet/src/SemanticKernel.UnitTests/SemanticKernel.UnitTests.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
</PackageReference>
2525
<PackageReference Include="Microsoft.ML.Tokenizers" />
2626
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
27-
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
27+
<PackageReference Include="Microsoft.Extensions.AI" />
2828
</ItemGroup>
2929

3030
<ItemGroup>

0 commit comments

Comments
 (0)