Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -696,11 +696,34 @@ def _prepare_messages_for_anthropic(self, messages: Sequence[Message]) -> list[d

This skips the first message if it is a system message,
as Anthropic expects system instructions as a separate parameter.

Anthropic's API requires that the conversation ends with a user message.
If the last message is from the assistant, a synthetic user turn is
appended when it will not break Anthropic tool_use/tool_result pairing.
"""
# first system message is passed as instructions
if messages and isinstance(messages[0], Message) and messages[0].role == "system":
return [self._prepare_message_for_anthropic(msg) for msg in messages[1:]]
return [self._prepare_message_for_anthropic(msg) for msg in messages]
msgs = list(messages[1:])
else:
msgs = list(messages)

result = [self._prepare_message_for_anthropic(msg) for msg in msgs]

# Anthropic requires the conversation to end with a user message.
# Append a synthetic user turn so chained agent outputs work as
# valid context for the next agent without rewriting the assistant message.
if result and result[-1].get("role") == "assistant" and not self._message_has_tool_use(result[-1]):
result.append({"role": "user", "content": "Continue"})
Comment thread
hanhan761 marked this conversation as resolved.

return result

def _message_has_tool_use(self, message: dict[str, Any]) -> bool:
"""Return whether an Anthropic message contains tool_use blocks."""
content = message.get("content")
return isinstance(content, list) and any(
isinstance(item, dict) and item.get("type") in {"tool_use", "mcp_tool_use", "server_tool_use"}
for item in content
)

def _prepare_message_for_anthropic(self, message: Message) -> dict[str, Any]:
"""Prepare a Message for the Anthropic client.
Expand Down
30 changes: 29 additions & 1 deletion python/packages/anthropic/tests/test_anthropic_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -618,9 +618,37 @@ def test_prepare_messages_for_anthropic_without_system(

result = client._prepare_messages_for_anthropic(messages)

assert len(result) == 2
assert len(result) == 3
assert result[0]["role"] == "user"
assert result[1]["role"] == "assistant"
assert result[2]["role"] == "user"
assert result[2]["content"] == "Continue"


def test_prepare_messages_for_anthropic_does_not_append_after_tool_use(
mock_anthropic_client: MagicMock,
) -> None:
"""Do not append plain user text after assistant tool_use blocks."""
client = create_test_anthropic_client(mock_anthropic_client)
messages = [
Message(role="user", contents=["What's the weather?"]),
Message(
role="assistant",
contents=[
Content.from_function_call(
call_id="call_123",
name="get_weather",
arguments={"location": "Seattle"},
)
],
),
]

result = client._prepare_messages_for_anthropic(messages)

assert len(result) == 2
assert result[1]["role"] == "assistant"
assert result[1]["content"][0]["type"] == "tool_use"


# Tool Conversion Tests
Expand Down
Loading