Python: KernelJsonSchemaBuilder ignores string forward references inside list[...]/dict[...], emitting a bare {"type": "object"}
Los mantenedores suelen responder en 2 días
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 3/5
- Tiempo estimado
- 1-2 días
- Aptitud para principiantes
- 76/100
Línea de trabajo
Comienza en semantic_kernel/schema/kernel_json_schema_builder.py, especialmente en build_model_schema y handle_complex_type; inspecciona cómo se pasan los args de los contenedores a build. Reproduce la diferencia entre list["Inner"] y list[Inner] usando los ejemplos de Holder del issue y luego añade cobertura que muestre esquemas equivalentes de item y additionalProperties.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
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.
- Lenguaje dominante
- C#
- Estrellas
- 28.6k
- Forks
- 4.8k
- Merge medio
- 13 h 24 min
- PR fusionados (30 d)
- 11
Preparar el entorno
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de microsoft/semantic-kernel
-
python triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
microsoft/semantic-kernel#14491 · 1 comentario ·
Los mantenedores suelen responder en 2 días
-
python triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
microsoft/semantic-kernel#14490 · 1 comentario ·
Los mantenedores suelen responder en 2 días
-
Python: [Python] structured_outputs_transform reuses ChatHistory across calls (prompt pollution)Abiertopython triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
microsoft/semantic-kernel#14483 · 2 comentarios ·
Los mantenedores suelen responder en 2 días
-
.NET python triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
microsoft/semantic-kernel#14482 · 3 comentarios ·
Los mantenedores suelen responder en 2 días
-
Python: [Python] as_agent_framework_tool drops parameter defaults (optionals become required)Abiertopython triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
microsoft/semantic-kernel#14481 ·
Los mantenedores suelen responder en 2 días
Todos los issues de microsoft/semantic-kernel
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
NethermindEth/nethermind#14012 ·
Los mantenedores suelen responder en 1 día
-
dependencies Status: Triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
json-schema-org/website#2518 ·
Los mantenedores suelen responder en 1 día
-
agentic-workflows
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
builtbybel/Flyoobe#498 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
builtbybel/CrapFixer#112 ·