From 82ac5df92ec5ae043062d8e47771c039ed7a04e4 Mon Sep 17 00:00:00 2001 From: yokomotod Date: Thu, 19 Jun 2025 21:00:07 +0900 Subject: [PATCH 1/6] fix: handle content.thinking --- src/claude_code_sdk/_internal/client.py | 8 ++++++++ src/claude_code_sdk/types.py | 8 ++++++++ tests/test_types.py | 16 +++++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/claude_code_sdk/_internal/client.py b/src/claude_code_sdk/_internal/client.py index ef1070d00..15704d4a0 100644 --- a/src/claude_code_sdk/_internal/client.py +++ b/src/claude_code_sdk/_internal/client.py @@ -11,6 +11,7 @@ ResultMessage, SystemMessage, TextBlock, + ThinkingBlock, ToolResultBlock, ToolUseBlock, UserMessage, @@ -55,6 +56,13 @@ def _parse_message(self, data: dict[str, Any]) -> Message | None: match block["type"]: case "text": content_blocks.append(TextBlock(text=block["text"])) + case "thinking": + content_blocks.append( + ThinkingBlock( + thinking=block["thinking"], + signature=block["signature"], + ) + ) case "tool_use": content_blocks.append( ToolUseBlock( diff --git a/src/claude_code_sdk/types.py b/src/claude_code_sdk/types.py index 21ae8ffb2..58509d70d 100644 --- a/src/claude_code_sdk/types.py +++ b/src/claude_code_sdk/types.py @@ -26,6 +26,14 @@ class TextBlock: text: str +@dataclass +class ThinkingBlock: + """Thinking content block.""" + + thinking: str + signature: str + + @dataclass class ToolUseBlock: """Tool use content block.""" diff --git a/tests/test_types.py b/tests/test_types.py index 604629235..0d0308a4a 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -5,7 +5,13 @@ ClaudeCodeOptions, ResultMessage, ) -from claude_code_sdk.types import TextBlock, ToolResultBlock, ToolUseBlock, UserMessage +from claude_code_sdk.types import ( + TextBlock, + ThinkingBlock, + ToolResultBlock, + ToolUseBlock, + UserMessage, +) class TestMessageTypes: @@ -23,6 +29,14 @@ def test_assistant_message_with_text(self): assert len(msg.content) == 1 assert msg.content[0].text == "Hello, human!" + def test_assistant_message_with_thinking(self): + """Test creating an AssistantMessage with thinking content.""" + thinking_block = ThinkingBlock(thinking="I'm thinking...", signature="sig-123") + msg = AssistantMessage(content=[thinking_block]) + assert len(msg.content) == 1 + assert msg.content[0].thinking == "I'm thinking..." + assert msg.content[0].signature == "sig-123" + def test_tool_use_block(self): """Test creating a ToolUseBlock.""" block = ToolUseBlock( From 232af0757ad76c51fae3b1cd83c08c7f769eba17 Mon Sep 17 00:00:00 2001 From: yokomotod Date: Sat, 12 Jul 2025 15:38:14 +0900 Subject: [PATCH 2/6] fix type --- src/claude_code_sdk/__init__.py | 2 ++ src/claude_code_sdk/types.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/claude_code_sdk/__init__.py b/src/claude_code_sdk/__init__.py index b8a115259..f9b971cc8 100644 --- a/src/claude_code_sdk/__init__.py +++ b/src/claude_code_sdk/__init__.py @@ -21,6 +21,7 @@ ResultMessage, SystemMessage, TextBlock, + ThinkingBlock, ToolResultBlock, ToolUseBlock, UserMessage, @@ -41,6 +42,7 @@ "Message", "ClaudeCodeOptions", "TextBlock", + "ThinkingBlock", "ToolUseBlock", "ToolResultBlock", "ContentBlock", diff --git a/src/claude_code_sdk/types.py b/src/claude_code_sdk/types.py index 4aabc9cf0..744f3c3d5 100644 --- a/src/claude_code_sdk/types.py +++ b/src/claude_code_sdk/types.py @@ -73,7 +73,7 @@ class ToolResultBlock: is_error: bool | None = None -ContentBlock = TextBlock | ToolUseBlock | ToolResultBlock +ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlock # Message types From 627d1e98feb08aa4fac371b9f8e16df66d04a013 Mon Sep 17 00:00:00 2001 From: Dickson Tsai Date: Wed, 6 Aug 2025 13:42:11 -0700 Subject: [PATCH 3/6] Tack on a change for parsing model --- src/claude_code_sdk/_internal/message_parser.py | 4 +++- src/claude_code_sdk/types.py | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/claude_code_sdk/_internal/message_parser.py b/src/claude_code_sdk/_internal/message_parser.py index 226a3be57..71736502e 100644 --- a/src/claude_code_sdk/_internal/message_parser.py +++ b/src/claude_code_sdk/_internal/message_parser.py @@ -108,7 +108,9 @@ def parse_message(data: dict[str, Any]) -> Message: ) ) - return AssistantMessage(content=content_blocks) + return AssistantMessage( + content=content_blocks, model=data["message"]["model"] + ) except KeyError as e: raise MessageParseError( f"Missing required field in assistant message: {e}", data diff --git a/src/claude_code_sdk/types.py b/src/claude_code_sdk/types.py index 092723d60..fc047dfdb 100644 --- a/src/claude_code_sdk/types.py +++ b/src/claude_code_sdk/types.py @@ -89,6 +89,7 @@ class AssistantMessage: """Assistant message with content blocks.""" content: list[ContentBlock] + model: str @dataclass From 94d2feb2ee07c1b5ce712abad7b6c77373af3cac Mon Sep 17 00:00:00 2001 From: Dickson Tsai Date: Wed, 6 Aug 2025 14:35:10 -0700 Subject: [PATCH 4/6] Fix AssistantMessage mocking --- tests/test_client.py | 5 +++-- tests/test_integration.py | 3 +++ tests/test_streaming_client.py | 6 ++++++ tests/test_types.py | 4 ++-- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index 3282ea1a8..07f386104 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -20,7 +20,7 @@ async def _test(): ) as mock_process: # Mock the async generator async def mock_generator(): - yield AssistantMessage(content=[TextBlock(text="4")]) + yield AssistantMessage(content=[TextBlock(text="4")], model="claude-opus-4-1-20250805") mock_process.return_value = mock_generator() @@ -43,7 +43,7 @@ async def _test(): ) as mock_process: async def mock_generator(): - yield AssistantMessage(content=[TextBlock(text="Hello!")]) + yield AssistantMessage(content=[TextBlock(text="Hello!")], model="claude-opus-4-1-20250805") mock_process.return_value = mock_generator() @@ -83,6 +83,7 @@ async def mock_receive(): "message": { "role": "assistant", "content": [{"type": "text", "text": "Done"}], + "model": "claude-opus-4-1-20250805", }, } yield { diff --git a/tests/test_integration.py b/tests/test_integration.py index a185335f4..aa6d12e53 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -38,6 +38,7 @@ async def mock_receive(): "message": { "role": "assistant", "content": [{"type": "text", "text": "2 + 2 equals 4"}], + "model": "claude-opus-4-1-20250805", }, } yield { @@ -103,6 +104,7 @@ async def mock_receive(): "input": {"file_path": "/test.txt"}, }, ], + "model": "claude-opus-4-1-20250805", }, } yield { @@ -179,6 +181,7 @@ async def mock_receive(): "text": "Continuing from previous conversation", } ], + "model": "claude-opus-4-1-20250805", }, } diff --git a/tests/test_streaming_client.py b/tests/test_streaming_client.py index 884d7c4e7..a9c2bb3c0 100644 --- a/tests/test_streaming_client.py +++ b/tests/test_streaming_client.py @@ -187,6 +187,7 @@ async def mock_receive(): "message": { "role": "assistant", "content": [{"type": "text", "text": "Hello!"}], + "model": "claude-opus-4-1-20250805", }, } yield { @@ -229,6 +230,7 @@ async def mock_receive(): "message": { "role": "assistant", "content": [{"type": "text", "text": "Answer"}], + "model": "claude-opus-4-1-20250805", }, } yield { @@ -250,6 +252,7 @@ async def mock_receive(): {"type": "text", "text": "Should not see this"} ], }, + "model": "claude-opus-4-1-20250805", } mock_transport.receive_messages = mock_receive @@ -335,6 +338,7 @@ async def mock_receive(): "message": { "role": "assistant", "content": [{"type": "text", "text": "Response 1"}], + "model": "claude-opus-4-1-20250805", }, } await asyncio.sleep(0.1) @@ -531,6 +535,7 @@ async def mock_receive(): "message": { "role": "assistant", "content": [{"type": "text", "text": "Hello"}], + "model": "claude-opus-4-1-20250805", }, } yield { @@ -538,6 +543,7 @@ async def mock_receive(): "message": { "role": "assistant", "content": [{"type": "text", "text": "World"}], + "model": "claude-opus-4-1-20250805", }, } yield { diff --git a/tests/test_types.py b/tests/test_types.py index 0d0308a4a..514524092 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -25,14 +25,14 @@ def test_user_message_creation(self): def test_assistant_message_with_text(self): """Test creating an AssistantMessage with text content.""" text_block = TextBlock(text="Hello, human!") - msg = AssistantMessage(content=[text_block]) + msg = AssistantMessage(content=[text_block], model="claude-opus-4-1-20250805") assert len(msg.content) == 1 assert msg.content[0].text == "Hello, human!" def test_assistant_message_with_thinking(self): """Test creating an AssistantMessage with thinking content.""" thinking_block = ThinkingBlock(thinking="I'm thinking...", signature="sig-123") - msg = AssistantMessage(content=[thinking_block]) + msg = AssistantMessage(content=[thinking_block], model="claude-opus-4-1-20250805") assert len(msg.content) == 1 assert msg.content[0].thinking == "I'm thinking..." assert msg.content[0].signature == "sig-123" From a421eed3e45380091c56653625d01e8ddc5738e5 Mon Sep 17 00:00:00 2001 From: Dickson Tsai Date: Wed, 6 Aug 2025 14:37:36 -0700 Subject: [PATCH 5/6] Ruff --- tests/test_client.py | 9 +++++++-- tests/test_types.py | 4 +++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index 07f386104..81560100c 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -20,7 +20,9 @@ async def _test(): ) as mock_process: # Mock the async generator async def mock_generator(): - yield AssistantMessage(content=[TextBlock(text="4")], model="claude-opus-4-1-20250805") + yield AssistantMessage( + content=[TextBlock(text="4")], model="claude-opus-4-1-20250805" + ) mock_process.return_value = mock_generator() @@ -43,7 +45,10 @@ async def _test(): ) as mock_process: async def mock_generator(): - yield AssistantMessage(content=[TextBlock(text="Hello!")], model="claude-opus-4-1-20250805") + yield AssistantMessage( + content=[TextBlock(text="Hello!")], + model="claude-opus-4-1-20250805", + ) mock_process.return_value = mock_generator() diff --git a/tests/test_types.py b/tests/test_types.py index 514524092..3e4f58904 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -32,7 +32,9 @@ def test_assistant_message_with_text(self): def test_assistant_message_with_thinking(self): """Test creating an AssistantMessage with thinking content.""" thinking_block = ThinkingBlock(thinking="I'm thinking...", signature="sig-123") - msg = AssistantMessage(content=[thinking_block], model="claude-opus-4-1-20250805") + msg = AssistantMessage( + content=[thinking_block], model="claude-opus-4-1-20250805" + ) assert len(msg.content) == 1 assert msg.content[0].thinking == "I'm thinking..." assert msg.content[0].signature == "sig-123" From 6ae1001d831982d2056814e769460eb2b4bef456 Mon Sep 17 00:00:00 2001 From: Dickson Tsai Date: Wed, 6 Aug 2025 14:40:30 -0700 Subject: [PATCH 6/6] add missing test fix --- tests/test_message_parser.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_message_parser.py b/tests/test_message_parser.py index b64753014..4e0892ee7 100644 --- a/tests/test_message_parser.py +++ b/tests/test_message_parser.py @@ -142,7 +142,8 @@ def test_parse_valid_assistant_message(self): "name": "Read", "input": {"file_path": "/test.txt"}, }, - ] + ], + "model": "claude-opus-4-1-20250805", }, } message = parse_message(data)