diff --git a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Utilities/AIJsonUtilities.Schema.Create.cs b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Utilities/AIJsonUtilities.Schema.Create.cs index a44836d8e96..ad4b897cef2 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Utilities/AIJsonUtilities.Schema.Create.cs +++ b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Utilities/AIJsonUtilities.Schema.Create.cs @@ -274,6 +274,12 @@ JsonNode TransformSchemaNode(JsonSchemaExporterContext schemaExporterContext, Js objSchema.InsertAtStart(TypePropertyName, "string"); } + // Include the type keyword in nullable enum types + if (Nullable.GetUnderlyingType(ctx.TypeInfo.Type)?.IsEnum is true && objSchema.ContainsKey(EnumPropertyName) && !objSchema.ContainsKey(TypePropertyName)) + { + objSchema.InsertAtStart(TypePropertyName, new JsonArray { (JsonNode)"string", (JsonNode)"null" }); + } + // Filter potentially disallowed keywords. foreach (string keyword in _schemaKeywordsDisallowedByAIVendors) { diff --git a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Utilities/AIJsonUtilitiesTests.cs b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Utilities/AIJsonUtilitiesTests.cs index 0001b8b2125..6df42c22670 100644 --- a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Utilities/AIJsonUtilitiesTests.cs +++ b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Utilities/AIJsonUtilitiesTests.cs @@ -477,6 +477,20 @@ public static void CreateJsonSchema_AcceptsOptionsWithoutResolver() Assert.Same(options.TypeInfoResolver, AIJsonUtilities.DefaultOptions.TypeInfoResolver); } + [Fact] + public static void CreateJsonSchema_NullableEnum_IncludesTypeKeyword() + { + JsonElement expectedSchema = JsonDocument.Parse(""" + { + "type": ["string", "null"], + "enum": ["A", "B", null] + } + """).RootElement; + + JsonElement schema = AIJsonUtilities.CreateJsonSchema(typeof(MyEnumValue?), serializerOptions: JsonContext.Default.Options); + AssertDeepEquals(expectedSchema, schema); + } + [Fact] public static void AddAIContentType_DerivedAIContent() { @@ -846,6 +860,7 @@ private class DerivedAIContent : AIContent [JsonSerializable(typeof(DerivedAIContent))] [JsonSerializable(typeof(MyPoco))] [JsonSerializable(typeof(PocoWithTypesWithOpenAIUnsupportedKeywords))] + [JsonSerializable(typeof(MyEnumValue?))] private partial class JsonContext : JsonSerializerContext; private static bool DeepEquals(JsonElement element1, JsonElement element2)