mirror of
https://github.com/anthropics/claude-agent-sdk-python.git
synced 2025-10-06 01:00:03 +03:00
154 lines
5.5 KiB
Python
154 lines
5.5 KiB
Python
"""End-to-end tests for include_partial_messages option with real Claude API calls.
|
|
|
|
These tests verify that the SDK properly handles partial message streaming,
|
|
including StreamEvent parsing and message interleaving.
|
|
"""
|
|
|
|
import asyncio
|
|
from typing import List, Any
|
|
|
|
import pytest
|
|
|
|
from claude_agent_sdk import ClaudeSDKClient
|
|
from claude_agent_sdk.types import (
|
|
ClaudeAgentOptions,
|
|
StreamEvent,
|
|
AssistantMessage,
|
|
SystemMessage,
|
|
ResultMessage,
|
|
ThinkingBlock,
|
|
TextBlock,
|
|
)
|
|
|
|
|
|
@pytest.mark.e2e
|
|
@pytest.mark.asyncio
|
|
async def test_include_partial_messages_stream_events():
|
|
"""Test that include_partial_messages produces StreamEvent messages."""
|
|
|
|
options = ClaudeAgentOptions(
|
|
include_partial_messages=True,
|
|
model="claude-sonnet-4-20250514",
|
|
max_turns=2,
|
|
env={
|
|
"MAX_THINKING_TOKENS": "8000",
|
|
},
|
|
)
|
|
|
|
collected_messages: List[Any] = []
|
|
|
|
async with ClaudeSDKClient(options) as client:
|
|
# Send a simple prompt that will generate streaming response with thinking
|
|
await client.query("Think of three jokes, then tell one")
|
|
|
|
async for message in client.receive_response():
|
|
collected_messages.append(message)
|
|
|
|
# Verify we got the expected message types
|
|
message_types = [type(msg).__name__ for msg in collected_messages]
|
|
|
|
# Should have SystemMessage(init) at the start
|
|
assert message_types[0] == "SystemMessage"
|
|
assert isinstance(collected_messages[0], SystemMessage)
|
|
assert collected_messages[0].subtype == "init"
|
|
|
|
# Should have multiple StreamEvent messages
|
|
stream_events = [msg for msg in collected_messages if isinstance(msg, StreamEvent)]
|
|
assert len(stream_events) > 0, "No StreamEvent messages received"
|
|
|
|
# Check for expected StreamEvent types
|
|
event_types = [event.event.get("type") for event in stream_events]
|
|
assert "message_start" in event_types, "No message_start StreamEvent"
|
|
assert "content_block_start" in event_types, "No content_block_start StreamEvent"
|
|
assert "content_block_delta" in event_types, "No content_block_delta StreamEvent"
|
|
assert "content_block_stop" in event_types, "No content_block_stop StreamEvent"
|
|
assert "message_stop" in event_types, "No message_stop StreamEvent"
|
|
|
|
# Should have AssistantMessage messages with thinking and text
|
|
assistant_messages = [msg for msg in collected_messages if isinstance(msg, AssistantMessage)]
|
|
assert len(assistant_messages) >= 1, "No AssistantMessage received"
|
|
|
|
# Check for thinking block in at least one AssistantMessage
|
|
has_thinking = any(
|
|
any(isinstance(block, ThinkingBlock) for block in msg.content)
|
|
for msg in assistant_messages
|
|
)
|
|
assert has_thinking, "No ThinkingBlock found in AssistantMessages"
|
|
|
|
# Check for text block (the joke) in at least one AssistantMessage
|
|
has_text = any(
|
|
any(isinstance(block, TextBlock) for block in msg.content)
|
|
for msg in assistant_messages
|
|
)
|
|
assert has_text, "No TextBlock found in AssistantMessages"
|
|
|
|
# Should end with ResultMessage
|
|
assert isinstance(collected_messages[-1], ResultMessage)
|
|
assert collected_messages[-1].subtype == "success"
|
|
|
|
|
|
@pytest.mark.e2e
|
|
@pytest.mark.asyncio
|
|
async def test_include_partial_messages_thinking_deltas():
|
|
"""Test that thinking content is streamed incrementally via deltas."""
|
|
|
|
options = ClaudeAgentOptions(
|
|
include_partial_messages=True,
|
|
model="claude-sonnet-4-20250514",
|
|
max_turns=2,
|
|
env={
|
|
"MAX_THINKING_TOKENS": "8000",
|
|
},
|
|
)
|
|
|
|
thinking_deltas = []
|
|
|
|
async with ClaudeSDKClient(options) as client:
|
|
await client.query("Think step by step about what 2 + 2 equals")
|
|
|
|
async for message in client.receive_response():
|
|
if isinstance(message, StreamEvent):
|
|
event = message.event
|
|
if event.get("type") == "content_block_delta":
|
|
delta = event.get("delta", {})
|
|
if delta.get("type") == "thinking_delta":
|
|
thinking_deltas.append(delta.get("thinking", ""))
|
|
|
|
# Should have received multiple thinking deltas
|
|
assert len(thinking_deltas) > 0, "No thinking deltas received"
|
|
|
|
# Combined thinking should form coherent text
|
|
combined_thinking = "".join(thinking_deltas)
|
|
assert len(combined_thinking) > 10, "Thinking content too short"
|
|
|
|
# Should contain some reasoning about the calculation
|
|
assert "2" in combined_thinking.lower(), "Thinking doesn't mention the numbers"
|
|
|
|
|
|
@pytest.mark.e2e
|
|
@pytest.mark.asyncio
|
|
async def test_partial_messages_disabled_by_default():
|
|
"""Test that partial messages are not included when option is not set."""
|
|
|
|
options = ClaudeAgentOptions(
|
|
# include_partial_messages not set (defaults to False)
|
|
model="claude-sonnet-4-20250514",
|
|
max_turns=2,
|
|
)
|
|
|
|
collected_messages: List[Any] = []
|
|
|
|
async with ClaudeSDKClient(options) as client:
|
|
await client.query("Say hello")
|
|
|
|
async for message in client.receive_response():
|
|
collected_messages.append(message)
|
|
|
|
# Should NOT have any StreamEvent messages
|
|
stream_events = [msg for msg in collected_messages if isinstance(msg, StreamEvent)]
|
|
assert len(stream_events) == 0, "StreamEvent messages present when partial messages disabled"
|
|
|
|
# Should still have the regular messages
|
|
assert any(isinstance(msg, SystemMessage) for msg in collected_messages)
|
|
assert any(isinstance(msg, AssistantMessage) for msg in collected_messages)
|
|
assert any(isinstance(msg, ResultMessage) for msg in collected_messages) |