Python: KernelJsonSchemaBuilder ignores string forward references inside list[...]/dict[...], emitting a bare {"type": "object"}
维护者通常 1 天内回复
@ErenAta16 已经在做这个了。
开始于 2026年7月29日。
评估
调研方向
从 semantic_kernel/schema/kernel_json_schema_builder.py 开始,重点检查 build_model_schema 和 handle_complex_type;查看容器的 args 是如何传入 build 的。使用 issue 中的 Holder 示例重现 list["Inner"] 和 list[Inner] 之间的差异,然后添加覆盖,展示等价的 item 和 additionalProperties schema。
由索引模型根据 Issue 内容生成。
描述
Describe the bug
KernelJsonSchemaBuilder silently drops the element schema when a container's element type is written as a string forward reference. list["Inner"] produces {"type": "object"} for the items — no properties, no required — while list[Inner] produces the full schema.
That schema is what gets sent to the model as a function-calling parameter definition, so a plugin using this annotation style hands the model an untyped blob for that argument.
To Reproduce
from semantic_kernel.kernel_pydantic import KernelBaseModel
from semantic_kernel.schema.kernel_json_schema_builder import KernelJsonSchemaBuilder as B
class Inner(KernelBaseModel):
value: int
label: str
class HolderDirect(KernelBaseModel):
items: list[Inner] = []
class HolderFwd(KernelBaseModel):
items: list["Inner"] = []
class HolderTop(KernelBaseModel):
one: "Inner"
| model | items / one schema |
|---|---|
HolderDirect |
{"type": "array", "items": {"type": "object", "properties": {"value": ..., "label": ...}, "required": [...]}} |
HolderFwd |
{"type": "array", "items": {"type": "object"}} |
HolderTop |
full Inner schema |
dict[str, "Inner"] |
additionalProperties: {"type": "object", "properties": {}} |
HolderTop works and HolderFwd doesn't, which is the part that makes this easy to miss — forward references look supported.
Why
HolderFwd.__annotations__["items"] -> list['Inner']
get_type_hints(HolderFwd)["items"] -> list['Inner'] # unchanged
get_args(...) -> ('Inner',) # a str, not a type
HolderFwd.model_fields["items"].annotation -> list['Inner'] # pydantic doesn't resolve it either
get_type_hints evaluates an annotation that is a string. It does not descend into a generic alias that already exists as an object and evaluate strings sitting in its __args__. list["Inner"] goes through list.__class_getitem__, which stores "Inner" verbatim — no ForwardRef wrapper, so there's nothing for get_type_hints to resolve. typing.Optional["Inner"] does wrap the string in a ForwardRef, which is why the top-level and Optional forms work.
handle_complex_type then calls cls.build("Inner", ...), which takes the isinstance(parameter_type, str) branch at the top of build and lands in build_from_type_name. No entry matches "Inner", so it returns the {"type": "object"} fallback:
KernelJsonSchemaBuilder.build("Inner") # -> {'type': 'object'}
Nothing raises, and {"type": "object"} is indistinguishable from a genuinely-unknown type downstream.
Expected behavior
list["Inner"] and list[Inner] produce the same schema.
Resolving str args against the owning model's module globals inside handle_complex_type before recursing would do it — the module globals are already fetched a few lines up in build_model_schema for the get_type_hints call.
Platform
- OS: Linux
- Python: 3.10.12
- Semantic Kernel:
main@ 7d885dd (python)
Additional context
Separate from the recursion work in #14198. That PR is about cycles; this fires on a plain non-recursive list["Inner"]. It's also the reason two of #14198's new tests currently fail — TreeNode and Author/Book reach their cycles through list[...], so the string never resolves to a class and the cycle detection never engages.
- 主要语言
- C#
- 星标
- 28.7k
- 派生
- 4.8k
- 平均合并
- 1 天 21 小时
- 30 天内合并 PR
- 28
环境准备
在浏览器里用你自己的 GitHub 账号启动这个项目的开发容器。
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
microsoft/semantic-kernel 的其他 Issue
-
Python: Rejected system messages leave AgentChat permanently active可能已有人在做 @Michael-Duck 于 4 天前认领。 未关闭python triage
难度 2/5 1-3 小时 新手友好度 82/100
microsoft/semantic-kernel#14638 ·
维护者通常 1 天内回复
-
Python: Bug: Anthropic, Bedrock, Google AI and Vertex AI tool formatters ignore include_in_function_choices=False可能已有人在做 @wasim-builds 于 9 天前认领。 未关闭python triage
难度 2/5 1-3 小时 新手友好度 72/100
microsoft/semantic-kernel#14615 ·
维护者通常 1 天内回复
-
Python: Bug: AnnotationContent and StreamingAnnotationContent to_element writes CitationType.X, so annotations in a rendered chat history fail to parse可能已有人在做 @CRYPTONIKAV 于 12 天前认领。 未关闭python triage
难度 2/5 1-3 小时 新手友好度 82/100
microsoft/semantic-kernel#14590 ·
维护者通常 1 天内回复
-
Python: Bug: TimePlugin timeZoneOffset and timeZoneName always return an empty string可能已有人在做 @jeevan1098 于 11 天前认领。 未关闭python triage
难度 2/5 1-3 小时 新手友好度 80/100
microsoft/semantic-kernel#14583 · 1 条评论 ·
维护者通常 1 天内回复
-
.Net: gpt-image-1 is the default image model in the .NET OpenAI connector, and OpenAI shuts it down on October 23可能已有人在做 @nightcityblade 于 23 天前认领。 未关闭.NET triage
难度 2/5 1-3 小时 新手友好度 73/100
microsoft/semantic-kernel#14526 · 1 条评论 ·
维护者通常 1 天内回复
查看 microsoft/semantic-kernel 的全部 Issue
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 64/100
HicServices/RDMP#2398 ·
-
is:bug is:FQNIssue
难度 2/5 1-3 小时 新手友好度 72/100
nunit/nunit3-vs-adapter#1527 ·
维护者通常 1 天内回复
-
难度 1/5 1 小时以内 新手友好度 72/100
维护者通常 1 天内回复
-
bug effort:S P3
难度 2/5 1-3 小时 新手友好度 72/100
nightscout/nocturne#2840 ·
维护者通常 1 天内回复
-
bug
难度 2/5 1-3 小时 新手友好度 68/100
fluentmigrator/fluentmigrator#2424 ·