54
54
)
55
55
from .lifecycle import RunHooks
56
56
from .logger import logger
57
- from .memory import Session
57
+ from .memory import Session , SessionInputCallback
58
58
from .model_settings import ModelSettings
59
59
from .models .interface import Model , ModelProvider
60
60
from .models .multi_provider import MultiProvider
@@ -179,6 +179,13 @@ class RunConfig:
179
179
An optional dictionary of additional metadata to include with the trace.
180
180
"""
181
181
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
+
182
189
call_model_input_filter : CallModelInputFilter | None = None
183
190
"""
184
191
Optional callback that is invoked immediately before calling the model. It receives the current
@@ -413,7 +420,9 @@ async def run(
413
420
414
421
# Keep original user input separate from session-prepared input
415
422
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
+ )
417
426
418
427
tool_use_tracker = AgentToolUseTracker ()
419
428
@@ -781,7 +790,9 @@ async def _start_streaming(
781
790
782
791
try :
783
792
# 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
+ )
785
796
786
797
# Update the streamed result with the prepared input
787
798
streamed_result .input = prepared_input
@@ -1474,19 +1485,20 @@ async def _prepare_input_with_session(
1474
1485
cls ,
1475
1486
input : str | list [TResponseInputItem ],
1476
1487
session : Session | None ,
1488
+ session_input_callback : SessionInputCallback | None ,
1477
1489
) -> str | list [TResponseInputItem ]:
1478
1490
"""Prepare input by combining it with session history if enabled."""
1479
1491
if session is None :
1480
1492
return input
1481
1493
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 :
1485
1496
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."
1490
1502
)
1491
1503
1492
1504
# Get previous conversation history
@@ -1495,10 +1507,18 @@ async def _prepare_input_with_session(
1495
1507
# Convert input to list format
1496
1508
new_input_list = ItemHelpers .input_to_new_input_list (input )
1497
1509
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
+ )
1502
1522
1503
1523
@classmethod
1504
1524
async def _save_result_to_session (
@@ -1507,7 +1527,11 @@ async def _save_result_to_session(
1507
1527
original_input : str | list [TResponseInputItem ],
1508
1528
new_items : list [RunItem ],
1509
1529
) -> 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
+ """
1511
1535
if session is None :
1512
1536
return
1513
1537
0 commit comments