Skip to main content

lc_agents/streaming/
state.rs

1//! Streaming tool-call states and events
2
3use serde_json::Value;
4
5/// Tool-call stream state
6#[derive(Debug, Clone)]
7pub enum ToolCallState {
8    /// Tool call started
9    Started {
10        /// Tool name
11        tool_name: String,
12        /// Call ID
13        call_id: String,
14    },
15    /// Arguments streaming in
16    ArgumentsStreaming {
17        /// Tool name
18        tool_name: String,
19        /// Call ID
20        call_id: String,
21        /// Partial arguments streamed so far
22        partial_args: String,
23    },
24    /// Arguments complete, ready to execute
25    ArgumentsComplete {
26        /// Tool name
27        tool_name: String,
28        /// Call ID
29        call_id: String,
30        /// Full tool arguments
31        args: Value,
32    },
33    /// Tool executing
34    Executing {
35        /// Tool name
36        tool_name: String,
37        /// Call ID
38        call_id: String,
39    },
40    /// Execution completed
41    Completed {
42        /// Tool name
43        tool_name: String,
44        /// Call ID
45        call_id: String,
46        /// Tool execution result
47        result: String,
48    },
49    /// Execution failed
50    Failed {
51        /// Tool name
52        tool_name: String,
53        /// Call ID
54        call_id: String,
55        /// Failure reason
56        error: String,
57    },
58}
59
60impl ToolCallState {
61    /// Returns the current tool name.
62    pub fn tool_name(&self) -> &str {
63        match self {
64            ToolCallState::Started { tool_name, .. }
65            | ToolCallState::ArgumentsStreaming { tool_name, .. }
66            | ToolCallState::ArgumentsComplete { tool_name, .. }
67            | ToolCallState::Executing { tool_name, .. }
68            | ToolCallState::Completed { tool_name, .. }
69            | ToolCallState::Failed { tool_name, .. } => tool_name,
70        }
71    }
72
73    /// Returns the current tool-call ID.
74    pub fn call_id(&self) -> &str {
75        match self {
76            ToolCallState::Started { call_id, .. }
77            | ToolCallState::ArgumentsStreaming { call_id, .. }
78            | ToolCallState::ArgumentsComplete { call_id, .. }
79            | ToolCallState::Executing { call_id, .. }
80            | ToolCallState::Completed { call_id, .. }
81            | ToolCallState::Failed { call_id, .. } => call_id,
82        }
83    }
84}
85
86/// Agent streaming event
87#[derive(Debug, Clone)]
88pub enum AgentStreamEvent {
89    /// LLM output text (token)
90    Text {
91        /// Output text content
92        content: String,
93    },
94
95    /// Tool-call state change (Function Calling style)
96    ToolCall {
97        /// Tool-call state
98        state: ToolCallState,
99    },
100
101    /// ReAct-style tool call start
102    ToolStart {
103        /// Tool name
104        name: String,
105        /// Tool input
106        input: String,
107    },
108
109    /// ReAct-style tool call completion
110    ToolEnd {
111        /// Tool name
112        name: String,
113        /// Tool output
114        output: String,
115    },
116
117    /// Pipeline step event (for RAG/research agents).
118    /// Indicates which stage of the pipeline is currently executing.
119    PipelineStep {
120        /// Step name (e.g., "retrieving", "grading", "generating", "planning", "searching", "synthesizing").
121        step: String,
122        /// Optional detail message.
123        detail: Option<String>,
124    },
125
126    /// Final answer
127    FinalAnswer {
128        /// Final answer content
129        content: String,
130    },
131
132    /// Streaming execution error
133    Error {
134        /// Error message
135        message: String,
136    },
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn test_tool_call_state_accessors() {
145        let s = ToolCallState::Started {
146            tool_name: "calc".to_string(),
147            call_id: "call_1".to_string(),
148        };
149        assert_eq!(s.tool_name(), "calc");
150        assert_eq!(s.call_id(), "call_1");
151    }
152
153    #[test]
154    fn test_completed_state() {
155        let s = ToolCallState::Completed {
156            tool_name: "search".to_string(),
157            call_id: "call_2".to_string(),
158            result: "结果".to_string(),
159        };
160        assert_eq!(s.tool_name(), "search");
161        assert_eq!(s.call_id(), "call_2");
162    }
163
164    #[test]
165    fn test_agent_stream_event_text() {
166        let e = AgentStreamEvent::Text {
167            content: "hello".to_string(),
168        };
169        assert!(matches!(e, AgentStreamEvent::Text { .. }));
170    }
171
172    #[test]
173    fn test_agent_stream_event_final() {
174        let e = AgentStreamEvent::FinalAnswer {
175            content: "done".to_string(),
176        };
177        assert!(matches!(e, AgentStreamEvent::FinalAnswer { .. }));
178    }
179
180    #[test]
181    fn test_agent_stream_event_error() {
182        let e = AgentStreamEvent::Error {
183            message: "stream failed".to_string(),
184        };
185        assert!(matches!(e, AgentStreamEvent::Error { .. }));
186    }
187}