diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/AIFunctionKernelFunction.cs b/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/AIFunctionKernelFunction.cs index 443f99aa367c..30de19e93839 100644 --- a/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/AIFunctionKernelFunction.cs +++ b/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/AIFunctionKernelFunction.cs @@ -66,6 +66,8 @@ private static IReadOnlyList MapParameterMetadata(AIFun return Array.Empty(); } + HashSet? requiredParameters = GetRequiredParameterNames(aiFunction.JsonSchema); + List kernelParams = []; var parameterInfos = aiFunction.UnderlyingMethod?.GetParameters().ToDictionary(p => p.Name!, StringComparer.Ordinal); foreach (var param in properties.EnumerateObject()) @@ -76,7 +78,7 @@ private static IReadOnlyList MapParameterMetadata(AIFun { Description = param.Value.TryGetProperty("description", out JsonElement description) ? description.GetString() : null, DefaultValue = param.Value.TryGetProperty("default", out JsonElement defaultValue) ? defaultValue : null, - IsRequired = param.Value.TryGetProperty("required", out JsonElement required) && required.GetBoolean(), + IsRequired = requiredParameters?.Contains(param.Name) ?? false, ParameterType = paramInfo?.ParameterType, Schema = param.Value.TryGetProperty("schema", out JsonElement schema) ? new KernelJsonSchema(schema) @@ -86,4 +88,25 @@ private static IReadOnlyList MapParameterMetadata(AIFun return kernelParams; } + + /// + /// Gets the names of the required parameters from the AI function's JSON schema. + /// + /// The JSON schema of the AI function. + /// The names of the required parameters. + private static HashSet? GetRequiredParameterNames(JsonElement schema) + { + HashSet? requiredParameterNames = null; + + if (schema.TryGetProperty("required", out JsonElement requiredElement) && requiredElement.ValueKind == JsonValueKind.Array) + { + foreach (var node in requiredElement.EnumerateArray()) + { + requiredParameterNames ??= []; + requiredParameterNames.Add(node.GetString()!); + } + } + + return requiredParameterNames; + } } diff --git a/dotnet/src/SemanticKernel.UnitTests/AI/ChatCompletion/AIFunctionKernelFunctionTests.cs b/dotnet/src/SemanticKernel.UnitTests/AI/ChatCompletion/AIFunctionKernelFunctionTests.cs new file mode 100644 index 000000000000..90c784d58109 --- /dev/null +++ b/dotnet/src/SemanticKernel.UnitTests/AI/ChatCompletion/AIFunctionKernelFunctionTests.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Linq; +using Microsoft.Extensions.AI; +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.ChatCompletion; +using Xunit; + +namespace SemanticKernel.UnitTests.AI.ChatCompletion; + +public class AIFunctionKernelFunctionTests +{ + [Fact] + public void ShouldAssignIsRequiredParameterMetadataPropertyCorrectly() + { + // Arrange and Act + AIFunction aiFunction = AIFunctionFactory.Create((string p1, int? p2 = null) => p1); + + AIFunctionKernelFunction sut = new(aiFunction); + + // Assert + KernelParameterMetadata? p1Metadata = sut.Metadata.Parameters.FirstOrDefault(p => p.Name == "p1"); + Assert.True(p1Metadata?.IsRequired); + + KernelParameterMetadata? p2Metadata = sut.Metadata.Parameters.FirstOrDefault(p => p.Name == "p2"); + Assert.False(p2Metadata?.IsRequired); + } +} diff --git a/dotnet/src/SemanticKernel.UnitTests/SemanticKernel.UnitTests.csproj b/dotnet/src/SemanticKernel.UnitTests/SemanticKernel.UnitTests.csproj index e3452f799be6..8580c9a173ab 100644 --- a/dotnet/src/SemanticKernel.UnitTests/SemanticKernel.UnitTests.csproj +++ b/dotnet/src/SemanticKernel.UnitTests/SemanticKernel.UnitTests.csproj @@ -24,7 +24,7 @@ - +