Skip to content

Commit 0e3b8e0

Browse files
feat: add custom repr to message and content block types (#78)
1 parent 85275d4 commit 0e3b8e0

File tree

2 files changed

+309
-14
lines changed

2 files changed

+309
-14
lines changed

src/claude_agent_sdk/types.py

Lines changed: 92 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -731,38 +731,67 @@ class SandboxSettings(TypedDict, total=False):
731731

732732

733733
# Content block types
734-
@dataclass
734+
735+
736+
def _truncate(text: str, max_length: int = 80) -> str:
737+
"""Truncate text for repr display."""
738+
if len(text) <= max_length:
739+
return text
740+
return text[: max_length - 3] + "..."
741+
742+
743+
@dataclass(repr=False)
735744
class TextBlock:
736745
"""Text content block."""
737746

738747
text: str
739748

749+
def __repr__(self) -> str:
750+
return f"TextBlock(text='{_truncate(self.text)}')"
740751

741-
@dataclass
752+
753+
@dataclass(repr=False)
742754
class ThinkingBlock:
743755
"""Thinking content block."""
744756

745757
thinking: str
746758
signature: str
747759

760+
def __repr__(self) -> str:
761+
return f"ThinkingBlock(thinking='{_truncate(self.thinking)}')"
748762

749-
@dataclass
763+
764+
@dataclass(repr=False)
750765
class ToolUseBlock:
751766
"""Tool use content block."""
752767

753768
id: str
754769
name: str
755770
input: dict[str, Any]
756771

772+
def __repr__(self) -> str:
773+
return f"ToolUseBlock(id='{self.id}', name='{self.name}')"
757774

758-
@dataclass
775+
776+
@dataclass(repr=False)
759777
class ToolResultBlock:
760778
"""Tool result content block."""
761779

762780
tool_use_id: str
763781
content: str | list[dict[str, Any]] | None = None
764782
is_error: bool | None = None
765783

784+
def __repr__(self) -> str:
785+
parts = [f"tool_use_id='{self.tool_use_id}'"]
786+
if self.content is not None:
787+
if isinstance(self.content, str):
788+
parts.append(f"content='{_truncate(self.content)}'")
789+
else:
790+
parts.append(f"content={self.content!r}")
791+
if self.is_error:
792+
parts.append("is_error=True")
793+
return f"ToolResultBlock({', '.join(parts)})"
794+
766795

767796
ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlock
768797

@@ -778,7 +807,7 @@ class ToolResultBlock:
778807
]
779808

780809

781-
@dataclass
810+
@dataclass(repr=False)
782811
class UserMessage:
783812
"""User message."""
784813

@@ -787,8 +816,13 @@ class UserMessage:
787816
parent_tool_use_id: str | None = None
788817
tool_use_result: dict[str, Any] | None = None
789818

819+
def __repr__(self) -> str:
820+
if isinstance(self.content, str):
821+
return f"UserMessage(content='{_truncate(self.content)}')"
822+
return f"UserMessage(content={self.content!r})"
790823

791-
@dataclass
824+
825+
@dataclass(repr=False)
792826
class AssistantMessage:
793827
"""Assistant message with content blocks."""
794828

@@ -798,14 +832,23 @@ class AssistantMessage:
798832
error: AssistantMessageError | None = None
799833
usage: dict[str, Any] | None = None
800834

835+
def __repr__(self) -> str:
836+
parts = [f"model='{self.model}'", f"content={self.content!r}"]
837+
if self.error is not None:
838+
parts.append(f"error='{self.error}'")
839+
return f"AssistantMessage({', '.join(parts)})"
801840

802-
@dataclass
841+
842+
@dataclass(repr=False)
803843
class SystemMessage:
804844
"""System message with metadata."""
805845

806846
subtype: str
807847
data: dict[str, Any]
808848

849+
def __repr__(self) -> str:
850+
return f"SystemMessage(subtype='{self.subtype}')"
851+
809852

810853
class TaskUsage(TypedDict):
811854
"""Usage statistics reported in task_progress and task_notification messages."""
@@ -819,7 +862,7 @@ class TaskUsage(TypedDict):
819862
TaskNotificationStatus = Literal["completed", "failed", "stopped"]
820863

821864

822-
@dataclass
865+
@dataclass(repr=False)
823866
class TaskStartedMessage(SystemMessage):
824867
"""System message emitted when a task starts.
825868
@@ -835,8 +878,11 @@ class TaskStartedMessage(SystemMessage):
835878
tool_use_id: str | None = None
836879
task_type: str | None = None
837880

881+
def __repr__(self) -> str:
882+
return f"TaskStartedMessage(task_id='{self.task_id}', description='{_truncate(self.description)}')"
838883

839-
@dataclass
884+
885+
@dataclass(repr=False)
840886
class TaskProgressMessage(SystemMessage):
841887
"""System message emitted while a task is in progress.
842888
@@ -853,8 +899,11 @@ class TaskProgressMessage(SystemMessage):
853899
tool_use_id: str | None = None
854900
last_tool_name: str | None = None
855901

902+
def __repr__(self) -> str:
903+
return f"TaskProgressMessage(task_id='{self.task_id}', description='{_truncate(self.description)}')"
856904

857-
@dataclass
905+
906+
@dataclass(repr=False)
858907
class TaskNotificationMessage(SystemMessage):
859908
"""System message emitted when a task completes, fails, or is stopped.
860909
@@ -872,8 +921,13 @@ class TaskNotificationMessage(SystemMessage):
872921
tool_use_id: str | None = None
873922
usage: TaskUsage | None = None
874923

924+
def __repr__(self) -> str:
925+
return (
926+
f"TaskNotificationMessage(task_id='{self.task_id}', status='{self.status}')"
927+
)
875928

876-
@dataclass
929+
930+
@dataclass(repr=False)
877931
class ResultMessage:
878932
"""Result message with cost and usage information."""
879933

@@ -889,8 +943,18 @@ class ResultMessage:
889943
result: str | None = None
890944
structured_output: Any = None
891945

946+
def __repr__(self) -> str:
947+
parts = [f"num_turns={self.num_turns}"]
948+
if self.is_error:
949+
parts.append("is_error=True")
950+
if self.total_cost_usd is not None:
951+
parts.append(f"total_cost_usd={self.total_cost_usd}")
952+
if self.stop_reason is not None:
953+
parts.append(f"stop_reason='{self.stop_reason}'")
954+
return f"ResultMessage({', '.join(parts)})"
892955

893-
@dataclass
956+
957+
@dataclass(repr=False)
894958
class StreamEvent:
895959
"""Stream event for partial message updates during streaming."""
896960

@@ -899,6 +963,9 @@ class StreamEvent:
899963
event: dict[str, Any] # The raw Anthropic API stream event
900964
parent_tool_use_id: str | None = None
901965

966+
def __repr__(self) -> str:
967+
return f"StreamEvent(session_id='{self.session_id}')"
968+
902969

903970
# Rate limit types — see https://docs.claude.com/en/docs/claude-code/rate-limits
904971
RateLimitStatus = Literal["allowed", "allowed_warning", "rejected"]
@@ -907,7 +974,7 @@ class StreamEvent:
907974
]
908975

909976

910-
@dataclass
977+
@dataclass(repr=False)
911978
class RateLimitInfo:
912979
"""Rate limit status emitted by the CLI when rate limit state changes.
913980
@@ -932,8 +999,16 @@ class RateLimitInfo:
932999
overage_disabled_reason: str | None = None
9331000
raw: dict[str, Any] = field(default_factory=dict)
9341001

1002+
def __repr__(self) -> str:
1003+
parts = [f"status='{self.status}'"]
1004+
if self.utilization is not None:
1005+
parts.append(f"utilization={self.utilization}")
1006+
if self.rate_limit_type is not None:
1007+
parts.append(f"rate_limit_type='{self.rate_limit_type}'")
1008+
return f"RateLimitInfo({', '.join(parts)})"
9351009

936-
@dataclass
1010+
1011+
@dataclass(repr=False)
9371012
class RateLimitEvent:
9381013
"""Rate limit event emitted when rate limit info changes.
9391014
@@ -946,6 +1021,9 @@ class RateLimitEvent:
9461021
uuid: str
9471022
session_id: str
9481023

1024+
def __repr__(self) -> str:
1025+
return f"RateLimitEvent(status='{self.rate_limit_info.status}')"
1026+
9491027

9501028
Message = (
9511029
UserMessage

0 commit comments

Comments
 (0)