diff --git a/pydantic_ai_slim/pydantic_ai/_agent_graph.py b/pydantic_ai_slim/pydantic_ai/_agent_graph.py index b1a0dd1350..c7c05f4224 100644 --- a/pydantic_ai_slim/pydantic_ai/_agent_graph.py +++ b/pydantic_ai_slim/pydantic_ai/_agent_graph.py @@ -216,6 +216,12 @@ async def run( # noqa: C901 ctx.state.message_history = messages ctx.deps.new_message_index = len(messages) + # Validate that message history starts with a user message + if messages and isinstance(messages[0], _messages.ModelResponse): + raise exceptions.UserError( + 'Message history cannot start with a `ModelResponse`. Conversations must begin with a user message.' + ) + if self.deferred_tool_results is not None: return await self._handle_deferred_tool_results(self.deferred_tool_results, messages, ctx) diff --git a/tests/models/test_outlines.py b/tests/models/test_outlines.py index 73adc28853..3758e3c404 100644 --- a/tests/models/test_outlines.py +++ b/tests/models/test_outlines.py @@ -573,6 +573,7 @@ def test_input_format(transformers_multimodal_model: OutlinesModel, binary_image # unsupported: tool calls tool_call_message_history: list[ModelMessage] = [ + ModelRequest(parts=[UserPromptPart(content='some user prompt')]), ModelResponse(parts=[ToolCallPart(tool_call_id='1', tool_name='get_location')]), ModelRequest(parts=[ToolReturnPart(tool_name='get_location', content='London', tool_call_id='1')]), ] @@ -588,7 +589,8 @@ def test_input_format(transformers_multimodal_model: OutlinesModel, binary_image # unsupported: non-image file parts file_part_message_history: list[ModelMessage] = [ - ModelResponse(parts=[FilePart(content=BinaryContent(data=b'test', media_type='text/plain'))]) + ModelRequest(parts=[UserPromptPart(content='some user prompt')]), + ModelResponse(parts=[FilePart(content=BinaryContent(data=b'test', media_type='text/plain'))]), ] with pytest.raises( UserError, match='File parts other than `BinaryImage` are not supported for Outlines models yet.' diff --git a/tests/test_agent.py b/tests/test_agent.py index 29b54643f0..c2a513af47 100644 --- a/tests/test_agent.py +++ b/tests/test_agent.py @@ -6132,3 +6132,19 @@ def llm(messages: list[ModelMessage], _info: AgentInfo) -> ModelResponse: ] ) assert run.all_messages_json().startswith(b'[{"parts":[{"content":"Hello",') + + +def test_message_history_cannot_start_with_model_response(): + """Test that message history starting with ModelResponse raises UserError.""" + + agent = Agent('test') + + invalid_history = [ + ModelResponse(parts=[TextPart(content='ai response')]), + ] + + with pytest.raises( + UserError, + match='Message history cannot start with a `ModelResponse`.', + ): + agent.run_sync('hello', message_history=invalid_history)