Skip to content

.Net: Python: KernelJsonSchemaBuilder emits {"type": "object"} with no enum for typing.Literal parameters and fields #14511

Closed

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 schema

The 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

  1. changed the title [-]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[/+] on Sep 30, 2026
  2. added a commit that references this issue on Oct 26, 2026
    2281d4d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    .NETIssue or Pull requests regarding .NET codepythonPull requests for the Python Semantic Kerneltriage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions