From 0fc0c5219ca37dbd74cce7e7b66bfb9ea8276e3c Mon Sep 17 00:00:00 2001 From: LeSingh1 Date: Tue, 14 Jul 2026 15:47:24 -0700 Subject: [PATCH] Python: Map NoneType to JSON Schema "null" instead of "object" in schema builder KernelJsonSchemaBuilder omitted NoneType from TYPE_MAPPING, so get_json_schema(type(None)) and any union of 3+ members containing None (e.g. int | str | None) emitted {"type": "object"} for the None member instead of the correct {"type": "null"}. These schemas mislead LLMs during function calling / structured output. The two-member Optional[T] path builds nullability separately and was already correct. Add NoneType (and the "None"/"NoneType" string names used by build_from_type_name) to TYPE_MAPPING, with regression tests. --- .../schema/kernel_json_schema_builder.py | 3 +++ .../tests/unit/schema/test_schema_builder.py | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/python/semantic_kernel/schema/kernel_json_schema_builder.py b/python/semantic_kernel/schema/kernel_json_schema_builder.py index 5ec519b5b377..03b9e73fb473 100644 --- a/python/semantic_kernel/schema/kernel_json_schema_builder.py +++ b/python/semantic_kernel/schema/kernel_json_schema_builder.py @@ -18,6 +18,7 @@ dict: "object", set: "array", tuple: "array", + type(None): "null", "int": "integer", "str": "string", "bool": "boolean", @@ -28,6 +29,8 @@ "tuple": "array", "object": "object", "array": "array", + "None": "null", + "NoneType": "null", } diff --git a/python/tests/unit/schema/test_schema_builder.py b/python/tests/unit/schema/test_schema_builder.py index 5d24a599c96c..91c521150a71 100644 --- a/python/tests/unit/schema/test_schema_builder.py +++ b/python/tests/unit/schema/test_schema_builder.py @@ -228,6 +228,27 @@ def test_build_optional(): assert schema == {"type": ["integer", "null"]} +def test_get_json_schema_none_type(): + # NoneType maps to the JSON Schema "null" type, not "object". + assert KernelJsonSchemaBuilder.get_json_schema(type(None)) == {"type": "null"} + + +def test_build_none_type(): + assert KernelJsonSchemaBuilder.build(type(None)) == {"type": "null"} + + +def test_build_union_with_none(): + # A union of more than two members that includes None must represent the + # None member as {"type": "null"}, not {"type": "object"}. + schema = KernelJsonSchemaBuilder.build(Union[int, str, None]) + assert schema == {"anyOf": [{"type": "integer"}, {"type": "string"}, {"type": "null"}]} + + +def test_build_from_type_name_none(): + assert KernelJsonSchemaBuilder.build_from_type_name("None") == {"type": "null"} + assert KernelJsonSchemaBuilder.build_from_type_name("NoneType") == {"type": "null"} + + def test_build_model_schema_for_many_types(): schema = KernelJsonSchemaBuilder.build(MockModel) expected = """