Repository navigation
fix(python): map type(None) to null in kernel JSON schema builder - #14493
Aniket Kumar (aniketkrs) wants to merge 1 commit into
Conversation
PRABHU KIRAN VANDRANKI (VANDRANKI)
left a comment
There was a problem hiding this comment.
Community review, so this does not clear the merge gate.
I read the diff (one line in python/semantic_kernel/schema/kernel_json_schema_builder.py) and tested it in a local venv with semantic_kernel installed, by patching TYPE_MAPPING at runtime. I did not run the repo test suite.
Confirmed the bug on an unpatched install: KernelJsonSchemaBuilder.build(int | str | None) returns {'anyOf': [{'type': 'integer'}, {'type': 'string'}, {'type': 'object'}]}. The None branch is wrong. Optional[int] is fine ({'type': ['integer', 'null']}), as the description says.
With TYPE_MAPPING[type(None)] = "null" applied:
build(int | str | None)givesanyOfwith integer, string and null. Correct.build(list[int | None])still givesitems: {'type': ['integer', 'null']}. Unchanged and fine.build(type(None))gives{'type': 'null'}.
Two things to raise:
- The description says
dict[str, None]value schemas also improve fromobjecttonull. I did not see that. Before and after the change I got{'type': 'object', 'additionalProperties': {'type': 'object', 'properties': {}}}. So that claim looks wrong for this input, or it needs a different input than the one I used. Please either drop the claim or add a test that shows it. - The string path is not covered.
build_from_type_namelooks up type-name strings, and"None"is not a key, so it still maps toobject. I did not check whether_parse_parametercan ever hand a "None" string to the builder, because it skipsNoneTypeargs when it buildstype_, so this may be unreachable for decorated functions.
There is no test in the PR. A one-case test in tests/unit/schema/test_schema_builder.py asserting the int | str | None anyOf would lock the behavior in and take two minutes.
The fix is right for the main case. I would ask for a test and a corrected description.
Fixes #14482
User-facing impact: Agent tool schemas for parameters typed with multi-member unions like
int | str | NoneemittedanyOf: [integer, string, object]— theNonebranch became typeobjectinstead ofnull. Models receiving the wrong schema can emit{}wherenull/omission was intended, and downstream function-calling payloads break.Technical cause:
TYPE_MAPPINGin python/semantic_kernel/schema/kernel_json_schema_builder.py has no entry fortype(None), and bothget_json_schemaandbuild_from_type_namedefault unknown lookups to"object". Only the 2-argOptional[T]path is special-cased (into{"type": [T, "null"]}); for 3+ member unions, each arg goes throughcls.build(...)→get_json_schema(type(None))→{"type": "object"}.Fix: Add one entry to
TYPE_MAPPING:type(None): "null". Nowbuild(int | str | None)produces{"anyOf": [{"type": "integer"}, {"type": "string"}, {"type": "null"}]}, anddict[str, None]value schemas also improve fromobjecttonull.The
Optional[T]path is unaffected (it never consultsTYPE_MAPPINGfor None), and the existing tests in python/tests/unit/schema/test_schema_builder.py only assert the 2-arg form, so no behavior change for them.