Repository navigation
Python: Map NoneType to JSON Schema "null" instead of "object" in schema builder - #14155
Shaurya Singh (LeSingh1) wants to merge 2 commits into
Conversation
…ema 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.
There was a problem hiding this comment.
Pull request overview
This PR fixes Python JSON Schema generation for NoneType in KernelJsonSchemaBuilder, ensuring None is represented as JSON Schema {"type": "null"} (instead of defaulting to {"type": "object"}) when encountered directly or as a member of larger unions used for function calling / structured output.
Changes:
- Add
type(None)toTYPE_MAPPINGsoget_json_schema(type(None))and union-member schema generation emit"null". - Add
"None"and"NoneType"string mappings sobuild_from_type_name(...)correctly emits"null". - Add regression tests covering
type(None),build(type(None)),Union[int, str, None], and"None"/"NoneType"type-name paths.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| python/semantic_kernel/schema/kernel_json_schema_builder.py | Maps type(None) / "None" / "NoneType" to JSON Schema "null" via TYPE_MAPPING. |
| python/tests/unit/schema/test_schema_builder.py | Adds regression tests for direct NoneType and 3+ member unions containing None. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
This fix does more than the description claims, and it's worth recording because it strengthens the case for merging — but it does not finish the job for the structured-output path. Everything below is measured on 1. The PR removes a hard vendor rejection, not just a misleading For
The pre-fix error is:
Note it is phrased in Zod vocabulary. A Python user who hits this has no 2. But the field is still dropped from
The vendor transformer now accepts this, but only because it repairs it — it rewrites That repair does not happen on this path. Worth contrasting with case 1 of that same function: a 3. The regression test will pass either way on this point. All four added tests assert on a member/union fragment ( 4. Two more
|
Motivation
KernelJsonSchemaBuilderomittedNoneTypefromTYPE_MAPPING, soget_json_schema(type(None))— and any union of 3+ members that includesNone(e.g.int | str | None) — emitted{"type": "object"}for theNonemember instead of the correct{"type": "null"}. These schemas are sent to models during function calling / structured output, where anobjecttype for aNonemember is misleading and can distort tool arguments.The two-member
Optional[T]path builds nullability separately and was already correct; this only affectedNoneTypereached directly or as one member of a 3+-member union.Fix
Add
NoneType(and the"None"/"NoneType"string names used bybuild_from_type_name) toTYPE_MAPPING, soNonemaps to JSON Schema{"type": "null"}.Files
python/semantic_kernel/schema/kernel_json_schema_builder.pypython/tests/unit/schema/test_schema_builder.py— regression testsVerification
Added regression tests covering
type(None)directly and a 3-member union containingNone; they fail before the change ({"type":"object"}) and pass after ({"type":"null"}). ExistingOptional[T]behavior is unchanged.