Repository navigation
.Net: Python: KernelJsonSchemaBuilder emits {"type": "object"} with no enum for typing.Literal parameters and fields #14511
Description
What happens
KernelJsonSchemaBuilder has no handling for typing.Literal. A parameter typed Literal["fast", "slow"] is advertised to the model as a bare {"type": "object"} with no enum. The model is told the argument is an object and is never told the allowed values.
This affects @kernel_function parameters, Pydantic model fields, and Literal nested inside list[...] or Optional[...].
Where
python/semantic_kernel/schema/kernel_json_schema_builder.py, KernelJsonSchemaBuilder.handle_complex_type() and get_json_schema().
Literal[...] has __args__, so build() sends it to handle_complex_type(). That method handles list, set, dict, tuple and Union, but not Literal, so it reaches the final fallback:
schema = cls.get_json_schema(parameter_type) # TYPE_MAPPING.get(Literal[...], "object")TYPE_MAPPING has no entry for a Literal alias, so the default "object" is returned. build_enum_schema() already produces the right shape for Enum classes ({"type": ..., "enum": [...]}), but nothing does that for Literal.
Repro
Tested with semantic-kernel==1.44.1. I diffed kernel_json_schema_builder.py against current main and the file is identical.
import json
from typing import Annotated, Literal
from pydantic import BaseModel
from semantic_kernel import Kernel
from semantic_kernel.functions import kernel_function
from semantic_kernel.connectors.ai.function_calling_utils import kernel_function_metadata_to_function_call_format
class Opts(BaseModel):
level: Literal["low", "high"]
class Plugin:
@kernel_function(name="run")
def run(self, mode: Annotated[Literal["fast", "slow"], "speed mode"], opts: Annotated[Opts, "options"]) -> str:
return mode
k = Kernel(); k.add_plugin(Plugin(), "p")
fn = k.get_function("p", "run")
print(json.dumps(kernel_function_metadata_to_function_call_format(fn.metadata)["function"]["parameters"], indent=1))Output:
{
"type": "object",
"properties": {
"mode": {"type": "object", "description": "speed mode"},
"opts": {
"type": "object",
"properties": {"level": {"type": "object"}},
"required": ["level"],
"description": "options"
}
},
"required": ["mode", "opts"]
}Direct builder calls:
KernelJsonSchemaBuilder.build(Literal["fast", "slow"]) # {'type': 'object'}
KernelJsonSchemaBuilder.build(Optional[Literal["fast", "slow"]]) # {'type': ['object', 'null']}
KernelJsonSchemaBuilder.build(list[Literal["a", "b"]]) # {'type': 'array', 'items': {'type': 'object'}}Expected
{"type": "string", "enum": ["fast", "slow"], "description": "speed mode"}This is the same shape build_enum_schema() already returns for an Enum subclass. Optional[Literal[...]] should be nullable and list[Literal[...]] should have an enum on items.
Actual
{"type": "object"} with no allowed values. The model can emit any value, including a JSON object, and the kernel then tries to bind it to a str parameter. Tools that use Literal to constrain choices (a very common pattern) lose that constraint entirely.
Suggested fix
Add a Literal branch in handle_complex_type():
if origin is Literal:
values = list(args)
types = {TYPE_MAPPING.get(type(v), "string") for v in values}
schema = {"type": types.pop() if len(types) == 1 else sorted(types), "enum": values}
if description:
schema["description"] = description
return schemaThe Optional[...] branch also needs to keep working with an enum schema (add None to the enum when making it nullable).
I searched open and closed issues and PRs for Literal, KernelJsonSchemaBuilder, and schema builder problems. #14239, #14241 and #14310 are about string forward references and only make sure Literal values are left alone while resolving them. #14482 and #14155 are about NoneType in unions. None of them adds Literal support.
Activity
[-]Python: KernelJsonSchemaBuilder emits {"type": "object"} with no enum for typing.Literal parameters and fields[/-][+].Net: Python: KernelJsonSchemaBuilder emits {"type": "object"} with no enum for typing.Literal parameters and fields[/+]Python: add typing.Literal support to KernelJsonSchemaBuilder (micros…