Skip to content

feat: expose output_schema in ModelInputData for call_model_input_filter#3571

Open
jordanchendev wants to merge 2 commits into
openai:mainfrom
jordanchendev:feat/3563-output-schema-in-model-input-filter
Open

feat: expose output_schema in ModelInputData for call_model_input_filter#3571
jordanchendev wants to merge 2 commits into
openai:mainfrom
jordanchendev:feat/3563-output-schema-in-model-input-filter

Conversation

@jordanchendev
Copy link
Copy Markdown

Summary

Add an optional output_schema field to ModelInputData so that call_model_input_filter callbacks can inspect and override the structured-output schema used for a given model call.

Before: ModelInputData only exposed input and instructions, making it impossible for a filter to change (or even read) the response format without reaching outside the callback.

After: Filters receive the current output_schema (derived from agent.output_type) in model_data.output_schema and may return a different AgentOutputSchemaBase instance to override it for that call. Returning None or omitting the field preserves the agent's schema unchanged, so existing filters are fully backward-compatible.

Both the streaming (run_single_turn_streamed) and non-streaming (get_new_response) paths honour the override. ToolOutputTrimmer is updated to forward output_schema so it is not silently dropped by that built-in filter.

Test plan

Four new tests in tests/test_call_model_input_filter.py:

  • test_filter_can_override_output_schema_non_streamed — filter replaces schema; model receives the override.
  • test_filter_can_override_output_schema_streamed — same for the streaming path.
  • test_filter_receives_agent_output_schema — filter can observe the agent's schema before deciding.
  • test_filter_not_setting_output_schema_preserves_agent_schema — omitting output_schema in the return value must not clear the agent's schema (backward-compat regression test).

All existing tests continue to pass.

uv run pytest tests/test_call_model_input_filter.py tests/extensions/test_tool_output_trimmer.py -q
# 47 passed in 0.19s

Issue number

Closes #3563

Checks

  • I've added new tests (if relevant)
  • I've added/updated the relevant documentation
  • I've run make lint and make format
  • I've made sure tests pass

Add an optional `output_schema` field to `ModelInputData` so that
`call_model_input_filter` callbacks can inspect and override the
response format used for a given model call.

When the filter returns `output_schema=None` (or omits it), the
runner falls back to the schema derived from `agent.output_type`,
preserving backward compatibility.

Both the streaming (`run_single_turn_streamed`) and non-streaming
(`get_new_response`) paths honour the override. `ToolOutputTrimmer`
is updated to forward `output_schema` unchanged so it is not silently
dropped by that built-in filter.

Fixes openai#3563
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: 57df396a73

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

system_instructions=system_prompt,
output_schema=output_schema,
)
output_schema = filtered.output_schema if filtered.output_schema is not None else output_schema
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 Propagate overridden schema to final-output parsing

In non-streamed runs, this reassignment only changes the local output_schema inside get_new_response; after it returns, run_single_turn still passes the original schema to get_single_step_result_from_response. When a call_model_input_filter sets a structured schema for an agent without output_type, the model request uses the override, but the final response is processed with None, so Runner.run returns the raw JSON string instead of validating/parsing it with the override. The streamed path keeps the updated schema in the same function, so this regression is specific to non-streamed runs.

Useful? React with 👍 / 👎.

…e parsing

get_new_response now returns (ModelResponse, AgentOutputSchemaBase | None)
so that run_single_turn can pass the filter-overridden schema to
get_single_step_result_from_response. Previously the local reassignment
inside get_new_response was invisible to the caller, causing non-streamed
runs to parse the model response with the original (pre-filter) schema
instead of the override.

Fixes the regression identified in review on openai#3571.
Copy link
Copy Markdown
Author

@jordanchendev jordanchendev left a comment

Choose a reason for hiding this comment

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

Good catch — fixed in e7e0bbb.

get_new_response now returns tuple[ModelResponse, AgentOutputSchemaBase | None] so the caller (run_single_turn) unpacks and forwards the effective schema to get_single_step_result_from_response. The existing non-streamed test was strengthened to assert result.final_output is a parsed _Reply instance (not a raw string), making the regression impossible to re-introduce silently.

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.

Add custom ResponseFormat support in CallModelInputFilter callback

2 participants