Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pydantic_ai_slim/pydantic_ai/_agent_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
4 changes: 3 additions & 1 deletion tests/models/test_outlines.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')]),
]
Expand All @@ -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.'
Expand Down
16 changes: 16 additions & 0 deletions tests/test_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)