Skip to content

fix(agent_tool_input): summarize Pydantic Optional[T] anyOf fields#3265

Open
adityasingh2400 wants to merge 3 commits into
openai:mainfrom
adityasingh2400:fix/ati-audit
Open

fix(agent_tool_input): summarize Pydantic Optional[T] anyOf fields#3265
adityasingh2400 wants to merge 3 commits into
openai:mainfrom
adityasingh2400:fix/ati-audit

Conversation

@adityasingh2400

Copy link
Copy Markdown
Contributor

Summary

  • The schema summary helper rejected any field with an anyOf key, but Pydantic renders T | None as anyOf: [{type: T}, {type: "null"}]. Because _summarize_json_schema returns None for the whole schema when any single field is unsupported, one Optional[T] field would silently suppress the entire structured-input summary, including sibling fields with valid types and descriptions.
  • Detect the 2-branch anyOf of a simple type plus null and render it as T | null, matching the existing type: ["T", "null"] path.
  • Other anyOf shapes (unions of two non-null types, anyOf with nested objects, etc.) are still rejected.

Repro

from pydantic import BaseModel, TypeAdapter
from agents.agent_tool_input import build_structured_input_schema_info

class Args(BaseModel):
    count: int | None = None  # Pydantic emits anyOf: [{type:integer},{type:null}]
    name: str  # has a description in the rendered schema

schema = TypeAdapter(Args).json_schema()
# add descriptions for the demo
schema["properties"]["count"]["description"] = "Optional count."
schema["properties"]["name"]["description"] = "A name."

print(build_structured_input_schema_info(schema, include_json_schema=False).summary)
# Before: None  (entire summary dropped)
# After:
#   - count (integer | null, optional) - Optional count.
#   - name (string, required) - A name.

Test plan

  • New test test_schema_summary_handles_pydantic_optional_anyof covers the nullable anyOf shape and confirms non-nullable anyOf unions are still rejected.
  • Test fails on main (assert None is not None) and passes with the fix.
  • All existing tests/test_agent_tool_input.py, tests/test_agent_tool_state.py, and tests/test_agent_as_tool.py tests still pass (60/60).

@github-actions github-actions Bot added bug Something isn't working feature:core labels May 8, 2026
@seratch

seratch commented May 8, 2026

Copy link
Copy Markdown
Member

@codex review

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

Copy link
Copy Markdown

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: d6ae9a108b

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/agents/agent_tool_input.py Outdated
Comment on lines +272 to +274
if entry_type in _SIMPLE_JSON_SCHEMA_TYPES and base_type is None:
base_type = cast(str, entry_type)
continue

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 Preserve enum/const constraints in nullable anyOf

Pydantic emits Optional[Literal[...]] as anyOf with an enum/const branch that also has type: "string". This helper now accepts that branch as a bare simple type, so the schema summary says e.g. string | null instead of the allowed literals, misleading the nested agent about valid structured input.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in c22eb566: Preserve enum/const branches in the nullable anyOf summary so Optional[Literal[...]] reports the allowed literals instead of bare string | null. Test added for the literal case.

@seratch
seratch marked this pull request as draft May 8, 2026 17:49
@seratch seratch removed the bug Something isn't working label May 12, 2026
The schema summary helper rejected any field with an `anyOf` key, but
Pydantic renders `T | None` as `anyOf: [{type: T}, {type: "null"}]`.
Because `_summarize_json_schema` returns None for the whole schema when
any single field is unsupported, one Optional field would silently
suppress the entire structured-input summary - even for sibling fields
with valid types and descriptions.

Detect the 2-branch `anyOf` of a simple type and `null` and render it
as `T | null`, matching the existing `type: ["T", "null"]` path.
Other `anyOf` shapes (unions of two non-null types, anyOf with nested
objects, etc.) are still rejected.
Pydantic emits Optional[Literal[...]] as a 2-branch anyOf where one
branch carries both enum/const and type: 'string'. The previous fix
collapsed that to 'string | null', hiding the allowed values from the
nested-agent schema summary. Prefer the enum/const label when present.
@seratch
seratch marked this pull request as ready for review June 22, 2026 02:03

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

Copy link
Copy Markdown

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: 64e23d389c

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if "const" in entry:
base_label = _format_literal_label(entry)
continue
if entry_type in _SIMPLE_JSON_SCHEMA_TYPES:

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 Guard non-string anyOf branch types

When a supported parameter model supplies a JSON Schema anyOf branch whose type uses the valid list form, for example via Pydantic json_schema_extra as {"type": ["integer", "string"]} plus a null branch, this membership test hashes the list and raises TypeError while building the agent tool. Other unsupported schema shapes in this helper fall back to None, so this should first verify entry_type is a string before checking _SIMPLE_JSON_SCHEMA_TYPES.

Useful? React with 👍 / 👎.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants