Skip to content

[BUG CLIENT]: JSONSchema Serialization Inconsistency with Alias Field #310

@tiborrr

Description

@tiborrr

Python -VV

Python 3.13.7 (main, Sep 25 2025, 21:20:20) [Clang 17.0.0 (clang-1700.3.19.1)]

Pip Freeze

aiofiles==25.1.0
alembic==1.17.2
annotated-doc==0.0.4
annotated-types==0.7.0
anyio==4.11.0
appdirs==1.4.4
APScheduler==3.11.1
argon2-cffi==25.1.0
argon2-cffi-bindings==25.1.0
async-property==0.2.2
certifi==2025.8.3
cffi==2.0.0
charset-normalizer==3.4.3
click==8.3.0
cryptography==46.0.1
debugpy==1.8.19
Deprecated==1.2.18
deprecation==2.1.0
distro==1.9.0
dnspython==2.8.0
ecdsa==0.19.1
elementpath==5.0.4
email-validator==2.3.0
eval_type_backport==0.2.2
fastapi==0.121.3
frozendict==2.4.6
h11==0.16.0
httpcore==1.0.9
httpx==0.28.1
idna==3.10
img2pdf==0.6.1
iniconfig==2.3.0
invoke==2.2.1
Jinja2==3.1.6
jwcrypto==1.5.6
lxml==6.0.2
Mako==1.3.10
markdown-it-py==4.0.0
MarkupSafe==3.0.3
mdurl==0.1.2
minio==7.2.18
mistralai==1.9.11
mollie-api-python==3.9.1
oauthlib==3.3.1
ocrmypdf==16.11.0
packaging==25.0
pdfminer.six==20250506
pi_heif==1.1.0
pikepdf==9.11.0
pillow==11.3.0
pluggy==1.6.0
pyasn1==0.6.1
pycparser==2.23
pycryptodome==3.23.0
pydantic==2.11.10
pydantic-settings==2.12.0
pydantic-xml==2.18.0
pydantic_core==2.33.2
Pygments==2.19.2
pyschematron==1.1.13
pytest==9.0.1
pytest-asyncio==1.3.0
python-dateutil==2.9.0.post0
python-dotenv==1.2.1
python-jose==3.5.0
python-keycloak==5.8.1
python-multipart==0.0.20
PyYAML==6.0.3
requests==2.32.5
requests-oauthlib==2.0.0
requests-toolbelt==1.0.0
rich==14.1.0
rsa==4.9.1
ruyaml==0.91.0
setuptools==80.9.0
shellingham==1.5.4
six==1.17.0
sniffio==1.3.1
SQLAlchemy==2.0.43
sqlmodel==0.0.27
starlette==0.48.0
typer==0.19.2
typing-inspection==0.4.2
typing_extensions==4.15.0
tzlocal==5.3.1
urllib3==2.5.0
uvicorn==0.38.0
wrapt==1.17.3
xmlschema==4.1.0

Reproduction Steps

  1. Create a simple Pydantic model:
from mistralai.extra import response_format_from_pydantic_model
from pydantic import BaseModel, Field
import json

class TestModel(BaseModel):
    name: str = Field(description="A name field")
    value: int = Field(description="A value field")
  1. Generate a ResponseFormat using the SDK function:
response_format = response_format_from_pydantic_model(TestModel)
  1. Serialize using model_dump() without by_alias=True:
serialized = response_format.model_dump(mode='json')
print(json.dumps(serialized, indent=2))
  1. Observe that json_schema.schema is null instead of containing the schema definition.

  2. Compare with model_dump(by_alias=True):

serialized_with_alias = response_format.model_dump(mode='json', by_alias=True)
print(json.dumps(serialized_with_alias, indent=2))

Expected Behavior

hen calling model_dump() on a ResponseFormat object created with response_format_from_pydantic_model(), the json_schema.schema field should contain the actual JSON schema definition, not null. The serialization should work consistently regardless of whether by_alias=True is used.

The expected output should be:

{
  "type": "json_schema",
  "json_schema": {
    "name": "TestModel",
    "schema": {
      "type": "object",
      "properties": {
        "name": {"type": "string", "description": "A name field"},
        "value": {"type": "integer", "description": "A value field"}
      },
      "required": ["name", "value"],
      "additionalProperties": false
    },
    "strict": true
  }
}

Additional Context

Current Behavior:

  • model_dump() shows schema: null
  • model_dump(by_alias=True) correctly shows the schema
  • Actual API calls succeed because the SDK's marshal_json() function (used for HTTP requests) internally calls model_dump(by_alias=True)

Root Cause:
The issue is in JSONSchema.model_serializer (located in mistralai/models/jsonschema.py). The serializer attempts to retrieve the schema_definition field using its alias "schema", but handler(self) returns a dict with the field name "schema_definition" instead of the alias when by_alias=False:

@model_serializer(mode="wrap")
def serialize_model(self, handler):
    serialized = handler(self)  # Returns {"schema_definition": {...}, ...}
    
    for n, f in type(self).model_fields.items():
        k = f.alias or n  # k = "schema" (alias)
        val = serialized.get(k)  # Returns None because key is "schema_definition", not "schema"

Impact:
While API calls work, this inconsistency causes confusion for:

  • Debugging/inspecting serialized data
  • Logging request payloads
  • Testing code that relies on model_dump()
  • Potential breakage if HTTP serialization method changes

Suggested Solutions

Suggested Fix:
The JSONSchema.model_serializer should check for both the alias key and the field name:

@model_serializer(mode="wrap")
def serialize_model(self, handler):
    serialized = handler(self)
    
    for n, f in type(self).model_fields.items():
        k = f.alias or n
        # Try alias first, then field name as fallback
        val = serialized.get(k) or serialized.get(n)
        # ... rest of logic ...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions