diff --git a/src/agents/run.py b/src/agents/run.py index 145169f6e9..bf8a5760d1 100644 --- a/src/agents/run.py +++ b/src/agents/run.py @@ -1976,16 +1976,6 @@ async def _prepare_input_with_session( if session is None: return input - # If the user doesn't specify an input callback and pass a list as input - if isinstance(input, list) and not session_input_callback: - raise UserError( - "When using session memory, list inputs require a " - "`RunConfig.session_input_callback` to define how they should be merged " - "with the conversation history. If you don't want to use a callback, " - "provide your input as a string instead, or disable session memory " - "(session=None) and pass a list to manage the history manually." - ) - # Get previous conversation history history = await session.get_items() @@ -1993,6 +1983,8 @@ async def _prepare_input_with_session( new_input_list = ItemHelpers.input_to_new_input_list(input) if session_input_callback is None: + # Historically we rejected list inputs without an explicit callback to avoid + # ambiguous merges; the default now appends new items to history for ergonomics. return history + new_input_list elif callable(session_input_callback): res = session_input_callback(history, new_input_list) diff --git a/tests/test_session.py b/tests/test_session.py index e0328056b2..305248eda5 100644 --- a/tests/test_session.py +++ b/tests/test_session.py @@ -7,7 +7,6 @@ import pytest from agents import Agent, RunConfig, Runner, SQLiteSession, TResponseInputItem -from agents.exceptions import UserError from .fake_model import FakeModel from .test_responses import get_text_message @@ -371,10 +370,8 @@ async def test_sqlite_session_get_items_with_limit(): @pytest.mark.parametrize("runner_method", ["run", "run_sync", "run_streamed"]) @pytest.mark.asyncio -async def test_session_memory_rejects_both_session_and_list_input(runner_method): - """Test that passing both a session and list input raises a UserError across all runner - methods. - """ +async def test_session_memory_appends_list_input_by_default(runner_method): + """Test that list inputs are appended to session history when no callback is provided.""" with tempfile.TemporaryDirectory() as temp_dir: db_path = Path(temp_dir) / "test_validation.db" session_id = "test_validation_parametrized" @@ -383,19 +380,18 @@ async def test_session_memory_rejects_both_session_and_list_input(runner_method) model = FakeModel() agent = Agent(name="test", model=model) - # Test that providing both a session and a list input raises a UserError - model.set_next_output([get_text_message("This shouldn't run")]) - - list_input = [ - {"role": "user", "content": "Test message"}, + initial_history: list[TResponseInputItem] = [ + {"role": "user", "content": "Earlier message"}, + {"role": "assistant", "content": "Saved reply"}, ] + await session.add_items(initial_history) + + list_input = [{"role": "user", "content": "Test message"}] - with pytest.raises(UserError) as exc_info: - await run_agent_async(runner_method, agent, list_input, session=session) + model.set_next_output([get_text_message("This should run")]) + await run_agent_async(runner_method, agent, list_input, session=session) - # Verify the error message explains the issue - assert "list inputs require a `RunConfig.session_input_callback" in str(exc_info.value) - assert "to manage the history manually" in str(exc_info.value) + assert model.last_turn_args["input"] == initial_history + list_input session.close()