[Fix] Require JSON booleans for response_format json_schema.strict (#34777)
Co-authored-by: James Liu <jamesl@modal.com>
This commit is contained in:
@@ -53,6 +53,7 @@ from pydantic import (
|
|||||||
BaseModel,
|
BaseModel,
|
||||||
ConfigDict,
|
ConfigDict,
|
||||||
Field,
|
Field,
|
||||||
|
StrictBool,
|
||||||
field_serializer,
|
field_serializer,
|
||||||
field_validator,
|
field_validator,
|
||||||
model_serializer,
|
model_serializer,
|
||||||
@@ -219,7 +220,10 @@ class JsonSchemaResponseFormat(BaseModel):
|
|||||||
description: Optional[str] = None
|
description: Optional[str] = None
|
||||||
# use alias to workaround pydantic conflict
|
# use alias to workaround pydantic conflict
|
||||||
schema_: Optional[Dict[str, object]] = Field(alias="schema", default=None)
|
schema_: Optional[Dict[str, object]] = Field(alias="schema", default=None)
|
||||||
strict: Optional[bool] = None
|
# The OpenAI wire contract accepts JSON booleans only; StrictBool rejects
|
||||||
|
# the values lax pydantic would coerce ("yes", "on", 0, 1, ...), matching
|
||||||
|
# OpenAI's 422 behavior. Omitted (None) keeps its meaning.
|
||||||
|
strict: Optional[StrictBool] = None
|
||||||
|
|
||||||
|
|
||||||
class ResponseFormat(BaseModel):
|
class ResponseFormat(BaseModel):
|
||||||
|
|||||||
@@ -114,6 +114,42 @@ class TestCompletionRequest(unittest.TestCase):
|
|||||||
class TestChatCompletionRequest(unittest.TestCase):
|
class TestChatCompletionRequest(unittest.TestCase):
|
||||||
"""Test ChatCompletionRequest protocol model"""
|
"""Test ChatCompletionRequest protocol model"""
|
||||||
|
|
||||||
|
def test_json_schema_strict_requires_json_boolean(self):
|
||||||
|
base_request = {
|
||||||
|
"model": "test-model",
|
||||||
|
"messages": [{"role": "user", "content": "Hello"}],
|
||||||
|
"response_format": {
|
||||||
|
"type": "json_schema",
|
||||||
|
"json_schema": {
|
||||||
|
"name": "answer",
|
||||||
|
"schema": {"type": "object"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for strict in (True, False, None):
|
||||||
|
with self.subTest(strict=strict):
|
||||||
|
response_format = dict(base_request["response_format"])
|
||||||
|
response_format["json_schema"] = {
|
||||||
|
**response_format["json_schema"],
|
||||||
|
"strict": strict,
|
||||||
|
}
|
||||||
|
request = ChatCompletionRequest.model_validate(
|
||||||
|
{**base_request, "response_format": response_format}
|
||||||
|
)
|
||||||
|
self.assertIs(request.response_format.json_schema.strict, strict)
|
||||||
|
|
||||||
|
for strict in ("yes", "false", 0, 1):
|
||||||
|
with self.subTest(strict=strict), self.assertRaises(ValidationError):
|
||||||
|
response_format = dict(base_request["response_format"])
|
||||||
|
response_format["json_schema"] = {
|
||||||
|
**response_format["json_schema"],
|
||||||
|
"strict": strict,
|
||||||
|
}
|
||||||
|
ChatCompletionRequest.model_validate(
|
||||||
|
{**base_request, "response_format": response_format}
|
||||||
|
)
|
||||||
|
|
||||||
def test_basic_chat_completion_request(self):
|
def test_basic_chat_completion_request(self):
|
||||||
"""Test basic chat completion request"""
|
"""Test basic chat completion request"""
|
||||||
messages = [{"role": "user", "content": "Hello"}]
|
messages = [{"role": "user", "content": "Hello"}]
|
||||||
|
|||||||
Reference in New Issue
Block a user