Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/agents/run_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -50,6 +50,7 @@ class ModelInputData:

input: list[TResponseInputItem]
instructions: str | None
response_format: ResponseFormat | None = None


@dataclass
Expand Down
35 changes: 32 additions & 3 deletions src/agents/run_internal/run_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -1366,13 +1367,27 @@ 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)

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=current_response_format,
)
if isinstance(filtered.input, list):
filtered.input = deduplicate_input_items_preferring_latest(filtered.input)
Expand Down Expand Up @@ -1815,19 +1830,33 @@ 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)

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=current_response_format,
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 Apply the filtered response format to the model call

When a call_model_input_filter returns a modified ModelInputData.response_format, the value is still discarded: this new argument only seeds the filter, while the subsequent model call continues to use the original output_schema and never reads filtered.response_format (the streamed path does the same). Fresh evidence in this revision is that the earlier crash was removed, but the returned response format remains unused, so filters cannot actually enforce or disable a JSON schema despite the new API field.

Useful? React with 👍 / 👎.

)
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)
Expand Down
9 changes: 8 additions & 1 deletion src/agents/run_internal/turn_preparation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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,
Expand Down