Skip to content

fix(responses): parse real Responses API output - #9

Merged
theQuert merged 1 commit into
mainfrom
claude/responses-output-items
Oct 7, 2026
Merged

theQuert merged 1 commit into
mainfrom
claude/responses-output-items

Conversation

@theQuert

@theQuert theQuert commented Oct 7, 2026

Copy link
Copy Markdown
Member

Closes #2.

client.responses.create() raised ValidationError on every successful response. Real Responses API output differs from the old model in two ways:

  • Message content parts have type: "output_text", with annotations.
  • output also contains non-message items, such as reasoning and function_call.

Changes in apertis/types/responses.py

  • New message content parts:
    • ResponseOutputText (output_text, annotations)
    • ResponseOutputRefusal
    • The legacy text and reasoning parts still validate.
  • New output item types:
    • ResponseReasoningItem
    • ResponseFunctionToolCall
    • ResponseUnknownOutputItem, which uses extra="allow" and keeps any other item type (for example web_search_call) with all its fields.
  • Output items are dispatched on type in a before validator, not resolved by a plain union. A malformed message therefore still fails validation instead of silently becoming an unknown item. This works on pydantic ≥2.0, so the dependency floor is unchanged.
  • Response.output_text joins the text parts of every message item.
  • Statuses:
    • Response.status accepts queued and in_progress.
    • Message items accept in_progress.
  • ResponseOutputMessage is an alias of ResponseOutput, so existing imports keep working.
  • New types are exported from apertis.types.
  • The README example now uses response.output_text. The old output[0].content[0].text breaks whenever a reasoning item comes first.

Proof

  • New tests in TestResponsesOutputItems (sync and async):
    • The response contains reasoning, message, function_call and web_search_call items, and each maps to its class.
    • A refusal with in_progress status parses.
    • A malformed message still fails validation.
  • The fixtures follow the OpenAI Responses object as typed in openai-python 3.26.0. They are respx mocks; no live calls were made.
  • Before the change, the test module failed to import, because none of these types existed.
  • Full suite: 101 passed on each of:
    • Python 3.13
    • Python 3.9
    • Python 3.9 with the minimum declared dependencies (pydantic 2.0.3, httpx 0.25.0, typing-extensions 4.7.1)
  • mypy reports nothing new for types/responses.py.
  • New ruff findings are only UP006/UP045. They match this file's existing Optional/List style, which pydantic needs on Python 3.9.

Not in scope: the input TypedDicts (ResponseInputTextContent and ResponseInputImageContent) still use text and image. The API names these input_text and input_image. That needs a follow-up.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Uy5vXsRpMhug7v3XwTMZHv

@theQuert
theQuert force-pushed the claude/responses-output-items branch from bbdd4df to 3d9770f Compare October 7, 2026 00:22
Response validation rejected every real response: message parts are
output_text (not text), and reasoning / function_call items sit beside the
message in output.

Model output_text and refusal parts, reasoning and function_call items, and
keep any other item type as ResponseUnknownOutputItem. Output items are
dispatched on their type, so a malformed known item still fails validation
instead of degrading to the unknown type. Add Response.output_text and the
queued / in_progress statuses. The legacy text / reasoning content parts
are still accepted.

Closes #2

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uy5vXsRpMhug7v3XwTMZHv
@theQuert
theQuert force-pushed the claude/responses-output-items branch from 3d9770f to 831056c Compare October 7, 2026 00:23
@theQuert
theQuert merged commit 45d2a71 into main Oct 7, 2026
3 checks passed
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.

responses.create fails to parse real Responses API output (output_text, reasoning items)

1 participant