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