Skip to main content

crabtalk_core/agent/
event.rs

1//! Agent event types for step-based execution and streaming.
2//!
3//! Two-level design:
4//! - [`AgentStep`]: data record of one LLM round (response + tool dispatch).
5//! - [`AgentEvent`]: fine-grained streaming enum for real-time UI updates.
6//! - [`AgentResponse`]: final result after a full agent run.
7//! - [`AgentStopReason`]: why the agent stopped.
8
9use crate::model::{Message, Response, ToolCall};
10
11/// A fine-grained event emitted during agent execution.
12///
13/// Yielded by `Agent::run_stream()` or emitted via `Hook::on_event()`
14/// for real-time status reporting to clients.
15#[derive(Debug, Clone)]
16pub enum AgentEvent {
17    /// Text content delta from the model.
18    TextDelta(String),
19    /// Thinking/reasoning content delta from the model.
20    ThinkingDelta(String),
21    /// Early notification: model is generating tool calls (names only, args incomplete).
22    ToolCallsBegin(Vec<ToolCall>),
23    /// Model is calling tools (with the complete tool calls).
24    ToolCallsStart(Vec<ToolCall>),
25    /// A single tool completed execution.
26    ToolResult {
27        /// The tool call ID this result belongs to.
28        call_id: String,
29        /// The output from the tool.
30        output: String,
31        /// Wall-clock duration of the tool dispatch in milliseconds.
32        duration_ms: u64,
33    },
34    /// All tools completed, continuing to next iteration.
35    ToolCallsComplete,
36    /// Context was compacted — carries the compaction summary.
37    Compact { summary: String },
38    /// Agent finished with final response.
39    Done(AgentResponse),
40}
41
42/// Data record of one LLM round (one model call + tool dispatch).
43#[derive(Debug, Clone)]
44pub struct AgentStep {
45    /// The model's response for this step.
46    pub response: Response,
47    /// Tool calls made in this step (if any).
48    pub tool_calls: Vec<ToolCall>,
49    /// Results from tool executions as messages.
50    pub tool_results: Vec<Message>,
51}
52
53/// Final response from a complete agent run.
54#[derive(Debug, Clone)]
55pub struct AgentResponse {
56    /// All steps taken during execution.
57    pub steps: Vec<AgentStep>,
58    /// Final text response (if any).
59    pub final_response: Option<String>,
60    /// Total number of iterations performed.
61    pub iterations: usize,
62    /// Why the agent stopped.
63    pub stop_reason: AgentStopReason,
64    /// The requested model name (from config, not the API-echoed value).
65    pub model: String,
66}
67
68/// Why the agent stopped executing.
69#[derive(Debug, Clone, PartialEq)]
70pub enum AgentStopReason {
71    /// Model produced a text response with no tool calls.
72    TextResponse,
73    /// Maximum iterations reached.
74    MaxIterations,
75    /// No tool calls and no text response.
76    NoAction,
77    /// Error during execution.
78    Error(String),
79}
80
81impl std::fmt::Display for AgentStopReason {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        match self {
84            Self::TextResponse => write!(f, "text_response"),
85            Self::MaxIterations => write!(f, "max_iterations"),
86            Self::NoAction => write!(f, "no_action"),
87            Self::Error(msg) => write!(f, "error: {msg}"),
88        }
89    }
90}