comfyui_client/meta.rs
1use crate::ClientError;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use std::{collections::HashMap, fmt::Debug};
5use tokio_tungstenite::tungstenite;
6
7/// Contains information about a prompt, including its execution details.
8#[derive(Clone, Serialize, Deserialize, Debug)]
9pub struct PromptInfo {
10 /// Execution information related to the prompt.
11 pub exec_info: ExecInfo,
12}
13
14/// Contains execution details such as the remaining queue length.
15#[derive(Clone, Serialize, Deserialize, Debug)]
16pub struct ExecInfo {
17 /// The number of remaining tasks in the execution queue.
18 pub queue_remaining: usize,
19}
20
21/// Represents file information including filename, subfolder, and file type.
22#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
23pub struct FileInfo {
24 /// The name of the file.
25 #[serde(alias = "name")]
26 pub filename: String,
27 /// The subfolder where the file is located.
28 pub subfolder: String,
29 /// The type of the file.
30 pub r#type: String,
31}
32
33/// Represents a prompt with an identifier, a number, and potential node errors.
34#[derive(Clone, Serialize, Deserialize, Debug)]
35pub struct PromptStatus {
36 /// Unique identifier for the prompt.
37 pub prompt_id: String,
38 /// A numeric identifier for the prompt.
39 pub number: usize,
40 /// A mapping of node identifiers to error details in JSON format.
41 pub node_errors: HashMap<String, Value>,
42}
43
44/// Represents the history of outputs for a prompt.
45#[derive(Clone, Serialize, Deserialize, Debug)]
46pub struct History {
47 /// A mapping of output identifiers to their corresponding images.
48 pub outputs: HashMap<String, Images>,
49}
50
51/// Contains an optional list of image file information.
52#[derive(Clone, Serialize, Deserialize, Debug)]
53pub struct Images {
54 /// A vector of file information objects, if available.
55 pub images: Option<Vec<FileInfo>>,
56 /// A vector of animated file information objects, if available.
57 pub gifs: Option<Vec<FileInfo>>,
58}
59
60/// Represents events emitted by the ComfyUI client during workflow execution.
61///
62/// This structure allows for clear separation between service-level events and
63/// client-side connection management events.
64#[non_exhaustive]
65pub enum Event {
66 /// `Comfy` events originate from the ComfyUI service itself
67 Comfy(ComfyEvent),
68 /// `Connection` events relate to WebSocket connection management
69 Connection(ConnectionEvent),
70}
71
72/// Represents events emitted by the ComfyUI service during workflow execution.
73///
74/// This enum encapsulates various event types that occur during the lifecycle
75/// of a workflow, from queuing to completion. Each variant contains specific
76/// data relevant to that event type. The `Unknown` variant captures any
77/// unrecognized events from the API.
78#[derive(Serialize, Deserialize, Debug)]
79#[serde(tag = "type")]
80#[serde(rename_all = "snake_case")]
81pub enum ComfyEvent {
82 /// A status event containing queue and execution information.
83 Status {
84 /// Data payload for the status event, including execution information.
85 data: StatusEventData,
86 /// Optional session identifier associated with this event.
87 sid: Option<String>,
88 },
89 /// A progress event indicating current progress of an operation.
90 Progress {
91 /// Data payload containing current and maximum progress values.
92 data: ProgressEventData,
93 },
94 /// An event indicating that a node has completed execution along with its
95 /// output data.
96 Executed {
97 /// Data payload containing node output information and associated
98 /// images.
99 data: ExecutedEventData,
100 },
101 /// An event indicating that a node is currently executing.
102 Executing {
103 /// Data payload identifying the currently executing node.
104 data: ExecutingEventData,
105 },
106 /// An event signaling the start of execution for a prompt.
107 ExecutionStart {
108 /// Data payload containing prompt ID and timestamp information.
109 data: ExecutionStartEventData,
110 },
111 /// An event signaling that an error occurred during execution.
112 ExecutionError {
113 /// Data payload containing detailed error information and context.
114 data: ExecutionErrorEventData,
115 },
116 /// An event indicating that the execution results were retrieved from the
117 /// cache.
118 ExecutionCached {
119 /// Data payload containing information about which nodes were retrieved
120 /// from cache.
121 data: ExecutionCachedEventData,
122 },
123 /// An event indicating that the execution was interrupted.
124 ExecutionInterrupted {
125 /// Data payload containing details about where and why the execution
126 /// was interrupted.
127 data: ExecutionInterruptedEventData,
128 },
129 /// An event indicating that the entire workflow has executed successfully.
130 ExecutionSuccess {
131 /// Data payload containing the prompt ID of the successfully executed
132 /// workflow.
133 data: ExecutionSuccessEventData,
134 },
135 /// An unknown event type that encapsulates raw JSON data for events not
136 /// explicitly defined.
137 #[serde(skip)]
138 Unknown(Value),
139}
140
141/// Represents events that are not part of the standard ComfyUI API
142/// but are added by the client for additional functionality.
143///
144/// These events are used internally by the client to handle WebSocket
145/// connection management and error reporting, allowing the application to
146/// respond to connection-related events that aren't part of the ComfyUI
147/// protocol.
148#[derive(Debug)]
149#[non_exhaustive]
150pub enum ConnectionEvent {
151 /// Event indicating a successful reconnection to the WebSocket.
152 ///
153 /// Emitted when the client successfully reestablishes a connection after
154 /// a disconnection.
155 WSReconnectSuccess,
156
157 /// Event containing an error that occurred during a reconnection attempt.
158 ///
159 /// Provides detailed error information about why a reconnection attempt
160 /// failed, allowing clients to implement appropriate retry or fallback
161 /// strategies.
162 WSReconnectError(ClientError),
163
164 /// Event containing an error that occurred while receiving messages.
165 ///
166 /// Indicates that an error occurred in the WebSocket communication channel
167 /// while trying to receive messages from the ComfyUI server.
168 WSReceiveError(tungstenite::Error),
169}
170
171/// Event payload for a status event, containing execution information.
172///
173/// This structure is received when ComfyUI sends a status update, typically
174/// containing information about the current execution queue state.
175#[derive(Clone, Serialize, Deserialize, Debug)]
176pub struct StatusEventData {
177 /// Execution information associated with the event, including queue
178 /// details.
179 pub status: StatusEventStatus,
180}
181
182/// Container for execution information within a status event.
183///
184/// Holds detailed execution information about the current state of the ComfyUI
185/// service, such as the number of remaining items in the execution queue.
186#[derive(Clone, Serialize, Deserialize, Debug)]
187pub struct StatusEventStatus {
188 /// Execution information including queue status and other execution
189 /// metrics.
190 pub exec_info: ExecInfo,
191}
192
193/// Event payload for a progress update, including current value and maximum
194/// value.
195///
196/// This structure is received when ComfyUI reports progress of an operation,
197/// such as image generation or processing.
198#[derive(Clone, Serialize, Deserialize, Debug)]
199pub struct ProgressEventData {
200 /// The current progress value representing the completed steps.
201 pub value: usize,
202 /// The maximum progress value representing the total number of steps.
203 pub max: usize,
204}
205
206/// Represents the output of an executed node.
207///
208/// Contains the results produced by a node in the workflow after successful
209/// execution. This can include generated or processed images in the `images`
210/// field, as well as other arbitrary output data in the `others` map.
211#[derive(Clone, Serialize, Deserialize, Debug)]
212pub struct ExecutedOutput {
213 /// Optional list of image file information objects generated or processed
214 /// by the node.
215 pub images: Option<Vec<FileInfo>>,
216 /// Additional output data that doesn't fit into predefined categories.
217 #[serde(flatten)]
218 pub others: HashMap<String, Value>,
219}
220
221/// Event payload for a completed execution, including the node identifier,
222/// prompt ID, and output data.
223///
224/// This structure is received when a specific node in the workflow completes
225/// execution and produces output, such as generated images.
226#[derive(Clone, Serialize, Deserialize, Debug)]
227pub struct ExecutedEventData {
228 /// Identifier of the node that completed execution.
229 pub node: String,
230 /// The prompt ID associated with the execution.
231 pub prompt_id: String,
232 /// The output generated by the executed node, containing resulting images
233 /// or other data.
234 pub output: Option<ExecutedOutput>,
235}
236
237/// Event payload for an execution in progress, including the node identifier
238/// and prompt ID.
239///
240/// This structure is received when ComfyUI begins executing a specific node in
241/// the workflow. It provides information about which node is currently being
242/// processed.
243#[derive(Clone, Serialize, Deserialize, Debug)]
244pub struct ExecutingEventData {
245 /// Identifier of the node currently executing. May be None in certain
246 /// cases.
247 pub node: Option<String>,
248 /// Optional display name of the executing node, providing a more
249 /// user-friendly identifier.
250 pub display_node: Option<String>,
251 /// The prompt ID associated with the execution, linking this event to a
252 /// specific workflow run.
253 pub prompt_id: String,
254}
255
256/// Event payload indicating that the execution has started.
257///
258/// This structure is received when ComfyUI begins executing a workflow.
259/// It serves as an initial notification that the workflow processing has begun
260/// and provides timing information for performance tracking.
261#[derive(Clone, Serialize, Deserialize, Debug)]
262pub struct ExecutionStartEventData {
263 /// The prompt ID for which the execution has started, identifying the
264 /// workflow run.
265 pub prompt_id: String,
266 /// Unix timestamp indicating when the execution started, useful for timing
267 /// analysis.
268 pub timestamp: u64,
269}
270
271/// Event payload for an execution error, containing details about the error and
272/// its context.
273///
274/// This structure is received when an error occurs during workflow execution.
275/// It provides comprehensive information about the error, including where it
276/// occurred and the state of inputs and outputs at the time of the error.
277#[derive(Clone, Serialize, Deserialize, Debug)]
278pub struct ExecutionErrorEventData {
279 /// The prompt ID associated with the error, identifying the workflow
280 /// execution.
281 pub prompt_id: String,
282 /// The identifier of the node where the error occurred within the workflow.
283 pub node_id: String,
284 /// The type of the node where the error occurred (e.g., "CLIPTextEncode",
285 /// "KSampler").
286 pub node_type: String,
287 /// A list of node identifiers that were successfully executed before the
288 /// error occurred.
289 pub executed: Vec<String>,
290 /// The error message from the exception, describing what went wrong.
291 pub exception_message: String,
292 /// The type of the exception that was raised (e.g., "ValueError",
293 /// "RuntimeError").
294 pub exception_type: String,
295 /// A traceback of the error as a list of strings, showing the execution
296 /// path that led to the error.
297 pub traceback: Vec<String>,
298 /// The current input values at the time of the error, mapping input names
299 /// to their values.
300 pub current_inputs: HashMap<String, Value>,
301 /// The current output values at the time of the error, mapping output names
302 /// to their values.
303 pub current_outputs: HashMap<String, Value>,
304}
305
306/// Event payload indicating that the execution result was obtained from the
307/// cache rather than recalculated.
308///
309/// This structure is received when ComfyUI uses cached results for nodes in the
310/// workflow, which can significantly speed up execution when identical
311/// operations are performed.
312#[derive(Clone, Serialize, Deserialize, Debug)]
313pub struct ExecutionCachedEventData {
314 /// A list of node identifiers that were retrieved from the cache instead of
315 /// being re-executed.
316 pub nodes: Vec<String>,
317 /// The prompt ID associated with the cached execution, linking this event
318 /// to a specific workflow run.
319 pub prompt_id: String,
320 /// Unix timestamp indicating when the cached execution result was
321 /// retrieved, useful for timing analysis.
322 pub timestamp: u64,
323}
324
325/// Event payload for an interrupted execution, containing details about the
326/// interruption.
327///
328/// This structure is received when the workflow execution is manually
329/// interrupted or terminated before completion, providing context about what
330/// was executing at the time of interruption.
331#[derive(Clone, Serialize, Deserialize, Debug)]
332pub struct ExecutionInterruptedEventData {
333 /// The prompt ID associated with the interruption, identifying the workflow
334 /// execution that was stopped.
335 pub prompt_id: String,
336 /// The identifier of the node where the execution was interrupted,
337 /// indicating which operation was in progress.
338 pub node_id: String,
339 /// The type of the node that was interrupted (e.g., "KSampler",
340 /// "VAEDecode"), helping identify what operation was stopped.
341 pub node_type: String,
342 /// A list of node identifiers that were successfully executed before the
343 /// interruption occurred.
344 pub executed: Vec<String>,
345}
346
347/// Event payload indicating successful completion of workflow execution.
348///
349/// This structure is received when an entire workflow has completed execution
350/// successfully. It serves as a final notification that all nodes in the
351/// workflow have been processed without errors, and the workflow is complete.
352#[derive(Clone, Serialize, Deserialize, Debug)]
353pub struct ExecutionSuccessEventData {
354 /// The prompt ID associated with the successful execution, identifying the
355 /// completed workflow.
356 pub prompt_id: String,
357}
358
359/// `Prompt` param for
360/// [`ComfyUIClient::post_prompt`](crate::ComfyUIClient::post_prompt).
361///
362/// Represents a prompt that can be submitted to the ComfyUI server for
363/// execution. The prompt defines the workflow to be executed, including all
364/// nodes and their connections, as well as the input parameters for each node.
365pub enum Prompt<'a> {
366 /// A string slice representing the prompt in JSON format.
367 ///
368 /// Use this variant when you have a JSON string representation of the
369 /// workflow.
370 Str(&'a str),
371
372 /// A JSON value representing the prompt data.
373 ///
374 /// Use this variant when you have already parsed the workflow into a
375 /// serde_json Value.
376 Value(&'a Value),
377}
378
379impl<'a> From<&'a str> for Prompt<'a> {
380 fn from(value: &'a str) -> Self {
381 Prompt::Str(value)
382 }
383}
384
385impl<'a> From<&'a String> for Prompt<'a> {
386 fn from(value: &'a String) -> Self {
387 Prompt::Str(value)
388 }
389}
390
391impl<'a> From<&'a Value> for Prompt<'a> {
392 fn from(value: &'a Value) -> Self {
393 Prompt::Value(value)
394 }
395}
396
397#[cfg(test)]
398mod tests {
399 use super::*;
400 use serde_json::json;
401
402 /// Tests serialization of different event types.
403 #[test]
404 fn test_serialize_event() {
405 let ev = ComfyEvent::Status {
406 data: StatusEventData {
407 status: StatusEventStatus {
408 exec_info: ExecInfo { queue_remaining: 0 },
409 },
410 },
411 sid: None,
412 };
413 let value = serde_json::to_value(&ev).unwrap();
414 assert_eq!(
415 value,
416 json!({
417 "type": "status",
418 "data": {
419 "status": {
420 "exec_info": {
421 "queue_remaining": 0,
422 }
423 }
424 },
425 "sid": null
426 })
427 );
428
429 let ev = ComfyEvent::ExecutionStart {
430 data: ExecutionStartEventData {
431 prompt_id: "xxxxxx".to_string(),
432 timestamp: 123456789,
433 },
434 };
435 let value = serde_json::to_value(&ev).unwrap();
436 assert_eq!(
437 value,
438 json!({
439 "type": "execution_start",
440 "data": {
441 "prompt_id": "xxxxxx",
442 "timestamp": 123456789
443 }
444 })
445 );
446 }
447}