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}