-
Notifications
You must be signed in to change notification settings - Fork 5.2k
fix(lib): don't propagate ValidationError on truncated JSON in chat.completions.parse (#1763) #3763
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 |
|---|---|---|
|
|
@@ -109,15 +109,25 @@ def parse_chat_completion( | |
| for tool_call in message.tool_calls: | ||
| if tool_call.type == "function": | ||
| tool_call_dict = tool_call.to_dict() | ||
| try: | ||
| parsed_arguments = parse_function_tool_arguments( | ||
| input_tools=input_tools, function=tool_call.function | ||
| ) | ||
| except (pydantic.ValidationError, json.JSONDecodeError) as exc: | ||
| # The model returned a function-call whose arguments are not | ||
| # valid JSON for the declared tool schema (e.g. truncated by a | ||
| # stream cut-off). Surface the call with parsed_arguments=None | ||
| # instead of letting the exception escape the best-effort parse | ||
| # boundary. See issue #1763. | ||
| log.debug("Failed to parse tool call arguments: %s", exc) | ||
| parsed_arguments = None | ||
| tool_calls.append( | ||
| construct_type_unchecked( | ||
| value={ | ||
| **tool_call_dict, | ||
| "function": { | ||
| **cast(Any, tool_call_dict["function"]), | ||
| "parsed_arguments": parse_function_tool_arguments( | ||
| input_tools=input_tools, function=tool_call.function | ||
| ), | ||
| "parsed_arguments": parsed_arguments, | ||
| }, | ||
| }, | ||
| type_=ParsedFunctionToolCall, | ||
|
|
@@ -143,7 +153,7 @@ def parse_chat_completion( | |
| **choice.to_dict(), | ||
| "message": { | ||
| **message.to_dict(), | ||
| "parsed": maybe_parse_content( | ||
| "parsed": _safe_maybe_parse_content( | ||
| response_format=response_format, | ||
| message=message, | ||
| ), | ||
|
|
@@ -198,6 +208,21 @@ def maybe_parse_content( | |
| return None | ||
|
|
||
|
|
||
| def _safe_maybe_parse_content( | ||
| *, | ||
| response_format: type[ResponseFormatT] | ResponseFormatParam | Omit, | ||
| message: ChatCompletionMessage | ParsedChatCompletionMessage[object], | ||
| ) -> ResponseFormatT | None: | ||
| """Same contract as ``maybe_parse_content`` but catches JSON-decode and | ||
| pydantic validation errors so the best-effort parsing boundary in | ||
| ``parse_chat_completion`` never lets them escape. See issue #1763.""" | ||
| try: | ||
| return maybe_parse_content(response_format=response_format, message=message) | ||
| except (pydantic.ValidationError, json.JSONDecodeError) as exc: | ||
|
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.
When the response is valid JSON but fails the supplied model's schema or a custom Pydantic validator, this broad catch now returns Useful? React with 👍 / 👎. |
||
| log.debug("Failed to parse structured-output content: %s", exc) | ||
| return None | ||
|
|
||
|
|
||
| def has_parseable_input( | ||
| *, | ||
| response_format: type | ResponseFormatParam | Omit, | ||
|
|
||
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.
When
openai.lib.parsingdebug logging is enabled and Pydantic rejects structured content or tool arguments, interpolatingexcrenders theValidationError, whose text can include the offendinginput_value; this can copy customer response data or credentials embedded in model output into application logs. Both new debug statements should log only sanitized metadata such as the exception type or error codes, not the exception string.AGENTS.md reference: AGENTS.md:L26-L30
Useful? React with 👍 / 👎.