1use crate::ai::{model::Model, ImageData, ReasoningData, TokenUsage, ToolUseData};
2use crate::modules::task_list::TaskList;
3use crate::persistence::session::SessionMetadata;
4use chrono::Utc;
5use schemars::schema::RootSchema;
6use serde::{Deserialize, Serialize};
7use std::sync::{Arc, Mutex};
8use std::time::Duration;
9use tokio::sync::mpsc;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
19#[serde(tag = "kind", content = "data")]
20pub enum ChatEvent {
21 MessageAdded(ChatMessage),
22 StreamStart {
23 message_id: String,
24 agent: String,
25 model: Model,
26 },
27 StreamDelta {
28 message_id: String,
29 text: String,
30 },
31 StreamReasoningDelta {
32 message_id: String,
33 text: String,
34 },
35 StreamEnd {
36 message: ChatMessage,
37 },
38 Settings(serde_json::Value),
39 TypingStatusChanged(bool),
40 ConversationCleared,
41 ToolRequest(ToolRequest),
42 ToolExecutionCompleted {
43 tool_call_id: String,
44 tool_name: String,
45 tool_result: ToolExecutionResult,
46 success: bool,
47 error: Option<String>,
48 },
49 OperationCancelled {
50 message: String,
51 },
52 RetryAttempt {
53 attempt: u32,
54 max_retries: u32,
55 error: String,
56 backoff_ms: u64,
57 },
58 TaskUpdate(TaskList),
59 SessionsList {
60 sessions: Vec<SessionMetadata>,
61 },
62 ProfilesList {
63 profiles: Vec<String>,
64 },
65 TimingUpdate {
66 waiting_for_human: Duration,
67 ai_processing: Duration,
68 tool_execution: Duration,
69 },
70 ModuleSchemas {
71 schemas: Vec<ModuleSchemaInfo>,
72 },
73 Error(String),
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct ModuleSchemaInfo {
78 pub namespace: String,
79 pub schema: RootSchema,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct ChatMessage {
84 pub timestamp: u64,
85 pub sender: MessageSender,
86 pub content: String,
87 pub reasoning: Option<ReasoningData>,
88 pub tool_calls: Vec<ToolUseData>,
89 pub model_info: Option<ModelInfo>,
90 pub token_usage: Option<TokenUsage>,
91 #[serde(default)]
92 pub images: Vec<ImageData>,
93}
94
95impl ChatMessage {
96 pub fn user(content: String) -> Self {
97 Self {
98 timestamp: Utc::now().timestamp_millis() as u64,
99 sender: MessageSender::User,
100 content,
101 reasoning: None,
102 tool_calls: vec![],
103 model_info: None,
104 token_usage: None,
105 images: vec![],
106 }
107 }
108
109 pub fn user_with_images(content: String, images: Vec<ImageData>) -> Self {
110 Self {
111 timestamp: Utc::now().timestamp_millis() as u64,
112 sender: MessageSender::User,
113 content,
114 reasoning: None,
115 tool_calls: vec![],
116 model_info: None,
117 token_usage: None,
118 images,
119 }
120 }
121
122 pub fn assistant(
123 agent: String,
124 content: String,
125 tool_calls: Vec<ToolUseData>,
126 model_info: ModelInfo,
127 token_usage: TokenUsage,
128 reasoning: Option<ReasoningData>,
129 ) -> Self {
130 Self {
131 timestamp: Utc::now().timestamp_millis() as u64,
132 sender: MessageSender::Assistant { agent },
133 content,
134 reasoning,
135 tool_calls,
136 model_info: Some(model_info),
137 token_usage: Some(token_usage),
138 images: vec![],
139 }
140 }
141
142 pub fn system(content: String) -> Self {
143 Self {
144 timestamp: Utc::now().timestamp_millis() as u64,
145 sender: MessageSender::System,
146 content,
147 reasoning: None,
148 tool_calls: vec![],
149 model_info: None,
150 token_usage: None,
151 images: vec![],
152 }
153 }
154
155 pub fn warning(content: String) -> Self {
156 Self {
157 timestamp: Utc::now().timestamp_millis() as u64,
158 sender: MessageSender::Warning,
159 content,
160 reasoning: None,
161 tool_calls: vec![],
162 model_info: None,
163 token_usage: None,
164 images: vec![],
165 }
166 }
167
168 pub fn error(content: String) -> Self {
169 Self {
170 timestamp: Utc::now().timestamp_millis() as u64,
171 sender: MessageSender::Error,
172 content,
173 reasoning: None,
174 tool_calls: vec![],
175 model_info: None,
176 token_usage: None,
177 images: vec![],
178 }
179 }
180}
181
182#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct FileInfo {
184 pub path: String,
185 pub bytes: usize,
186}
187
188#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct ModelInfo {
190 pub model: Model,
191}
192
193#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
194pub enum MessageSender {
195 User,
196 Assistant { agent: String },
197 System,
198 Warning,
199 Error,
200}
201
202#[derive(Debug, Clone, Serialize, Deserialize)]
203pub struct ToolRequest {
204 pub tool_call_id: String,
205 pub tool_name: String,
206
207 pub tool_type: ToolRequestType,
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize)]
211#[serde(tag = "kind")]
212pub enum ToolRequestType {
213 ModifyFile {
214 file_path: String,
215 before: String,
216 after: String,
217 },
218 RunCommand {
219 command: String,
220 working_directory: String,
221 },
222 ReadFiles {
223 file_paths: Vec<String>,
224 },
225 Other {
226 args: serde_json::Value,
227 },
228 SearchTypes {
229 language: String,
230 workspace_root: String,
231 type_name: String,
232 },
233 GetTypeDocs {
234 language: String,
235 workspace_root: String,
236 type_path: String,
237 },
238}
239
240#[derive(Debug, Clone, Serialize, Deserialize)]
241#[serde(tag = "kind")]
242pub enum ToolExecutionResult {
243 ModifyFile {
244 lines_added: u32,
245 lines_removed: u32,
246 },
247 RunCommand {
248 exit_code: i32,
249 stdout: String,
250 stderr: String,
251 },
252 ReadFiles {
253 files: Vec<FileInfo>,
254 },
255 SearchTypes {
256 types: Vec<String>,
257 },
258 GetTypeDocs {
259 documentation: String,
260 },
261 Error {
262 short_message: String,
263 detailed_message: String,
264 },
265 Other {
266 result: serde_json::Value,
267 },
268}
269
270#[derive(Clone)]
272pub struct EventSender {
273 event_tx: mpsc::UnboundedSender<ChatEvent>,
274 event_history: Arc<Mutex<Vec<ChatEvent>>>,
275}
276
277impl EventSender {
278 pub fn new() -> (Self, mpsc::UnboundedReceiver<ChatEvent>) {
279 let (event_tx, rx) = mpsc::unbounded_channel();
280 (
281 Self {
282 event_tx,
283 event_history: Arc::new(Mutex::new(Vec::new())),
284 },
285 rx,
286 )
287 }
288
289 pub fn add_message(&self, message: ChatMessage) {
290 let _ = self.event_tx.send(ChatEvent::MessageAdded(message));
291 }
292
293 pub fn set_typing(&self, typing: bool) {
294 let _ = self.event_tx.send(ChatEvent::TypingStatusChanged(typing));
295 }
296
297 pub fn clear_conversation(&self) {
298 let _ = self.event_tx.send(ChatEvent::ConversationCleared);
299 }
300
301 pub fn send(&self, event: ChatEvent) {
302 self.event_history.lock().unwrap().push(event.clone());
303 let _ = self.event_tx.send(event);
304 }
305
306 pub fn send_message(&self, message: ChatMessage) {
307 let event = ChatEvent::MessageAdded(message);
308 self.send(event);
309 }
310
311 pub fn send_replay(&self, event: ChatEvent) {
312 let _ = self.event_tx.send(event);
313 }
314
315 pub fn event_history(&self) -> Vec<ChatEvent> {
316 self.event_history.lock().unwrap().clone()
317 }
318
319 pub fn stream_start(&self, message_id: String, agent: String, model: Model) {
320 self.send(ChatEvent::StreamStart {
321 message_id,
322 agent,
323 model,
324 });
325 }
326
327 pub fn stream_delta(&self, message_id: String, text: String) {
328 self.send(ChatEvent::StreamDelta { message_id, text });
329 }
330
331 pub fn stream_reasoning_delta(&self, message_id: String, text: String) {
332 self.send(ChatEvent::StreamReasoningDelta { message_id, text });
333 }
334
335 pub fn stream_end(&self, message: ChatMessage) {
336 self.send(ChatEvent::StreamEnd { message });
337 }
338
339 pub(crate) fn clear_history(&self) {
340 self.event_history.lock().unwrap().clear();
341 }
342}