"""Agent event dataclasses — emitted during run_stream() and forwarded to WebSocket clients."""
from dataclasses import dataclass, field
@dataclass
class ToolStarted:
"""Emitted immediately when a tool call begins, before execution completes."""
tool_name: str
arguments: dict
is_subagent: bool = False # True when emitted from inside run_ephemeral
@dataclass
class ToolEvent:
"""Emitted when a tool call finishes — carries the result."""
tool_name: str
arguments: dict
result: str
success: bool
is_subagent: bool = False # True when emitted from inside run_ephemeral
@dataclass
class TextDelta:
"""A chunk of text from the streaming LLM response."""
delta: str
@dataclass
class ThinkingDelta:
"""A chunk of thinking/reasoning text from the streaming LLM response."""
delta: str
@dataclass
class ThinkingEnd:
"""Marks the end of the thinking phase."""
@dataclass
class StreamEnd:
"""Marks the end of the streaming response."""
full_content: str
context_tokens: int | None = None # total tokens used in this turn
max_context_tokens: int = 0 # ollama_num_ctx from config
@dataclass
class ContextCompressed:
"""Emitted after context compression runs successfully."""
messages_before: int
messages_after: int
@dataclass
class TurnThinking:
"""Full thinking/reasoning block from a tool-calling turn (complete() response).
Unlike ThinkingDelta (which streams chunks), this carries the full text at once
because complete() is non-streaming. Emitted before tool calls for that turn.
is_subagent=True when emitted from run_ephemeral().
"""
thinking: str
is_subagent: bool = False
AgentEvent = (
ToolStarted | ToolEvent | TextDelta | ThinkingDelta | ThinkingEnd
| StreamEnd | ContextCompressed | TurnThinking
)