-
Notifications
You must be signed in to change notification settings - Fork 7.6k
fix: check model support before sending json_schema response_format #6357
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -723,16 +723,19 @@ def _prepare_responses_params( | |
| if response_model or self.response_format: | ||
| format_model = response_model or self.response_format | ||
| if isinstance(format_model, type) and issubclass(format_model, BaseModel): | ||
| schema_output = generate_model_description(format_model) | ||
| json_schema = schema_output.get("json_schema", {}) | ||
| params["text"] = { | ||
| "format": { | ||
| "type": "json_schema", | ||
| "name": json_schema.get("name", format_model.__name__), | ||
| "strict": json_schema.get("strict", True), | ||
| "schema": json_schema.get("schema", {}), | ||
| if self._supports_structured_output(): | ||
| schema_output = generate_model_description(format_model) | ||
| json_schema = schema_output.get("json_schema", {}) | ||
| params["text"] = { | ||
| "format": { | ||
| "type": "json_schema", | ||
| "name": json_schema.get("name", format_model.__name__), | ||
| "strict": json_schema.get("strict", True), | ||
| "schema": json_schema.get("schema", {}), | ||
| } | ||
| } | ||
| } | ||
| else: | ||
| params["text"] = {"format": {"type": "json_object"}} | ||
| elif isinstance(format_model, dict): | ||
| params["text"] = {"format": format_model} | ||
|
|
||
|
|
@@ -1573,9 +1576,12 @@ def _prepare_completion_params( | |
| if isinstance(self.response_format, type) and issubclass( | ||
| self.response_format, BaseModel | ||
| ): | ||
| params["response_format"] = generate_model_description( | ||
| self.response_format | ||
| ) | ||
| if self._supports_structured_output(): | ||
| params["response_format"] = generate_model_description( | ||
| self.response_format | ||
| ) | ||
| else: | ||
| params["response_format"] = {"type": "json_object"} | ||
| elif isinstance(self.response_format, dict): | ||
| params["response_format"] = self.response_format | ||
|
|
||
|
|
@@ -2377,6 +2383,20 @@ async def _ahandle_streaming_completion( | |
| response_id=stream_response_id, | ||
| ) | ||
|
|
||
| def _supports_structured_output(self) -> bool: | ||
| """Whether the model supports json_schema response_format type.""" | ||
| model_lower = self.model.lower() if self.model else "" | ||
|
|
||
| unsupported_prefixes = ("o1-preview", "o1-mini", "gpt-4-base", "gpt-3.5", "gpt-35") | ||
| if model_lower.startswith(unsupported_prefixes): | ||
| return False | ||
|
|
||
| supported_prefixes = ("gpt-4o", "gpt-4-turbo", "gpt-4.1", "gpt-5", "o1", "o3", "o4") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor 🧩 Analysis chain🌐 Web query:
💡 Result: No, OpenAI's gpt-4-turbo does not support the json_schema structured outputs response_format [1][2][3]. While Structured Outputs as a capability is generally available in the API, its support is divided into two distinct methods [1][2]: 1. Structured Outputs via Function Calling: This method is supported on a wide range of models, including gpt-4-turbo, gpt-4, and gpt-3.5-turbo, for all models that already supported function calling [2][3]. 2. Structured Outputs via response_format (using json_schema): This specific implementation is restricted to newer models, starting with gpt-4o-2024-08-06 and gpt-4o-mini-2024-07-18 [1][2][3]. If you are using gpt-4-turbo, you cannot use the { "type": "json_schema",... } response format directly [1]. Instead, for gpt-4-turbo, you may use JSON mode by setting response_format to { "type": "json_object" } (which ensures valid JSON but does not enforce a specific schema) or continue using function calling if you require strict schema adherence [1][3]. Citations:
Remove OpenAI's Original Snippet supported_prefixes = ("gpt-4o", "gpt-4-turbo", "gpt-4.1", "gpt-5", "o1", "o3", "o4")🤖 Prompt for AI Agents |
||
| if model_lower.startswith(supported_prefixes): | ||
| return True | ||
|
|
||
| return True | ||
|
|
||
|
Comment on lines
+2386
to
+2399
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Confirm the response_model -> beta.parse path is unguarded by _supports_structured_output
rg -nP -C3 'beta\.chat\.completions\.(parse|stream)\(' lib/crewai/src/crewai/llms/providers/openai/completion.py
# Confirm litellm compat helper is available to reuse
rg -nP 'supports_response_schema' lib/crewai/src/crewai/llm.pyRepository: crewAIInc/crewAI Length of output: 1648 Guard structured output with a capability check and remove dead allowlist The Use Suggested changes:
Replace lines 2386–2399 def _supports_structured_output(self) -> bool:
"""Whether the model supports json_schema response_format type."""
global supports_response_schema
if not _ensure_litellm() or supports_response_schema is None:
# Fallback: deny known incompatible prefixes
model_lower = self.model.lower() if self.model else ""
unsupported_prefixes = ("o1-preview", "o1-mini", "gpt-4-base", "gpt-3.5", "gpt-35")
return not model_lower.startswith(unsupported_prefixes)
return bool(supports_response_schema(self.model))
Apply guards at the call sitesFor each occurrence around lines 1649–1651, 1922–1923, and 2070–2073:
This prevents mis-routed 🤖 Prompt for AI Agents |
||
| def supports_function_calling(self) -> bool: | ||
| """Check if the model supports function calling.""" | ||
| return not self.is_o1_model | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟡 Minor
🧩 Analysis chain
🌐 Web query:
OpenAI chat completions response_format json_object requires messages contain the word json💡 Result:
Yes, when using the OpenAI Chat Completions API with response_format set to {"type": "json_object"}, it is a strict requirement that your messages (which includes the system, user, or assistant messages) contain the word "json" (or "JSON") [1][2][3]. If this requirement is not met, the API will return a 400 error stating that the messages must contain the word "json" in some form [4][5][6][3]. This safeguard exists because, without an explicit instruction to produce JSON, the model may fail to output a valid JSON object, potentially resulting in an infinite stream of whitespace or other malformed outputs [2][6][3]. To satisfy this requirement, you should include a clear instruction in your messages—typically in the system prompt—such as "You are a helpful assistant designed to output JSON" or "Respond in JSON format" [1][2][3]. Note that while this "JSON mode" (
json_object) is a common way to generate JSON, OpenAI now recommends using Structured Outputs (type: "json_schema") for applications that require guaranteed adherence to a specific schema [2][7][8][3]. Structured Outputs also inherently instruct the model to produce valid JSON, making the manual inclusion of the word "json" in the prompt less of a primary constraint compared to JSON mode [2][7].Citations:
json_objectfallback may trigger an OpenAI API error if the prompt lacks "json"Using
response_format={"type": "json_object"}with the Chat Completions API strictly requires the word "json" to appear in the messages. If CrewAI's generated prompts do not explicitly include this term, the API will reject the request with a 400 error, replacing the original integration failure with a validation error.When
self._supports_structured_output()is false, ensure the system prompt is augmented to include a "Respond in JSON" instruction.file: lib/crewai/src/crewai/llms/providers/openai/completion.py
🤖 Prompt for AI Agents