44import copy
55import inspect
66from dataclasses import dataclass , field
7- from typing import Any , Generic , cast
7+ from typing import Any , Callable , Generic , cast
88
99from openai .types .responses import ResponseCompletedEvent
1010from openai .types .responses .response_prompt_param import (
5656from .tracing .span_data import AgentSpanData
5757from .usage import Usage
5858from .util import _coro , _error_tracing
59+ from .util ._types import MaybeAwaitable
5960
6061DEFAULT_MAX_TURNS = 10
6162
@@ -81,6 +82,27 @@ def get_default_agent_runner() -> AgentRunner:
8182 return DEFAULT_AGENT_RUNNER
8283
8384
85+ @dataclass
86+ class ModelInputData :
87+ """Container for the data that will be sent to the model."""
88+
89+ input : list [TResponseInputItem ]
90+ instructions : str | None
91+
92+
93+ @dataclass
94+ class CallModelData (Generic [TContext ]):
95+ """Data passed to `RunConfig.call_model_input_filter` prior to model call."""
96+
97+ model_data : ModelInputData
98+ agent : Agent [TContext ]
99+ context : TContext | None
100+
101+
102+ # Type alias for the optional input filter callback
103+ CallModelInputFilter = Callable [[CallModelData [Any ]], MaybeAwaitable [ModelInputData ]]
104+
105+
84106@dataclass
85107class RunConfig :
86108 """Configures settings for the entire agent run."""
@@ -139,6 +161,16 @@ class RunConfig:
139161 An optional dictionary of additional metadata to include with the trace.
140162 """
141163
164+ call_model_input_filter : CallModelInputFilter | None = None
165+ """
166+ Optional callback that is invoked immediately before calling the model. It receives the current
167+ agent, context and the model input (instructions and input items), and must return a possibly
168+ modified `ModelInputData` to use for the model call.
169+
170+ This allows you to edit the input sent to the model e.g. to stay within a token limit.
171+ For example, you can use this to add a system prompt to the input.
172+ """
173+
142174
143175class RunOptions (TypedDict , Generic [TContext ]):
144176 """Arguments for ``AgentRunner`` methods."""
@@ -863,10 +895,40 @@ async def _run_single_turn_streamed(
863895 input = ItemHelpers .input_to_new_input_list (streamed_result .input )
864896 input .extend ([item .to_input_item () for item in streamed_result .new_items ])
865897
898+ # Allow user to modify model input right before the streaming call, if configured
899+ effective_instructions = system_prompt
900+ effective_input : list [TResponseInputItem ] = input
901+ if run_config .call_model_input_filter is not None :
902+ try :
903+ model_input = ModelInputData (
904+ input = copy .deepcopy (effective_input ),
905+ instructions = effective_instructions ,
906+ )
907+ filter_payload : CallModelData [TContext ] = CallModelData (
908+ model_data = model_input ,
909+ agent = agent ,
910+ context = context_wrapper .context ,
911+ )
912+ maybe_updated = run_config .call_model_input_filter (filter_payload )
913+ updated = (
914+ await maybe_updated
915+ if inspect .isawaitable (maybe_updated )
916+ else maybe_updated
917+ )
918+ if not isinstance (updated , ModelInputData ):
919+ raise UserError ("call_model_input_filter must return a ModelInputData instance" )
920+ effective_input = updated .input
921+ effective_instructions = updated .instructions
922+ except Exception as e :
923+ _error_tracing .attach_error_to_current_span (
924+ SpanError (message = "Error in call_model_input_filter" , data = {"error" : str (e )})
925+ )
926+ raise
927+
866928 # 1. Stream the output events
867929 async for event in model .stream_response (
868- system_prompt ,
869- input ,
930+ effective_instructions ,
931+ effective_input ,
870932 model_settings ,
871933 all_tools ,
872934 output_schema ,
@@ -1034,7 +1096,6 @@ async def _get_single_step_result_from_streamed_response(
10341096 run_config : RunConfig ,
10351097 tool_use_tracker : AgentToolUseTracker ,
10361098 ) -> SingleStepResult :
1037-
10381099 original_input = streamed_result .input
10391100 pre_step_items = streamed_result .new_items
10401101 event_queue = streamed_result ._event_queue
@@ -1161,13 +1222,46 @@ async def _get_new_response(
11611222 previous_response_id : str | None ,
11621223 prompt_config : ResponsePromptParam | None ,
11631224 ) -> ModelResponse :
1225+ # Allow user to modify model input right before the call, if configured
1226+ effective_instructions = system_prompt
1227+ effective_input : list [TResponseInputItem ] = input
1228+
1229+ if run_config .call_model_input_filter is not None :
1230+ try :
1231+ model_input = ModelInputData (
1232+ input = copy .deepcopy (effective_input ),
1233+ instructions = effective_instructions ,
1234+ )
1235+ filter_payload : CallModelData [TContext ] = CallModelData (
1236+ model_data = model_input ,
1237+ agent = agent ,
1238+ context = context_wrapper .context ,
1239+ )
1240+ maybe_updated = run_config .call_model_input_filter (filter_payload )
1241+ updated = (
1242+ await maybe_updated
1243+ if inspect .isawaitable (maybe_updated )
1244+ else maybe_updated
1245+ )
1246+ # Basic validation to prevent accidental None returns
1247+ if not isinstance (updated , ModelInputData ):
1248+ raise UserError ("call_model_input_filter must return a ModelInputData instance" )
1249+ effective_input = updated .input
1250+ effective_instructions = updated .instructions
1251+ except Exception as e :
1252+ # Attach to trace and re-raise as user error so it's clear
1253+ _error_tracing .attach_error_to_current_span (
1254+ SpanError (message = "Error in call_model_input_filter" , data = {"error" : str (e )})
1255+ )
1256+ raise
1257+
11641258 model = cls ._get_model (agent , run_config )
11651259 model_settings = agent .model_settings .resolve (run_config .model_settings )
11661260 model_settings = RunImpl .maybe_reset_tool_choice (agent , tool_use_tracker , model_settings )
11671261
11681262 new_response = await model .get_response (
1169- system_instructions = system_prompt ,
1170- input = input ,
1263+ system_instructions = effective_instructions ,
1264+ input = effective_input ,
11711265 model_settings = model_settings ,
11721266 tools = all_tools ,
11731267 output_schema = output_schema ,
0 commit comments