5454)
5555from  .lifecycle  import  RunHooks 
5656from  .logger  import  logger 
57- from  .memory  import  Session 
57+ from  .memory  import  Session ,  SessionInputCallback 
5858from  .model_settings  import  ModelSettings 
5959from  .models .interface  import  Model , ModelProvider 
6060from  .models .multi_provider  import  MultiProvider 
@@ -179,6 +179,13 @@ class RunConfig:
179179    An optional dictionary of additional metadata to include with the trace. 
180180    """ 
181181
182+     session_input_callback : SessionInputCallback  |  None  =  None 
183+     """Defines how to handle session history when new input is provided. 
184+     - `None` (default): The new input is appended to the session history. 
185+     - `SessionInputCallback`: A custom function that receives the history and new input, and 
186+       returns the desired combined list of items. 
187+     """ 
188+ 
182189    call_model_input_filter : CallModelInputFilter  |  None  =  None 
183190    """ 
184191    Optional callback that is invoked immediately before calling the model. It receives the current 
@@ -413,7 +420,9 @@ async def run(
413420
414421        # Keep original user input separate from session-prepared input 
415422        original_user_input  =  input 
416-         prepared_input  =  await  self ._prepare_input_with_session (input , session )
423+         prepared_input  =  await  self ._prepare_input_with_session (
424+             input , session , run_config .session_input_callback 
425+         )
417426
418427        tool_use_tracker  =  AgentToolUseTracker ()
419428
@@ -781,7 +790,9 @@ async def _start_streaming(
781790
782791        try :
783792            # Prepare input with session if enabled 
784-             prepared_input  =  await  AgentRunner ._prepare_input_with_session (starting_input , session )
793+             prepared_input  =  await  AgentRunner ._prepare_input_with_session (
794+                 starting_input , session , run_config .session_input_callback 
795+             )
785796
786797            # Update the streamed result with the prepared input 
787798            streamed_result .input  =  prepared_input 
@@ -1474,19 +1485,20 @@ async def _prepare_input_with_session(
14741485        cls ,
14751486        input : str  |  list [TResponseInputItem ],
14761487        session : Session  |  None ,
1488+         session_input_callback : SessionInputCallback  |  None ,
14771489    ) ->  str  |  list [TResponseInputItem ]:
14781490        """Prepare input by combining it with session history if enabled.""" 
14791491        if  session  is  None :
14801492            return  input 
14811493
1482-         # Validate that we don't have both a session and a list input, as this creates 
1483-         # ambiguity about whether the list should append to or replace existing session history 
1484-         if  isinstance (input , list ):
1494+         # If the user doesn't specify an input callback and pass a list as input 
1495+         if  isinstance (input , list ) and  not  session_input_callback :
14851496            raise  UserError (
1486-                 "Cannot provide both a session and a list of input items. " 
1487-                 "When using session memory, provide only a string input to append to the " 
1488-                 "conversation, or use session=None and provide a list to manually manage " 
1489-                 "conversation history." 
1497+                 "When using session memory, list inputs require a " 
1498+                 "`RunConfig.session_input_callback` to define how they should be merged " 
1499+                 "with the conversation history. If you don't want to use a callback, " 
1500+                 "provide your input as a string instead, or disable session memory " 
1501+                 "(session=None) and pass a list to manage the history manually." 
14901502            )
14911503
14921504        # Get previous conversation history 
@@ -1495,10 +1507,18 @@ async def _prepare_input_with_session(
14951507        # Convert input to list format 
14961508        new_input_list  =  ItemHelpers .input_to_new_input_list (input )
14971509
1498-         # Combine history with new input 
1499-         combined_input  =  history  +  new_input_list 
1500- 
1501-         return  combined_input 
1510+         if  session_input_callback  is  None :
1511+             return  history  +  new_input_list 
1512+         elif  callable (session_input_callback ):
1513+             res  =  session_input_callback (history , new_input_list )
1514+             if  inspect .isawaitable (res ):
1515+                 return  await  res 
1516+             return  res 
1517+         else :
1518+             raise  UserError (
1519+                 f"Invalid `session_input_callback` value: { session_input_callback }  
1520+                 "Choose between `None` or a custom callable function." 
1521+             )
15021522
15031523    @classmethod  
15041524    async  def  _save_result_to_session (
@@ -1507,7 +1527,11 @@ async def _save_result_to_session(
15071527        original_input : str  |  list [TResponseInputItem ],
15081528        new_items : list [RunItem ],
15091529    ) ->  None :
1510-         """Save the conversation turn to session.""" 
1530+         """ 
1531+         Save the conversation turn to session. 
1532+         It does not account for any filtering or modification performed by 
1533+         `RunConfig.session_input_callback`. 
1534+         """ 
15111535        if  session  is  None :
15121536            return 
15131537
0 commit comments