Describe the issue as clearly as possible:
Dict[str, V] doesn't survive a JSON-Schema round trip. schema_type_to_python treats any {"type": "object"} as a nested model and ignores additionalProperties, so a mapping field comes back as a generated model instead of Dict[str, V]. The generated class is named from the schema title, which for a field like by_user is "By User" — a name with a space in it, not a valid Python identifier.
That means the reconstructed type accepts a fixed (empty) set of keys rather than arbitrary string keys, which is the opposite of what the original annotation meant.
Steps/code to reproduce the bug:
from typing import Dict
from pydantic import BaseModel
from outlines.types.json_schema_utils import (
json_schema_dict_to_pydantic,
json_schema_dict_to_dataclass,
)
class Scores(BaseModel):
by_user: Dict[str, int] = {}
schema = Scores.model_json_schema()
# {"properties": {"by_user": {"additionalProperties": {"type": "integer"},
# "default": {}, "title": "By User", "type": "object"}}, ...}
json_schema_dict_to_pydantic(schema).model_fields["by_user"].annotation
json_schema_dict_to_dataclass(schema)
Expected result:
Error message:
No error — it degrades quietly:
| converter |
result |
json_schema_dict_to_pydantic |
Optional[outlines.types.json_schema_utils.By User] |
json_schema_dict_to_dataclass |
<class 'outlines.types.json_schema_utils.By User'> |
json_schema_dict_to_typeddict |
NotRequired[outlines.types.json_schema_utils.By User] |
"By User".isidentifier() # False
The generated model has no fields, so nothing in the original mapping's value type survives either — int is gone.
List[str] in the same model round-trips correctly to Optional[List[str]], so this is specific to the additionalProperties shape.
Outlines/Python version information:
outlines @ 13fe8dd (main), Python 3.10.12, pydantic 2.12.5
Context for the maintainers:
schema_type_to_python's t == "object" branch goes straight to json_schema_dict_to_{pydantic,typeddict,dataclass} without looking at additionalProperties. Reading it first — mapping {"type": "object", "additionalProperties": S} with no properties to Dict[str, schema_type_to_python(S)] — would cover it.
Filing this separately because #1908, #1949, #1951 and #1963 are all open against schema_type_to_python and none of them touch additionalProperties; I checked each of them against this repro.
Describe the issue as clearly as possible:
Dict[str, V]doesn't survive a JSON-Schema round trip.schema_type_to_pythontreats any{"type": "object"}as a nested model and ignoresadditionalProperties, so a mapping field comes back as a generated model instead ofDict[str, V]. The generated class is named from the schematitle, which for a field likeby_useris"By User"— a name with a space in it, not a valid Python identifier.That means the reconstructed type accepts a fixed (empty) set of keys rather than arbitrary string keys, which is the opposite of what the original annotation meant.
Steps/code to reproduce the bug:
Expected result:
Error message:
No error — it degrades quietly:
json_schema_dict_to_pydanticOptional[outlines.types.json_schema_utils.By User]json_schema_dict_to_dataclass<class 'outlines.types.json_schema_utils.By User'>json_schema_dict_to_typeddictNotRequired[outlines.types.json_schema_utils.By User]The generated model has no fields, so nothing in the original mapping's value type survives either —
intis gone.List[str]in the same model round-trips correctly toOptional[List[str]], so this is specific to theadditionalPropertiesshape.Outlines/Python version information:
outlines@ 13fe8dd (main), Python 3.10.12, pydantic 2.12.5Context for the maintainers:
schema_type_to_python'st == "object"branch goes straight tojson_schema_dict_to_{pydantic,typeddict,dataclass}without looking atadditionalProperties. Reading it first — mapping{"type": "object", "additionalProperties": S}with nopropertiestoDict[str, schema_type_to_python(S)]— would cover it.Filing this separately because #1908, #1949, #1951 and #1963 are all open against
schema_type_to_pythonand none of them touchadditionalProperties; I checked each of them against this repro.