From c9ef7dc95da1ba4f0db08509451d1e5347ce3e31 Mon Sep 17 00:00:00 2001 From: ylzn567 Date: Wed, 3 Jun 2026 15:47:00 +0300 Subject: [PATCH 1/4] feat: add ResponseFormat support to call_model_input_filter (#3563) --- src/agents/run_config.py | 3 ++- src/agents/run_internal/run_loop.py | 3 +++ src/agents/run_internal/turn_preparation.py | 9 ++++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/agents/run_config.py b/src/agents/run_config.py index fcc9b01315..ba6f7d32dd 100644 --- a/src/agents/run_config.py +++ b/src/agents/run_config.py @@ -28,7 +28,7 @@ from .sandbox.session.sandbox_client import BaseSandboxClient from .sandbox.session.sandbox_session_state import SandboxSessionState from .sandbox.snapshot import SnapshotBase, SnapshotSpec - +from openai.types.chat.completion_create_params import ResponseFormat DEFAULT_MAX_TURNS = 10 DEFAULT_MAX_MANIFEST_ENTRY_CONCURRENCY = 4 @@ -50,6 +50,7 @@ class ModelInputData: input: list[TResponseInputItem] instructions: str | None + response_format: ResponseFormat | None = None @dataclass diff --git a/src/agents/run_internal/run_loop.py b/src/agents/run_internal/run_loop.py index 45f09c0fa0..22cc4c67c4 100644 --- a/src/agents/run_internal/run_loop.py +++ b/src/agents/run_internal/run_loop.py @@ -194,6 +194,7 @@ resolve_interrupted_turn, run_final_output_hooks, ) +from openai.types.chat.completion_create_params import ResponseFormat __all__ = [ "extract_tool_call_id", @@ -1373,6 +1374,7 @@ def _tool_search_fingerprint(raw_item: Any) -> str: context_wrapper=context_wrapper, input_items=input, system_instructions=system_prompt, + response_format=model_settings.response_format, ) if isinstance(filtered.input, list): filtered.input = deduplicate_input_items_preferring_latest(filtered.input) @@ -1821,6 +1823,7 @@ async def get_new_response( context_wrapper=context_wrapper, input_items=input, system_instructions=system_prompt, + response_format=model_settings.response_format, ) if isinstance(filtered.input, list): filtered.input = deduplicate_input_items_preferring_latest(filtered.input) diff --git a/src/agents/run_internal/turn_preparation.py b/src/agents/run_internal/turn_preparation.py index 0a79ebd813..2a7602a334 100644 --- a/src/agents/run_internal/turn_preparation.py +++ b/src/agents/run_internal/turn_preparation.py @@ -18,6 +18,7 @@ from ..tool import Tool from ..tracing import SpanError from ..util import _error_tracing +from openai.types.chat import ResponseFormat __all__ = [ "validate_run_hooks", @@ -55,18 +56,24 @@ async def maybe_filter_model_input( context_wrapper: RunContextWrapper[TContext], input_items: list[TResponseInputItem], system_instructions: str | None, + response_format: ResponseFormat | None = None, ) -> ModelInputData: """Apply optional call_model_input_filter to modify model input.""" effective_instructions = system_instructions effective_input: list[TResponseInputItem] = input_items if run_config.call_model_input_filter is None: - return ModelInputData(input=effective_input, instructions=effective_instructions) + return ModelInputData( + input=effective_input, + instructions=effective_instructions, + response_format=response_format, + ) try: model_input = ModelInputData( input=effective_input.copy(), instructions=effective_instructions, + response_format=response_format, ) filter_payload: CallModelData[TContext] = CallModelData( model_data=model_input, From 29b8bb6f519e4842b8797d1face95af952186f11 Mon Sep 17 00:00:00 2001 From: ylzn567 Date: Wed, 3 Jun 2026 19:34:47 +0300 Subject: [PATCH 2/4] fix: resolve UnboundLocalError, fix typo, and pass None to filter --- src/agents/run_internal/run_loop.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/agents/run_internal/run_loop.py b/src/agents/run_internal/run_loop.py index 22cc4c67c4..e4c3efdf2a 100644 --- a/src/agents/run_internal/run_loop.py +++ b/src/agents/run_internal/run_loop.py @@ -1367,6 +1367,9 @@ def _tool_search_fingerprint(raw_item: Any) -> str: streamed_result._model_input_items, reasoning_item_id_policy, ) + model_settings = get_model_settings(execution_agent, run_config) + model_settings = maybe_reset_tool_choice(public_agent, tool_use_tracker, model_settings) + filtered = await maybe_filter_model_input( agent=public_agent, @@ -1374,7 +1377,7 @@ def _tool_search_fingerprint(raw_item: Any) -> str: context_wrapper=context_wrapper, input_items=input, system_instructions=system_prompt, - response_format=model_settings.response_format, + response_format=None, ) if isinstance(filtered.input, list): filtered.input = deduplicate_input_items_preferring_latest(filtered.input) @@ -1817,20 +1820,22 @@ async def get_new_response( """Call the model and return the raw response, handling retries and hooks.""" public_agent = bindings.public_agent execution_agent = bindings.execution_agent + + model = get_model(execution_agent, run_config) + model_settings = get_model_settings(execution_agent, run_config) + model_settings = maybe_reset_tool_choice(public_agent, tool_use_tracker, model_settings) + filtered = await maybe_filter_model_input( agent=public_agent, run_config=run_config, context_wrapper=context_wrapper, input_items=input, system_instructions=system_prompt, - response_format=model_settings.response_format, + response_format=None, ) if isinstance(filtered.input, list): filtered.input = deduplicate_input_items_preferring_latest(filtered.input) - model = get_model(execution_agent, run_config) - model_settings = get_model_settings(execution_agent, run_config) - model_settings = maybe_reset_tool_choice(public_agent, tool_use_tracker, model_settings) if server_conversation_tracker is not None: server_conversation_tracker.mark_input_as_sent(filtered.input) From 8f6f503e865988cb77bae5f96da1a120d10fde34 Mon Sep 17 00:00:00 2001 From: ylzn567 Date: Wed, 3 Jun 2026 19:48:53 +0300 Subject: [PATCH 3/4] fix: pass current response_format to filter and forward it to model --- src/agents/run_internal/run_loop.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/agents/run_internal/run_loop.py b/src/agents/run_internal/run_loop.py index e4c3efdf2a..f63685e9d3 100644 --- a/src/agents/run_internal/run_loop.py +++ b/src/agents/run_internal/run_loop.py @@ -1370,6 +1370,16 @@ def _tool_search_fingerprint(raw_item: Any) -> str: model_settings = get_model_settings(execution_agent, run_config) model_settings = maybe_reset_tool_choice(public_agent, tool_use_tracker, model_settings) + current_response_format: ResponseFormat | None = None + if output_schema and hasattr(output_schema, "is_plain_text") and not output_schema.is_plain_text(): + current_response_format = cast(ResponseFormat, { + "type": "json_schema", + "json_schema": { + "name": "final_output", + "strict": output_schema.is_strict_json_schema(), + "schema": output_schema.json_schema(), + }, + }) filtered = await maybe_filter_model_input( agent=public_agent, @@ -1377,7 +1387,7 @@ def _tool_search_fingerprint(raw_item: Any) -> str: context_wrapper=context_wrapper, input_items=input, system_instructions=system_prompt, - response_format=None, + response_format=current_response_format, ) if isinstance(filtered.input, list): filtered.input = deduplicate_input_items_preferring_latest(filtered.input) @@ -1476,6 +1486,7 @@ async def rewind_model_request() -> None: previous_response_id=previous_response_id, conversation_id=conversation_id, prompt=prompt_config, + response_format=filtered.response_format, ), rewind=rewind_model_request, retry_settings=model_settings.retry, @@ -1825,13 +1836,24 @@ async def get_new_response( model_settings = get_model_settings(execution_agent, run_config) model_settings = maybe_reset_tool_choice(public_agent, tool_use_tracker, model_settings) + current_response_format: ResponseFormat | None = None + if output_schema and hasattr(output_schema, "is_plain_text") and not output_schema.is_plain_text(): + current_response_format = cast(ResponseFormat, { + "type": "json_schema", + "json_schema": { + "name": "final_output", + "strict": output_schema.is_strict_json_schema(), + "schema": output_schema.json_schema(), + }, + }) + filtered = await maybe_filter_model_input( agent=public_agent, run_config=run_config, context_wrapper=context_wrapper, input_items=input, system_instructions=system_prompt, - response_format=None, + response_format=current_response_format, ) if isinstance(filtered.input, list): filtered.input = deduplicate_input_items_preferring_latest(filtered.input) @@ -1901,6 +1923,7 @@ async def rewind_model_request() -> None: previous_response_id=previous_response_id, conversation_id=conversation_id, prompt=prompt_config, + response_format=filtered.response_format, ), rewind=rewind_model_request, retry_settings=model_settings.retry, From 17b8225a267af44111f42d2efebde8276698931b Mon Sep 17 00:00:00 2001 From: ylzn567 Date: Wed, 3 Jun 2026 19:54:33 +0300 Subject: [PATCH 4/4] chore: remove response_format kwarg to prevent interface TypeError pending maintainer guidance --- src/agents/run_internal/run_loop.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/agents/run_internal/run_loop.py b/src/agents/run_internal/run_loop.py index f63685e9d3..3e550cb35d 100644 --- a/src/agents/run_internal/run_loop.py +++ b/src/agents/run_internal/run_loop.py @@ -1486,7 +1486,6 @@ async def rewind_model_request() -> None: previous_response_id=previous_response_id, conversation_id=conversation_id, prompt=prompt_config, - response_format=filtered.response_format, ), rewind=rewind_model_request, retry_settings=model_settings.retry, @@ -1923,7 +1922,6 @@ async def rewind_model_request() -> None: previous_response_id=previous_response_id, conversation_id=conversation_id, prompt=prompt_config, - response_format=filtered.response_format, ), rewind=rewind_model_request, retry_settings=model_settings.retry,