Skip to content

Python: Exclude ClassVar annotations from generated JSON schemas - #14528

Open
Pushpak Siva Sai (Pushpak731) wants to merge 2 commits into
microsoft:mainfrom
Pushpak731:python-fix-classvar-schema
Open

Pushpak Siva Sai (Pushpak731) wants to merge 2 commits into
microsoft:mainfrom
Pushpak731:python-fix-classvar-schema

Conversation

@Pushpak731

Copy link
Copy Markdown

Fixes #14527

Description

KernelJsonSchemaBuilder.build_model_schema builds the schema from get_type_hints, which includes ClassVar annotations, so a class constant became a required object-valued property in the generated schema. Pydantic excludes ClassVars from model_fields and from serialized instances, so the resulting schema rejected valid serialized payloads ({'value': 1} failed because marker was required). The same wrong schema reached KernelParameterMetadata, generated function-call tool definitions and structured_output=True schemas.

The fix skips ClassVar-annotated hints before the required/property loop — covering the parameterized form (ClassVar[str]), the bare form (ClassVar, whose get_origin is None), and inherited ClassVars, for KernelBaseModel subclasses, plain annotated classes and model instances alike.

class Payload(KernelBaseModel):
    marker: ClassVar[str] = "constant"
    value: int

KernelParameterMetadata(name="payload", type_object=Payload).schema_data
# before: properties include marker (type object) and required: [marker, value]
# after:  {'properties': {'value': ...}, 'required': ['value']}

Contribution Checklist

  • I have read the contribution guidelines.
  • The code builds clean without any errors or warnings
  • The PR follows the SK Contribution Guidelines and the pre-submit framework
  • Tests are included and pass locally: tests/unit/schema/ (45 passed) and tests/unit/functions/test_kernel_parameter_metadata.py (7 passed) — 10 new regression tests cover pydantic models, bare/inherited ClassVar, plain classes, instances, structured_output, descriptions, optionals and the serialized-payload contract
  • ruff check and ruff format --check are clean on both changed files
  • Documentation updates are not needed (no public API change; behavior now matches model_dump)

KernelJsonSchemaBuilder.build_model_schema builds the instance schema
from get_type_hints, which includes ClassVar annotations, so a class
constant became a required object-valued property. Pydantic excludes
ClassVars from model fields and serialized instances, so the generated
schema rejected valid payloads (a serialized {'value': 1} failed
validation because 'marker' was required). The builder now skips
ClassVar-annotated hints (both the parameterized and bare forms,
including inherited ones), for KernelBaseModel subclasses, plain
annotated classes and instances, covering KernelParameterMetadata,
generated tool definitions and structured_output schemas.

Fixes microsoft#14527
Copilot AI balanced review requested due to automatic review settings October 2, 2026 18:46
@Pushpak731
Pushpak Siva Sai (Pushpak731) requested a review from a team as a code owner October 2, 2026 18:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Pushpak731

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

@semantic-kernel-automation semantic-kernel-automation Bot added the python Pull requests for the Python Semantic Kernel label Oct 2, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Community review. This does not clear the merge gate, it is one reader's check.

I reproduced the bug and checked the fix by running code. I loaded the installed semantic_kernel/schema/kernel_json_schema_builder.py (no ClassVar handling in it), then built a copy with your two added lines applied by string patch, and compared KernelJsonSchemaBuilder.build output for three classes.

  • class M(KernelBaseModel): marker: ClassVar[str] = "c"; value: int
    • current code: properties marker: {"type": "object"} and value, required ["marker", "value"]
    • with the patch: only value, required ["value"]
    • M(value=1).model_dump() is {"value": 1} and M.model_fields is ["value"], so the current schema demands a field the model never serializes, and it also types it wrongly as object.
  • Annotated[ClassVar[int], "x"] on a KernelBaseModel: same result, the patch drops it.
  • A plain (non-pydantic) class with a string annotation "ClassVar[dict]": also dropped, because get_type_hints resolves the string before the check.

The check field_type is ClassVar or get_origin(field_type) is ClassVar covers both the bare ClassVar and the subscripted form, which are the two shapes get_type_hints returns. It also fits with the loop: nothing after the continue depends on the skipped field, so required and properties stay consistent.

Notes:

  • I did not run the repo's test file or the full suite, only the builder on the three classes above. The new tests (inherited ClassVar, structured output with additionalProperties, via-instance) read correctly to me, but I did not execute them.
  • I did not check typing_extensions.ClassVar on older Pythons. It is the same object as typing.ClassVar on current versions, so I expect it to work, but that is unverified.
  • Nit: the ten new tests overlap a lot. Two or three would cover the same branches (bare, subscripted, inherited).

Approving from my side.

This branch was successfully deployed

1 active deployment
github-app-auth — 71390565 Deployed Oct 2, 2026 by Pushpak731 via add_label #29227
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Pull requests for the Python Semantic Kernel

Projects

None yet

Development

Successfully merging this pull request may close these issues.

.Net: Python: Bug: ClassVar annotations become required fields in generated tool schemas

3 participants