Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 81 additions & 2 deletions src/mistralai/extra/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
response_format_from_pydantic_model,
rec_strict_json_schema,
)
from pydantic import BaseModel, ValidationError
from pydantic import BaseModel, Field, ValidationError

from ...models import ResponseFormat, JSONSchema
from ...types.basemodel import Unset
Expand Down Expand Up @@ -149,11 +149,90 @@ def test_response_format_from_pydantic_model(self):
)

def test_rec_strict_json_schema(self):
invalid_schema = mathdemo_schema | {"wrong_value": 1}
self.assertEqual(
rec_strict_json_schema(mathdemo_schema), mathdemo_strict_schema
)

def test_rec_strict_json_schema_with_numeric_constraints(self):
"""
Test that rec_strict_json_schema handles JSON Schema constraint keywords
that have numeric values (e.g., minLength, maxLength, minItems, maxItems).

This is a regression test for issue #300 where Pydantic models with
constraint keywords like min_length would cause a ValueError.
"""
# Schema with numeric constraint values (minItems, maxItems, minimum, etc.)
schema_with_constraints = {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {"type": "integer"},
"minItems": 1,
"maxItems": 10,
},
"name": {
"type": "string",
"minLength": 1,
"maxLength": 100,
},
"count": {
"type": "number",
"minimum": 0,
"maximum": 1000,
"multipleOf": 0.5, # float value
},
},
"required": ["items", "name"],
}

# Should not raise ValueError - integers and floats are valid terminal values
result = rec_strict_json_schema(schema_with_constraints)

# Check that additionalProperties was added
self.assertEqual(result["additionalProperties"], False)
# Check that numeric constraints are preserved
self.assertEqual(result["properties"]["items"]["minItems"], 1)
self.assertEqual(result["properties"]["items"]["maxItems"], 10)
self.assertEqual(result["properties"]["name"]["minLength"], 1)
self.assertEqual(result["properties"]["count"]["multipleOf"], 0.5)

def test_response_format_with_constrained_pydantic_model(self):
"""
Test that response_format_from_pydantic_model works with Pydantic models
that use constraint keywords like min_length.

This is a regression test for issue #300.
"""

class ModelWithConstraints(BaseModel):
some_list: list[int] = Field(
default_factory=list,
description="A list of integers",
min_length=1,
)
name: str = Field(
description="A name",
min_length=1,
max_length=100,
)

# Should not raise ValueError
result = response_format_from_pydantic_model(ModelWithConstraints)

# Verify it returns a valid ResponseFormat
self.assertIsInstance(result, ResponseFormat)
self.assertEqual(result.type, "json_schema")
self.assertIsNotNone(result.json_schema)

def test_rec_strict_json_schema_with_invalid_type(self):
"""Test that rec_strict_json_schema raises ValueError for truly invalid types."""
# A custom object that is not a valid JSON schema node type
class CustomObject:
pass

invalid_schema = {"invalid": CustomObject()}

with self.assertRaises(ValueError):
rec_strict_json_schema(invalid_schema)

Expand Down
4 changes: 3 additions & 1 deletion src/mistralai/extra/utils/_pydantic_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ def rec_strict_json_schema(schema_node: Any) -> Any:
Recursively set the additionalProperties property to False for all objects in the JSON Schema.
This makes the JSON Schema strict (i.e. no additional properties are allowed).
"""
if isinstance(schema_node, (str, bool)) or schema_node is None:
# Include int and float as terminal types to handle JSON Schema constraint keywords
# like minLength, maxLength, minItems, maxItems, minimum, maximum, etc.
if isinstance(schema_node, (str, bool, int, float)) or schema_node is None:
return schema_node
if isinstance(schema_node, dict):
if "type" in schema_node and schema_node["type"] == "object":
Expand Down