from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional
@dataclass
class TextContent:
text: str
type: str = "text"
@dataclass
class ToolUseBlock:
id: str
name: str
input: Dict[str, Any]
type: str = "tool_use"
@dataclass
class ToolResultBlock:
tool_use_id: str
content: str
type: str = "tool_result"
@dataclass
class AssistantMessage:
type: str content: List[Any] session_id: str = ""
def text(self) -> str:
parts = [b.text for b in self.content if isinstance(b, TextContent)]
return "".join(parts)
@dataclass
class UserMessage:
type: str content: str
session_id: str = ""
@dataclass
class SystemMessage:
type: str subtype: str = ""
data: Dict[str, Any] = field(default_factory=dict)
@dataclass
class PartialAssistantMessage:
type: str text: str
step: int = 0
session_id: str = ""
@dataclass
class ToolProgressMessage:
type: str tool_use_id: str
tool_name: str
elapsed_ms: int
session_id: str = ""
@dataclass
class UsageMeta:
input_tokens: int = 0
output_tokens: int = 0
cache_creation_tokens: Optional[int] = None
cache_read_tokens: Optional[int] = None
reasoning_tokens: Optional[int] = None
def _map_finish_reason_to_subtype(finish_reason: Optional[str], status: str) -> str:
if status == "cancelled":
return "cancelled"
if status != "finished" or finish_reason is None:
if finish_reason and (
"BudgetExceeded" in finish_reason or "TranscriptLimit" in finish_reason
):
return "error_max_turns"
return "error_during_execution"
if "NoMoreToolCalls" in finish_reason or "PlanPending" in finish_reason:
return "success"
if "BudgetExceeded" in finish_reason or "TranscriptLimit" in finish_reason:
return "error_max_turns"
if "Cancelled" in finish_reason:
return "cancelled"
return "success"
@dataclass
class RunResult:
id: str
status: str
finish_reason: Optional[str] = None
usage: Optional[UsageMeta] = None
error: Optional[str] = None
result: Optional[str] = None
num_turns: int = 0
duration_ms: Optional[int] = None
@property
def ok(self) -> bool:
return self.status == "finished"
@property
def subtype(self) -> str:
return _map_finish_reason_to_subtype(self.finish_reason, self.status)
@dataclass
class SessionInfo:
id: str
created_at: str
message_count: int
last_prompt: Optional[str] = None
first_prompt: Optional[str] = None
goal: Optional[str] = None
@dataclass
class GoalState:
condition: str
status: str
turns: int = 0
max_turns: int = 20
last_reason: Optional[str] = None