4
4
import copy
5
5
import inspect
6
6
from dataclasses import dataclass , field
7
- from typing import Any , Generic , cast
7
+ from typing import Any , Callable , Generic , cast
8
8
9
9
from openai .types .responses import ResponseCompletedEvent
10
10
from openai .types .responses .response_prompt_param import (
56
56
from .tracing .span_data import AgentSpanData
57
57
from .usage import Usage
58
58
from .util import _coro , _error_tracing
59
+ from .util ._types import MaybeAwaitable
59
60
60
61
DEFAULT_MAX_TURNS = 10
61
62
@@ -81,6 +82,27 @@ def get_default_agent_runner() -> AgentRunner:
81
82
return DEFAULT_AGENT_RUNNER
82
83
83
84
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
+
84
106
@dataclass
85
107
class RunConfig :
86
108
"""Configures settings for the entire agent run."""
@@ -139,6 +161,16 @@ class RunConfig:
139
161
An optional dictionary of additional metadata to include with the trace.
140
162
"""
141
163
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
+
142
174
143
175
class RunOptions (TypedDict , Generic [TContext ]):
144
176
"""Arguments for ``AgentRunner`` methods."""
@@ -863,10 +895,40 @@ async def _run_single_turn_streamed(
863
895
input = ItemHelpers .input_to_new_input_list (streamed_result .input )
864
896
input .extend ([item .to_input_item () for item in streamed_result .new_items ])
865
897
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
+
866
928
# 1. Stream the output events
867
929
async for event in model .stream_response (
868
- system_prompt ,
869
- input ,
930
+ effective_instructions ,
931
+ effective_input ,
870
932
model_settings ,
871
933
all_tools ,
872
934
output_schema ,
@@ -1034,7 +1096,6 @@ async def _get_single_step_result_from_streamed_response(
1034
1096
run_config : RunConfig ,
1035
1097
tool_use_tracker : AgentToolUseTracker ,
1036
1098
) -> SingleStepResult :
1037
-
1038
1099
original_input = streamed_result .input
1039
1100
pre_step_items = streamed_result .new_items
1040
1101
event_queue = streamed_result ._event_queue
@@ -1161,13 +1222,46 @@ async def _get_new_response(
1161
1222
previous_response_id : str | None ,
1162
1223
prompt_config : ResponsePromptParam | None ,
1163
1224
) -> 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
+
1164
1258
model = cls ._get_model (agent , run_config )
1165
1259
model_settings = agent .model_settings .resolve (run_config .model_settings )
1166
1260
model_settings = RunImpl .maybe_reset_tool_choice (agent , tool_use_tracker , model_settings )
1167
1261
1168
1262
new_response = await model .get_response (
1169
- system_instructions = system_prompt ,
1170
- input = input ,
1263
+ system_instructions = effective_instructions ,
1264
+ input = effective_input ,
1171
1265
model_settings = model_settings ,
1172
1266
tools = all_tools ,
1173
1267
output_schema = output_schema ,
0 commit comments