fix: resolve PydanticSerializationUnexpectedValue warnings in responses.parse()#2885
fix: resolve PydanticSerializationUnexpectedValue warnings in responses.parse()#2885bledden wants to merge 2 commits intoopenai:mainfrom
Conversation
…es.parse() When calling model_dump() or model_dump_json() on a ParsedResponse returned by responses.parse(), Pydantic emits ~19 PydanticSerializationUnexpectedValue warnings. This happens because the runtime types (ParsedResponseOutputMessage[TypeVar], ParsedResponseOutputText[TypeVar]) don't match the declared union member types in the schema, so the serializer tries every variant and warns on each mismatch. The fix wraps the `output` and `content` fields with SerializeAsAny, which tells Pydantic to serialize using the runtime type's schema instead of the declared union member's schema. This only affects serialization — validation and deserialization are unchanged. Fixes openai#2872
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b4cd54235b
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| from typing import TYPE_CHECKING, List, Union, Generic, TypeVar, Optional | ||
| from typing_extensions import Annotated, TypeAlias | ||
|
|
||
| from pydantic import SerializeAsAny |
There was a problem hiding this comment.
Preserve pydantic v1 compatibility for parsed response types
SerializeAsAny is a Pydantic v2-only symbol, so this unconditional import raises ImportError for environments still on Pydantic 1.x; that breaks importing openai.types.responses.parsed_response (and therefore responses.parse() paths) before any runtime fallback logic can run. The repo still carries explicit v1 compatibility (pydantic>=1.9.0,<3 in pyproject.toml and PYDANTIC_V1 branches in src/openai/_compat.py), so this change introduces a regression for those users.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
This is already addressed in the current version of the PR. The SerializeAsAny import is guarded behind if not PYDANTIC_V1 (line 8-9), and both ParsedResponseOutputMessage and ParsedResponse use three-branch conditionals (TYPE_CHECKING / not PYDANTIC_V1 / else) so the symbol is never referenced on v1.
The unconditional `from pydantic import SerializeAsAny` import raises ImportError on Pydantic v1, which the SDK still supports (pydantic>=1.9.0). Guard the import behind PYDANTIC_V1 and add v1 fallback branches in the class bodies that use the original undecorated type annotations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
Calling
model_dump()ormodel_dump_json()on aParsedResponsereturned byresponses.parse()emits ~19PydanticSerializationUnexpectedValuewarnings per call. This is because the runtime types (ParsedResponseOutputMessage[TypeVar],ParsedResponseOutputText[TypeVar]) don't match the declared union member types in the Pydantic schema, so the serializer tries every variant in the union and warns on each mismatch.Root cause
The
PropertyInfo(discriminator="type")metadata used onParsedContentandParsedResponseOutputItemis only used during deserialization (inconstruct_type()). Pydantic's serializer ignores it entirely, so it falls back to a plain union strategy that tries each variant sequentially. Combined with the unresolvedTypeVarin the runtime-constructed objects, this produces a warning for every non-matching variant.Fix
Wrapped the
contentandoutputfields onParsedResponseOutputMessageandParsedResponsewithSerializeAsAny, which tells Pydantic to serialize using the runtime type's schema instead of the declared union member's schema. This only affects serialization — validation and deserialization are unchanged.Before
After
Test plan
ParsedResponsefrom a dict (no API calls) and capturing warnings duringmodel_dump()SerializeAsAnytests/lib/responses/test_parsed_response_serialization.pyruff checkpasses on all modified filesFixes #2872