From 1dc0933b4a7279087285ac70b2b532f19d0999bf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 31 Jan 2026 04:08:57 +0000 Subject: [PATCH 1/3] Initial plan From b4d9bf6ac8a0323ebc8420f48d2ae21e837c1d62 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 31 Jan 2026 04:16:07 +0000 Subject: [PATCH 2/3] Add JsonExtensionData to ToolJson to preserve extra properties like $defs Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../OpenAIClientExtensions.cs | 3 ++ .../OpenAIConversionTests.cs | 43 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIClientExtensions.cs b/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIClientExtensions.cs index d353d46855f..2dd070584fe 100644 --- a/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIClientExtensions.cs +++ b/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIClientExtensions.cs @@ -262,5 +262,8 @@ internal sealed class ToolJson [JsonPropertyName("additionalProperties")] public bool AdditionalProperties { get; set; } + + [JsonExtensionData] + public Dictionary? ExtensionData { get; set; } } } diff --git a/test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAIConversionTests.cs b/test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAIConversionTests.cs index 1aa7e1e4d0f..7ff028ec736 100644 --- a/test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAIConversionTests.cs +++ b/test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAIConversionTests.cs @@ -104,6 +104,49 @@ public void AsOpenAIChatTool_ProducesValidInstance() ValidateSchemaParameters(tool.FunctionParameters); } + [Fact] + public void AsOpenAIChatTool_PreservesExtraTopLevelPropertiesLikeDefs() + { + // Create a JSON schema with $defs (used for reference types) + var jsonSchema = JsonDocument.Parse(""" + { + "type": "object", + "properties": { + "person": { "$ref": "#/$defs/Person" } + }, + "required": ["person"], + "$defs": { + "Person": { + "type": "object", + "properties": { + "name": { "type": "string" } + } + } + } + } + """).RootElement; + + var functionWithDefs = AIFunctionFactory.CreateDeclaration( + "test_function_with_defs", + "A test function with $defs", + jsonSchema); + + var tool = functionWithDefs.AsOpenAIChatTool(); + + Assert.NotNull(tool); + Assert.Equal("test_function_with_defs", tool.FunctionName); + Assert.Equal("A test function with $defs", tool.FunctionDescription); + + // Verify that $defs is preserved in the function parameters + using var parsedParams = JsonDocument.Parse(tool.FunctionParameters); + var root = parsedParams.RootElement; + + Assert.True(root.TryGetProperty("$defs", out var defs), "The $defs property should be preserved in the function parameters"); + Assert.True(defs.TryGetProperty("Person", out var person), "The Person definition should exist in $defs"); + Assert.True(person.TryGetProperty("properties", out var properties), "Person should have properties"); + Assert.True(properties.TryGetProperty("name", out _), "Person should have a name property"); + } + [Fact] public void AsOpenAIResponseTool_ProducesValidInstance() { From 36f153edae61df63cd281d639710d975dc2c8e42 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Feb 2026 15:19:03 +0000 Subject: [PATCH 3/3] Update comment to reflect that ToolJson no longer removes extra properties Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../Microsoft.Extensions.AI.OpenAI/OpenAIClientExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIClientExtensions.cs b/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIClientExtensions.cs index 2dd070584fe..801305ad43a 100644 --- a/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIClientExtensions.cs +++ b/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIClientExtensions.cs @@ -187,8 +187,8 @@ internal static BinaryData ToOpenAIFunctionParameters(AIFunctionDeclaration aiFu StrictSchemaTransformCache.GetOrCreateTransformedSchema(aiFunction) : aiFunction.JsonSchema; - // Roundtrip the schema through the ToolJson model type to remove extra properties - // and force missing ones into existence, then return the serialized UTF8 bytes as BinaryData. + // Roundtrip the schema through the ToolJson model type to force missing properties + // into existence, then return the serialized UTF8 bytes as BinaryData. var tool = JsonSerializer.Deserialize(jsonSchema, OpenAIJsonContext.Default.ToolJson)!; var functionParameters = BinaryData.FromBytes(JsonSerializer.SerializeToUtf8Bytes(tool, OpenAIJsonContext.Default.ToolJson));