Description
KernelJsonSchemaBuilder maps NoneType to JSON type "object" in multi-member unions. A type like int | str | None becomes anyOf: [integer, string, object] instead of including "null".
This breaks function-calling / agent tool schemas for pydantic model parameters: models may emit {} where null/omit was intended.
Distinct from open #14443 (Ge constraint objects in descriptions).
Environment
semantic-kernel==1.44.1 (Python)
- Python 3.13
Minimal repro
import json
from typing import Annotated
from pydantic import BaseModel, Field
from semantic_kernel.connectors.ai.function_calling_utils import (
kernel_function_metadata_to_function_call_format,
)
from semantic_kernel.functions import KernelPlugin, kernel_function
from semantic_kernel.schema.kernel_json_schema_builder import KernelJsonSchemaBuilder
direct = KernelJsonSchemaBuilder.build(int | str | None)
print(json.dumps(direct))
# {"anyOf": [{"type": "integer"}, {"type": "string"}, {"type": "object"}]}
# null missing; object stand-in for None
class Payload(BaseModel):
value: int | str | None = Field(default=None, description="flexible")
@kernel_function(name="handle")
def handle(payload: Annotated[Payload, "payload"]) -> str:
"""handle payload"""
return str(payload)
fn = KernelPlugin(name="P", functions=[handle])["handle"]
fmt = kernel_function_metadata_to_function_call_format(fn.metadata)
print(json.dumps(fmt["function"]["parameters"]["properties"]["payload"]["properties"]["value"]))
# anyOf includes type object for None
Expected
None in unions should map to JSON Schema "null" (or an equivalent nullable form), not "object".
Actual
anyOf includes {"type": "object"} and no "null".
Root cause (pointer)
semantic_kernel/schema/kernel_json_schema_builder.py — TYPE_MAPPING / get_json_schema default "object"; handle_complex_type Union branch only special-cases 2-arg Optional, not multi-member unions containing None.
Suggested fix direction
Map type(None) → {"type": "null"} in TYPE_MAPPING/get_json_schema, and/or strip None from multi-unions into a proper nullable / anyOf+null form.
Description
KernelJsonSchemaBuildermapsNoneTypeto JSON type"object"in multi-member unions. A type likeint | str | NonebecomesanyOf: [integer, string, object]instead of including"null".This breaks function-calling / agent tool schemas for pydantic model parameters: models may emit
{}wherenull/omit was intended.Distinct from open #14443 (Ge constraint objects in descriptions).
Environment
semantic-kernel==1.44.1(Python)Minimal repro
Expected
Nonein unions should map to JSON Schema"null"(or an equivalent nullable form), not"object".Actual
anyOfincludes{"type": "object"}and no"null".Root cause (pointer)
semantic_kernel/schema/kernel_json_schema_builder.py—TYPE_MAPPING/get_json_schemadefault"object";handle_complex_typeUnion branch only special-cases 2-arg Optional, not multi-member unions containingNone.Suggested fix direction
Map
type(None)→{"type": "null"}inTYPE_MAPPING/get_json_schema, and/or stripNonefrom multi-unions into a proper nullable /anyOf+null form.