Skip to content

fix(_utils/_transform): propagate __api_exclude__ in _async_transform_recursive#3324

Open
rmotgi1227 wants to merge 1 commit into
openai:mainfrom
rmotgi1227:fix/async-transform-missing-api-exclude
Open

fix(_utils/_transform): propagate __api_exclude__ in _async_transform_recursive#3324
rmotgi1227 wants to merge 1 commit into
openai:mainfrom
rmotgi1227:fix/async-transform-missing-api-exclude

Conversation

@rmotgi1227
Copy link
Copy Markdown

Summary

The sync _transform_recursive path (line 221 of _utils/_transform.py) correctly passes exclude=getattr(data, "__api_exclude__", None) to model_dump() when it encounters a pydantic BaseModel value. The async counterpart _async_transform_recursive (line 387) was missing this argument.

Bug

# sync — correct
if isinstance(data, pydantic.BaseModel):
    return model_dump(data, exclude_unset=True, mode="json", exclude=getattr(data, "__api_exclude__", None))

# async — missing exclude= kwarg (before this fix)
if isinstance(data, pydantic.BaseModel):
    return model_dump(data, exclude_unset=True, mode="json")

Impact

ParsedResponse (in openai/types/responses/parsed_response.py) declares:

__api_exclude__ = {"parsed_arguments"}

This is specifically to prevent the client-side computed parsed_arguments field from being sent back to the API server in multi-turn conversations. With the async client that exclusion had no effect: parsed_arguments leaked into every request body that included a ParsedResponse in the message history.

Fix

One-line change — add the missing exclude kwarg to match the sync path:

return model_dump(data, exclude_unset=True, mode="json", exclude=getattr(data, "__api_exclude__", None))

Tests

Added tests/test_async_transform_api_exclude.py with three tests (no live API calls):

  1. test_async_transform_excludes_api_excluded_fields — asserts __api_exclude__ fields are absent from the async output (this test fails before the fix, passes after)
  2. test_sync_transform_excludes_api_excluded_fields — sanity-check that the sync path behaves the same
  3. test_async_transform_pydantic_model_without_api_exclude — models without __api_exclude__ are unaffected

Run with PYTHONPATH=src pytest tests/test_async_transform_api_exclude.py.

…_recursive

The sync _transform_recursive path correctly passes
  exclude=getattr(data, "__api_exclude__", None)
to model_dump() when it encounters a pydantic BaseModel value.

The async _async_transform_recursive path omitted this kwarg, so any
field listed in __api_exclude__ was serialised into the outgoing
payload when the async client was used.

ParsedResponse (openai/types/responses/parsed_response.py) marks its
parsed_arguments field with __api_exclude__ precisely to stop client-side
computed data from being sent back to the API.  With the async client
that exclusion had no effect and parsed_arguments leaked into every
multi-turn request body that included a ParsedResponse in the history.

Fix: add the missing exclude= kwarg to the model_dump() call in
_async_transform_recursive, matching the sync implementation.
@rmotgi1227 rmotgi1227 requested a review from a team as a code owner May 27, 2026 18:47
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b762fc6492

ℹ️ 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".

inside get_type_hints().
"""

import asyncio
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove the unused asyncio import

When the repo's lint/typecheck checks include this new test file, this import makes ruff check . fail with F401 (and import ordering I001), so the change cannot pass CI until the unused import is removed.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant