comfyui-node 1.6.2 ā 1.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.d.ts +18 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -7
- package/dist/index.js.map +1 -1
- package/dist/multipool/client-registry.js +152 -152
- package/dist/multipool/helpers.js +52 -52
- package/dist/multipool/helpers.js.map +1 -1
- package/dist/multipool/index.js +2 -2
- package/dist/multipool/interfaces.d.ts +29 -12
- package/dist/multipool/interfaces.d.ts.map +1 -1
- package/dist/multipool/interfaces.js +1 -1
- package/dist/multipool/job-state-registry.js +282 -282
- package/dist/multipool/multi-workflow-pool.d.ts +102 -42
- package/dist/multipool/multi-workflow-pool.d.ts.map +1 -1
- package/dist/multipool/multi-workflow-pool.js +424 -313
- package/dist/multipool/multi-workflow-pool.js.map +1 -1
- package/dist/multipool/pool-event-manager.js +27 -27
- package/dist/multipool/tests/client-registry-api-demo.d.ts +7 -0
- package/dist/multipool/tests/client-registry-api-demo.d.ts.map +1 -0
- package/dist/multipool/tests/client-registry-api-demo.js +136 -0
- package/dist/multipool/tests/client-registry-api-demo.js.map +1 -0
- package/dist/multipool/tests/client-registry.spec.d.ts +2 -0
- package/dist/multipool/tests/client-registry.spec.d.ts.map +1 -0
- package/dist/multipool/tests/client-registry.spec.js +191 -0
- package/dist/multipool/tests/client-registry.spec.js.map +1 -0
- package/dist/multipool/tests/error-classification-tests.js +373 -373
- package/dist/multipool/tests/event-forwarding-demo.d.ts +7 -0
- package/dist/multipool/tests/event-forwarding-demo.d.ts.map +1 -0
- package/dist/multipool/tests/event-forwarding-demo.js +88 -0
- package/dist/multipool/tests/event-forwarding-demo.js.map +1 -0
- package/dist/multipool/tests/helpers.spec.d.ts +2 -0
- package/dist/multipool/tests/helpers.spec.d.ts.map +1 -0
- package/dist/multipool/tests/helpers.spec.js +100 -0
- package/dist/multipool/tests/helpers.spec.js.map +1 -0
- package/dist/multipool/tests/job-queue-processor.spec.d.ts +2 -0
- package/dist/multipool/tests/job-queue-processor.spec.d.ts.map +1 -0
- package/dist/multipool/tests/job-queue-processor.spec.js +89 -0
- package/dist/multipool/tests/job-queue-processor.spec.js.map +1 -0
- package/dist/multipool/tests/job-state-registry.spec.d.ts +2 -0
- package/dist/multipool/tests/job-state-registry.spec.d.ts.map +1 -0
- package/dist/multipool/tests/job-state-registry.spec.js +143 -0
- package/dist/multipool/tests/job-state-registry.spec.js.map +1 -0
- package/dist/multipool/tests/multipool-basic.js +141 -141
- package/dist/multipool/tests/profiling-demo.js +87 -87
- package/dist/multipool/tests/profiling-demo.js.map +1 -1
- package/dist/multipool/tests/two-stage-edit-simulation.js +298 -298
- package/dist/multipool/tests/two-stage-edit-simulation.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,374 +1,374 @@
|
|
|
1
|
-
import { MultiWorkflowPool } from "src/multipool/multi-workflow-pool.js";
|
|
2
|
-
import { Workflow } from "src/multipool/workflow.js";
|
|
3
|
-
/**
|
|
4
|
-
* Error Classification Validation Tests
|
|
5
|
-
*
|
|
6
|
-
* This script validates the error classification logic by intentionally triggering
|
|
7
|
-
* different types of errors from real ComfyUI servers.
|
|
8
|
-
*
|
|
9
|
-
* Test Cases:
|
|
10
|
-
* 1. Missing Model Error (workflow_incompatibility)
|
|
11
|
-
* 2. Missing Custom Node Error (workflow_incompatibility)
|
|
12
|
-
* 3. Invalid Input Error (transient)
|
|
13
|
-
* 4. Connection Error (connection)
|
|
14
|
-
*/
|
|
15
|
-
const pool = new MultiWorkflowPool();
|
|
16
|
-
// Add your real ComfyUI servers here
|
|
17
|
-
pool.addClient("http://server1:8188", {
|
|
18
|
-
workflowAffinity: [],
|
|
19
|
-
priority: 1
|
|
20
|
-
});
|
|
21
|
-
pool.addClient("http://server2:8188", {
|
|
22
|
-
workflowAffinity: [],
|
|
23
|
-
priority: 1
|
|
24
|
-
});
|
|
25
|
-
pool.addClient("http://server3:8188", {
|
|
26
|
-
workflowAffinity: [],
|
|
27
|
-
priority: 1
|
|
28
|
-
});
|
|
29
|
-
await pool.init();
|
|
30
|
-
console.log("\n" + "=".repeat(80));
|
|
31
|
-
console.log("ERROR CLASSIFICATION VALIDATION TESTS");
|
|
32
|
-
console.log("=".repeat(80) + "\n");
|
|
33
|
-
// ============================================================================
|
|
34
|
-
// TEST 1: Missing Model Error (should be classified as workflow_incompatibility)
|
|
35
|
-
// ============================================================================
|
|
36
|
-
async function testMissingModel() {
|
|
37
|
-
console.log("\nš TEST 1: Missing Model Error");
|
|
38
|
-
console.log("-".repeat(80));
|
|
39
|
-
const workflow = new Workflow({
|
|
40
|
-
"1": {
|
|
41
|
-
"inputs": {
|
|
42
|
-
"text": "beautiful landscape"
|
|
43
|
-
},
|
|
44
|
-
"class_type": "CLIPTextEncode",
|
|
45
|
-
"_meta": {
|
|
46
|
-
"title": "CLIP Text Encode (Prompt)"
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
"2": {
|
|
50
|
-
"inputs": {
|
|
51
|
-
"ckpt_name": "this_model_definitely_does_not_exist_anywhere_12345.safetensors"
|
|
52
|
-
},
|
|
53
|
-
"class_type": "CheckpointLoaderSimple",
|
|
54
|
-
"_meta": {
|
|
55
|
-
"title": "Load Checkpoint"
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
"3": {
|
|
59
|
-
"inputs": {
|
|
60
|
-
"seed": 12345,
|
|
61
|
-
"steps": 20,
|
|
62
|
-
"cfg": 7.0,
|
|
63
|
-
"sampler_name": "euler",
|
|
64
|
-
"scheduler": "normal",
|
|
65
|
-
"denoise": 1.0,
|
|
66
|
-
"model": ["2", 0],
|
|
67
|
-
"positive": ["1", 0],
|
|
68
|
-
"negative": ["1", 0],
|
|
69
|
-
"latent_image": ["4", 0]
|
|
70
|
-
},
|
|
71
|
-
"class_type": "KSampler",
|
|
72
|
-
"_meta": {
|
|
73
|
-
"title": "KSampler"
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
"4": {
|
|
77
|
-
"inputs": {
|
|
78
|
-
"width": 512,
|
|
79
|
-
"height": 512,
|
|
80
|
-
"batch_size": 1
|
|
81
|
-
},
|
|
82
|
-
"class_type": "EmptyLatentImage",
|
|
83
|
-
"_meta": {
|
|
84
|
-
"title": "Empty Latent Image"
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
"5": {
|
|
88
|
-
"inputs": {
|
|
89
|
-
"samples": ["3", 0],
|
|
90
|
-
"vae": ["2", 2]
|
|
91
|
-
},
|
|
92
|
-
"class_type": "VAEDecode",
|
|
93
|
-
"_meta": {
|
|
94
|
-
"title": "VAE Decode"
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
"6": {
|
|
98
|
-
"inputs": {
|
|
99
|
-
"filename_prefix": "ComfyUI",
|
|
100
|
-
"images": ["5", 0]
|
|
101
|
-
},
|
|
102
|
-
"class_type": "SaveImage",
|
|
103
|
-
"_meta": {
|
|
104
|
-
"title": "Save Image"
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
try {
|
|
109
|
-
const jobId = await pool.submitJob(workflow);
|
|
110
|
-
console.log(`Submitted job: ${jobId}`);
|
|
111
|
-
const result = await pool.waitForJobCompletion(jobId);
|
|
112
|
-
if (result.status === "failed") {
|
|
113
|
-
console.log("ā
Test PASSED: Job failed as expected");
|
|
114
|
-
console.log(`Error classification should be: workflow_incompatibility`);
|
|
115
|
-
console.log(`Actual error:`, result.error);
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
console.log("ā Test FAILED: Job should have failed but didn't");
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
catch (error) {
|
|
122
|
-
console.log("ā Test ERROR:", error);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
// ============================================================================
|
|
126
|
-
// TEST 2: Missing Custom Node Error (should be classified as workflow_incompatibility)
|
|
127
|
-
// ============================================================================
|
|
128
|
-
async function testMissingCustomNode() {
|
|
129
|
-
console.log("\nš TEST 2: Missing Custom Node Error");
|
|
130
|
-
console.log("-".repeat(80));
|
|
131
|
-
const workflow = new Workflow({
|
|
132
|
-
"1": {
|
|
133
|
-
"inputs": {
|
|
134
|
-
"some_input": "test"
|
|
135
|
-
},
|
|
136
|
-
"class_type": "NonExistentCustomNode_XYZ_12345",
|
|
137
|
-
"_meta": {
|
|
138
|
-
"title": "This Node Does Not Exist"
|
|
139
|
-
}
|
|
140
|
-
},
|
|
141
|
-
"2": {
|
|
142
|
-
"inputs": {
|
|
143
|
-
"filename_prefix": "test",
|
|
144
|
-
"images": ["1", 0]
|
|
145
|
-
},
|
|
146
|
-
"class_type": "SaveImage",
|
|
147
|
-
"_meta": {
|
|
148
|
-
"title": "Save Image"
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
try {
|
|
153
|
-
const jobId = await pool.submitJob(workflow);
|
|
154
|
-
console.log(`Submitted job: ${jobId}`);
|
|
155
|
-
const result = await pool.waitForJobCompletion(jobId);
|
|
156
|
-
if (result.status === "failed") {
|
|
157
|
-
console.log("ā
Test PASSED: Job failed as expected");
|
|
158
|
-
console.log(`Error classification should be: workflow_incompatibility`);
|
|
159
|
-
console.log(`Actual error:`, result.error);
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
console.log("ā Test FAILED: Job should have failed but didn't");
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
catch (error) {
|
|
166
|
-
console.log("ā Test ERROR:", error);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
// ============================================================================
|
|
170
|
-
// TEST 3: Invalid Input Error (should be classified as transient)
|
|
171
|
-
// ============================================================================
|
|
172
|
-
async function testInvalidInput() {
|
|
173
|
-
console.log("\nš TEST 3: Invalid Input Error");
|
|
174
|
-
console.log("-".repeat(80));
|
|
175
|
-
const workflow = new Workflow({
|
|
176
|
-
"1": {
|
|
177
|
-
"inputs": {
|
|
178
|
-
"width": -999999, // Negative width should cause validation error
|
|
179
|
-
"height": -999999, // Negative height should cause validation error
|
|
180
|
-
"batch_size": 1
|
|
181
|
-
},
|
|
182
|
-
"class_type": "EmptyLatentImage",
|
|
183
|
-
"_meta": {
|
|
184
|
-
"title": "Empty Latent Image"
|
|
185
|
-
}
|
|
186
|
-
},
|
|
187
|
-
"2": {
|
|
188
|
-
"inputs": {
|
|
189
|
-
"filename_prefix": "test",
|
|
190
|
-
"images": ["1", 0]
|
|
191
|
-
},
|
|
192
|
-
"class_type": "SaveImage",
|
|
193
|
-
"_meta": {
|
|
194
|
-
"title": "Save Image"
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
});
|
|
198
|
-
try {
|
|
199
|
-
const jobId = await pool.submitJob(workflow);
|
|
200
|
-
console.log(`Submitted job: ${jobId}`);
|
|
201
|
-
const result = await pool.waitForJobCompletion(jobId);
|
|
202
|
-
if (result.status === "failed") {
|
|
203
|
-
console.log("ā
Test PASSED: Job failed as expected");
|
|
204
|
-
console.log(`Error classification should be: transient`);
|
|
205
|
-
console.log(`Actual error:`, result.error);
|
|
206
|
-
}
|
|
207
|
-
else {
|
|
208
|
-
console.log("ā Test FAILED: Job should have failed but didn't");
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
catch (error) {
|
|
212
|
-
console.log("ā Test ERROR:", error);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
// ============================================================================
|
|
216
|
-
// TEST 4: Connection Error (should be classified as connection)
|
|
217
|
-
// ============================================================================
|
|
218
|
-
async function testConnectionError() {
|
|
219
|
-
console.log("\nš TEST 4: Connection Error");
|
|
220
|
-
console.log("-".repeat(80));
|
|
221
|
-
const isolatedPool = new MultiWorkflowPool();
|
|
222
|
-
// Add a client that definitely doesn't exist
|
|
223
|
-
isolatedPool.addClient("http://this-server-does-not-exist-12345:8188", {
|
|
224
|
-
workflowAffinity: [],
|
|
225
|
-
priority: 1
|
|
226
|
-
});
|
|
227
|
-
try {
|
|
228
|
-
console.log("Attempting to connect to non-existent server...");
|
|
229
|
-
await isolatedPool.init();
|
|
230
|
-
console.log("ā Test FAILED: Should have failed to connect");
|
|
231
|
-
}
|
|
232
|
-
catch (error) {
|
|
233
|
-
console.log("ā
Test PASSED: Connection failed as expected");
|
|
234
|
-
console.log(`Error classification should be: connection`);
|
|
235
|
-
console.log(`Actual error:`, error);
|
|
236
|
-
}
|
|
237
|
-
finally {
|
|
238
|
-
await isolatedPool.shutdown();
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
// ============================================================================
|
|
242
|
-
// TEST 5: Missing LoRA Error (should be classified as workflow_incompatibility)
|
|
243
|
-
// ============================================================================
|
|
244
|
-
async function testMissingLoRA() {
|
|
245
|
-
console.log("\nš TEST 5: Missing LoRA Error");
|
|
246
|
-
console.log("-".repeat(80));
|
|
247
|
-
const workflow = new Workflow({
|
|
248
|
-
"1": {
|
|
249
|
-
"inputs": {
|
|
250
|
-
"text": "beautiful landscape"
|
|
251
|
-
},
|
|
252
|
-
"class_type": "CLIPTextEncode",
|
|
253
|
-
"_meta": {
|
|
254
|
-
"title": "CLIP Text Encode"
|
|
255
|
-
}
|
|
256
|
-
},
|
|
257
|
-
"2": {
|
|
258
|
-
"inputs": {
|
|
259
|
-
"ckpt_name": "novaAnimeXL_ilV125.safetensors" // Use a model that exists on the servers
|
|
260
|
-
},
|
|
261
|
-
"class_type": "CheckpointLoaderSimple",
|
|
262
|
-
"_meta": {
|
|
263
|
-
"title": "Load Checkpoint"
|
|
264
|
-
}
|
|
265
|
-
},
|
|
266
|
-
"3": {
|
|
267
|
-
"inputs": {
|
|
268
|
-
"lora_name": "this_lora_does_not_exist_12345.safetensors",
|
|
269
|
-
"strength_model": 1.0,
|
|
270
|
-
"strength_clip": 1.0,
|
|
271
|
-
"model": ["2", 0],
|
|
272
|
-
"clip": ["2", 1]
|
|
273
|
-
},
|
|
274
|
-
"class_type": "LoraLoader",
|
|
275
|
-
"_meta": {
|
|
276
|
-
"title": "Load LoRA"
|
|
277
|
-
}
|
|
278
|
-
},
|
|
279
|
-
"4": {
|
|
280
|
-
"inputs": {
|
|
281
|
-
"width": 512,
|
|
282
|
-
"height": 512,
|
|
283
|
-
"batch_size": 1
|
|
284
|
-
},
|
|
285
|
-
"class_type": "EmptyLatentImage",
|
|
286
|
-
"_meta": {
|
|
287
|
-
"title": "Empty Latent Image"
|
|
288
|
-
}
|
|
289
|
-
},
|
|
290
|
-
"5": {
|
|
291
|
-
"inputs": {
|
|
292
|
-
"seed": 12345,
|
|
293
|
-
"steps": 20,
|
|
294
|
-
"cfg": 7.0,
|
|
295
|
-
"sampler_name": "euler",
|
|
296
|
-
"scheduler": "normal",
|
|
297
|
-
"denoise": 1.0,
|
|
298
|
-
"model": ["3", 0],
|
|
299
|
-
"positive": ["1", 0],
|
|
300
|
-
"negative": ["1", 0],
|
|
301
|
-
"latent_image": ["4", 0]
|
|
302
|
-
},
|
|
303
|
-
"class_type": "KSampler",
|
|
304
|
-
"_meta": {
|
|
305
|
-
"title": "KSampler"
|
|
306
|
-
}
|
|
307
|
-
},
|
|
308
|
-
"6": {
|
|
309
|
-
"inputs": {
|
|
310
|
-
"samples": ["5", 0],
|
|
311
|
-
"vae": ["2", 2]
|
|
312
|
-
},
|
|
313
|
-
"class_type": "VAEDecode",
|
|
314
|
-
"_meta": {
|
|
315
|
-
"title": "VAE Decode"
|
|
316
|
-
}
|
|
317
|
-
},
|
|
318
|
-
"7": {
|
|
319
|
-
"inputs": {
|
|
320
|
-
"filename_prefix": "ComfyUI",
|
|
321
|
-
"images": ["6", 0]
|
|
322
|
-
},
|
|
323
|
-
"class_type": "SaveImage",
|
|
324
|
-
"_meta": {
|
|
325
|
-
"title": "Save Image"
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
});
|
|
329
|
-
try {
|
|
330
|
-
const jobId = await pool.submitJob(workflow);
|
|
331
|
-
console.log(`Submitted job: ${jobId}`);
|
|
332
|
-
const result = await pool.waitForJobCompletion(jobId);
|
|
333
|
-
if (result.status === "failed") {
|
|
334
|
-
console.log("ā
Test PASSED: Job failed as expected");
|
|
335
|
-
console.log(`Error classification should be: workflow_incompatibility`);
|
|
336
|
-
console.log(`Actual error:`, result.error);
|
|
337
|
-
}
|
|
338
|
-
else {
|
|
339
|
-
console.log("ā Test FAILED: Job should have failed but didn't");
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
catch (error) {
|
|
343
|
-
console.log("ā Test ERROR:", error);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
// ============================================================================
|
|
347
|
-
// Run all tests
|
|
348
|
-
// ============================================================================
|
|
349
|
-
async function runAllTests() {
|
|
350
|
-
try {
|
|
351
|
-
await testMissingModel();
|
|
352
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
353
|
-
await testMissingCustomNode();
|
|
354
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
355
|
-
await testInvalidInput();
|
|
356
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
357
|
-
await testMissingLoRA();
|
|
358
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
359
|
-
await testConnectionError();
|
|
360
|
-
console.log("\n" + "=".repeat(80));
|
|
361
|
-
console.log("ALL TESTS COMPLETED");
|
|
362
|
-
console.log("=".repeat(80) + "\n");
|
|
363
|
-
}
|
|
364
|
-
catch (error) {
|
|
365
|
-
console.error("Fatal error during tests:", error);
|
|
366
|
-
}
|
|
367
|
-
finally {
|
|
368
|
-
await pool.shutdown();
|
|
369
|
-
process.exit(0);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
// Run the tests
|
|
373
|
-
runAllTests();
|
|
1
|
+
import { MultiWorkflowPool } from "src/multipool/multi-workflow-pool.js";
|
|
2
|
+
import { Workflow } from "src/multipool/workflow.js";
|
|
3
|
+
/**
|
|
4
|
+
* Error Classification Validation Tests
|
|
5
|
+
*
|
|
6
|
+
* This script validates the error classification logic by intentionally triggering
|
|
7
|
+
* different types of errors from real ComfyUI servers.
|
|
8
|
+
*
|
|
9
|
+
* Test Cases:
|
|
10
|
+
* 1. Missing Model Error (workflow_incompatibility)
|
|
11
|
+
* 2. Missing Custom Node Error (workflow_incompatibility)
|
|
12
|
+
* 3. Invalid Input Error (transient)
|
|
13
|
+
* 4. Connection Error (connection)
|
|
14
|
+
*/
|
|
15
|
+
const pool = new MultiWorkflowPool();
|
|
16
|
+
// Add your real ComfyUI servers here
|
|
17
|
+
pool.addClient("http://server1:8188", {
|
|
18
|
+
workflowAffinity: [],
|
|
19
|
+
priority: 1
|
|
20
|
+
});
|
|
21
|
+
pool.addClient("http://server2:8188", {
|
|
22
|
+
workflowAffinity: [],
|
|
23
|
+
priority: 1
|
|
24
|
+
});
|
|
25
|
+
pool.addClient("http://server3:8188", {
|
|
26
|
+
workflowAffinity: [],
|
|
27
|
+
priority: 1
|
|
28
|
+
});
|
|
29
|
+
await pool.init();
|
|
30
|
+
console.log("\n" + "=".repeat(80));
|
|
31
|
+
console.log("ERROR CLASSIFICATION VALIDATION TESTS");
|
|
32
|
+
console.log("=".repeat(80) + "\n");
|
|
33
|
+
// ============================================================================
|
|
34
|
+
// TEST 1: Missing Model Error (should be classified as workflow_incompatibility)
|
|
35
|
+
// ============================================================================
|
|
36
|
+
async function testMissingModel() {
|
|
37
|
+
console.log("\nš TEST 1: Missing Model Error");
|
|
38
|
+
console.log("-".repeat(80));
|
|
39
|
+
const workflow = new Workflow({
|
|
40
|
+
"1": {
|
|
41
|
+
"inputs": {
|
|
42
|
+
"text": "beautiful landscape"
|
|
43
|
+
},
|
|
44
|
+
"class_type": "CLIPTextEncode",
|
|
45
|
+
"_meta": {
|
|
46
|
+
"title": "CLIP Text Encode (Prompt)"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"2": {
|
|
50
|
+
"inputs": {
|
|
51
|
+
"ckpt_name": "this_model_definitely_does_not_exist_anywhere_12345.safetensors"
|
|
52
|
+
},
|
|
53
|
+
"class_type": "CheckpointLoaderSimple",
|
|
54
|
+
"_meta": {
|
|
55
|
+
"title": "Load Checkpoint"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"3": {
|
|
59
|
+
"inputs": {
|
|
60
|
+
"seed": 12345,
|
|
61
|
+
"steps": 20,
|
|
62
|
+
"cfg": 7.0,
|
|
63
|
+
"sampler_name": "euler",
|
|
64
|
+
"scheduler": "normal",
|
|
65
|
+
"denoise": 1.0,
|
|
66
|
+
"model": ["2", 0],
|
|
67
|
+
"positive": ["1", 0],
|
|
68
|
+
"negative": ["1", 0],
|
|
69
|
+
"latent_image": ["4", 0]
|
|
70
|
+
},
|
|
71
|
+
"class_type": "KSampler",
|
|
72
|
+
"_meta": {
|
|
73
|
+
"title": "KSampler"
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"4": {
|
|
77
|
+
"inputs": {
|
|
78
|
+
"width": 512,
|
|
79
|
+
"height": 512,
|
|
80
|
+
"batch_size": 1
|
|
81
|
+
},
|
|
82
|
+
"class_type": "EmptyLatentImage",
|
|
83
|
+
"_meta": {
|
|
84
|
+
"title": "Empty Latent Image"
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"5": {
|
|
88
|
+
"inputs": {
|
|
89
|
+
"samples": ["3", 0],
|
|
90
|
+
"vae": ["2", 2]
|
|
91
|
+
},
|
|
92
|
+
"class_type": "VAEDecode",
|
|
93
|
+
"_meta": {
|
|
94
|
+
"title": "VAE Decode"
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"6": {
|
|
98
|
+
"inputs": {
|
|
99
|
+
"filename_prefix": "ComfyUI",
|
|
100
|
+
"images": ["5", 0]
|
|
101
|
+
},
|
|
102
|
+
"class_type": "SaveImage",
|
|
103
|
+
"_meta": {
|
|
104
|
+
"title": "Save Image"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
try {
|
|
109
|
+
const jobId = await pool.submitJob(workflow);
|
|
110
|
+
console.log(`Submitted job: ${jobId}`);
|
|
111
|
+
const result = await pool.waitForJobCompletion(jobId);
|
|
112
|
+
if (result.status === "failed") {
|
|
113
|
+
console.log("ā
Test PASSED: Job failed as expected");
|
|
114
|
+
console.log(`Error classification should be: workflow_incompatibility`);
|
|
115
|
+
console.log(`Actual error:`, result.error);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
console.log("ā Test FAILED: Job should have failed but didn't");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
console.log("ā Test ERROR:", error);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// ============================================================================
|
|
126
|
+
// TEST 2: Missing Custom Node Error (should be classified as workflow_incompatibility)
|
|
127
|
+
// ============================================================================
|
|
128
|
+
async function testMissingCustomNode() {
|
|
129
|
+
console.log("\nš TEST 2: Missing Custom Node Error");
|
|
130
|
+
console.log("-".repeat(80));
|
|
131
|
+
const workflow = new Workflow({
|
|
132
|
+
"1": {
|
|
133
|
+
"inputs": {
|
|
134
|
+
"some_input": "test"
|
|
135
|
+
},
|
|
136
|
+
"class_type": "NonExistentCustomNode_XYZ_12345",
|
|
137
|
+
"_meta": {
|
|
138
|
+
"title": "This Node Does Not Exist"
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
"2": {
|
|
142
|
+
"inputs": {
|
|
143
|
+
"filename_prefix": "test",
|
|
144
|
+
"images": ["1", 0]
|
|
145
|
+
},
|
|
146
|
+
"class_type": "SaveImage",
|
|
147
|
+
"_meta": {
|
|
148
|
+
"title": "Save Image"
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
try {
|
|
153
|
+
const jobId = await pool.submitJob(workflow);
|
|
154
|
+
console.log(`Submitted job: ${jobId}`);
|
|
155
|
+
const result = await pool.waitForJobCompletion(jobId);
|
|
156
|
+
if (result.status === "failed") {
|
|
157
|
+
console.log("ā
Test PASSED: Job failed as expected");
|
|
158
|
+
console.log(`Error classification should be: workflow_incompatibility`);
|
|
159
|
+
console.log(`Actual error:`, result.error);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
console.log("ā Test FAILED: Job should have failed but didn't");
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
console.log("ā Test ERROR:", error);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// ============================================================================
|
|
170
|
+
// TEST 3: Invalid Input Error (should be classified as transient)
|
|
171
|
+
// ============================================================================
|
|
172
|
+
async function testInvalidInput() {
|
|
173
|
+
console.log("\nš TEST 3: Invalid Input Error");
|
|
174
|
+
console.log("-".repeat(80));
|
|
175
|
+
const workflow = new Workflow({
|
|
176
|
+
"1": {
|
|
177
|
+
"inputs": {
|
|
178
|
+
"width": -999999, // Negative width should cause validation error
|
|
179
|
+
"height": -999999, // Negative height should cause validation error
|
|
180
|
+
"batch_size": 1
|
|
181
|
+
},
|
|
182
|
+
"class_type": "EmptyLatentImage",
|
|
183
|
+
"_meta": {
|
|
184
|
+
"title": "Empty Latent Image"
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
"2": {
|
|
188
|
+
"inputs": {
|
|
189
|
+
"filename_prefix": "test",
|
|
190
|
+
"images": ["1", 0]
|
|
191
|
+
},
|
|
192
|
+
"class_type": "SaveImage",
|
|
193
|
+
"_meta": {
|
|
194
|
+
"title": "Save Image"
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
try {
|
|
199
|
+
const jobId = await pool.submitJob(workflow);
|
|
200
|
+
console.log(`Submitted job: ${jobId}`);
|
|
201
|
+
const result = await pool.waitForJobCompletion(jobId);
|
|
202
|
+
if (result.status === "failed") {
|
|
203
|
+
console.log("ā
Test PASSED: Job failed as expected");
|
|
204
|
+
console.log(`Error classification should be: transient`);
|
|
205
|
+
console.log(`Actual error:`, result.error);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
console.log("ā Test FAILED: Job should have failed but didn't");
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
console.log("ā Test ERROR:", error);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// ============================================================================
|
|
216
|
+
// TEST 4: Connection Error (should be classified as connection)
|
|
217
|
+
// ============================================================================
|
|
218
|
+
async function testConnectionError() {
|
|
219
|
+
console.log("\nš TEST 4: Connection Error");
|
|
220
|
+
console.log("-".repeat(80));
|
|
221
|
+
const isolatedPool = new MultiWorkflowPool();
|
|
222
|
+
// Add a client that definitely doesn't exist
|
|
223
|
+
isolatedPool.addClient("http://this-server-does-not-exist-12345:8188", {
|
|
224
|
+
workflowAffinity: [],
|
|
225
|
+
priority: 1
|
|
226
|
+
});
|
|
227
|
+
try {
|
|
228
|
+
console.log("Attempting to connect to non-existent server...");
|
|
229
|
+
await isolatedPool.init();
|
|
230
|
+
console.log("ā Test FAILED: Should have failed to connect");
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
console.log("ā
Test PASSED: Connection failed as expected");
|
|
234
|
+
console.log(`Error classification should be: connection`);
|
|
235
|
+
console.log(`Actual error:`, error);
|
|
236
|
+
}
|
|
237
|
+
finally {
|
|
238
|
+
await isolatedPool.shutdown();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
// ============================================================================
|
|
242
|
+
// TEST 5: Missing LoRA Error (should be classified as workflow_incompatibility)
|
|
243
|
+
// ============================================================================
|
|
244
|
+
async function testMissingLoRA() {
|
|
245
|
+
console.log("\nš TEST 5: Missing LoRA Error");
|
|
246
|
+
console.log("-".repeat(80));
|
|
247
|
+
const workflow = new Workflow({
|
|
248
|
+
"1": {
|
|
249
|
+
"inputs": {
|
|
250
|
+
"text": "beautiful landscape"
|
|
251
|
+
},
|
|
252
|
+
"class_type": "CLIPTextEncode",
|
|
253
|
+
"_meta": {
|
|
254
|
+
"title": "CLIP Text Encode"
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
"2": {
|
|
258
|
+
"inputs": {
|
|
259
|
+
"ckpt_name": "novaAnimeXL_ilV125.safetensors" // Use a model that exists on the servers
|
|
260
|
+
},
|
|
261
|
+
"class_type": "CheckpointLoaderSimple",
|
|
262
|
+
"_meta": {
|
|
263
|
+
"title": "Load Checkpoint"
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
"3": {
|
|
267
|
+
"inputs": {
|
|
268
|
+
"lora_name": "this_lora_does_not_exist_12345.safetensors",
|
|
269
|
+
"strength_model": 1.0,
|
|
270
|
+
"strength_clip": 1.0,
|
|
271
|
+
"model": ["2", 0],
|
|
272
|
+
"clip": ["2", 1]
|
|
273
|
+
},
|
|
274
|
+
"class_type": "LoraLoader",
|
|
275
|
+
"_meta": {
|
|
276
|
+
"title": "Load LoRA"
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
"4": {
|
|
280
|
+
"inputs": {
|
|
281
|
+
"width": 512,
|
|
282
|
+
"height": 512,
|
|
283
|
+
"batch_size": 1
|
|
284
|
+
},
|
|
285
|
+
"class_type": "EmptyLatentImage",
|
|
286
|
+
"_meta": {
|
|
287
|
+
"title": "Empty Latent Image"
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
"5": {
|
|
291
|
+
"inputs": {
|
|
292
|
+
"seed": 12345,
|
|
293
|
+
"steps": 20,
|
|
294
|
+
"cfg": 7.0,
|
|
295
|
+
"sampler_name": "euler",
|
|
296
|
+
"scheduler": "normal",
|
|
297
|
+
"denoise": 1.0,
|
|
298
|
+
"model": ["3", 0],
|
|
299
|
+
"positive": ["1", 0],
|
|
300
|
+
"negative": ["1", 0],
|
|
301
|
+
"latent_image": ["4", 0]
|
|
302
|
+
},
|
|
303
|
+
"class_type": "KSampler",
|
|
304
|
+
"_meta": {
|
|
305
|
+
"title": "KSampler"
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
"6": {
|
|
309
|
+
"inputs": {
|
|
310
|
+
"samples": ["5", 0],
|
|
311
|
+
"vae": ["2", 2]
|
|
312
|
+
},
|
|
313
|
+
"class_type": "VAEDecode",
|
|
314
|
+
"_meta": {
|
|
315
|
+
"title": "VAE Decode"
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
"7": {
|
|
319
|
+
"inputs": {
|
|
320
|
+
"filename_prefix": "ComfyUI",
|
|
321
|
+
"images": ["6", 0]
|
|
322
|
+
},
|
|
323
|
+
"class_type": "SaveImage",
|
|
324
|
+
"_meta": {
|
|
325
|
+
"title": "Save Image"
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
try {
|
|
330
|
+
const jobId = await pool.submitJob(workflow);
|
|
331
|
+
console.log(`Submitted job: ${jobId}`);
|
|
332
|
+
const result = await pool.waitForJobCompletion(jobId);
|
|
333
|
+
if (result.status === "failed") {
|
|
334
|
+
console.log("ā
Test PASSED: Job failed as expected");
|
|
335
|
+
console.log(`Error classification should be: workflow_incompatibility`);
|
|
336
|
+
console.log(`Actual error:`, result.error);
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
console.log("ā Test FAILED: Job should have failed but didn't");
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
catch (error) {
|
|
343
|
+
console.log("ā Test ERROR:", error);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
// ============================================================================
|
|
347
|
+
// Run all tests
|
|
348
|
+
// ============================================================================
|
|
349
|
+
async function runAllTests() {
|
|
350
|
+
try {
|
|
351
|
+
await testMissingModel();
|
|
352
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
353
|
+
await testMissingCustomNode();
|
|
354
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
355
|
+
await testInvalidInput();
|
|
356
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
357
|
+
await testMissingLoRA();
|
|
358
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
359
|
+
await testConnectionError();
|
|
360
|
+
console.log("\n" + "=".repeat(80));
|
|
361
|
+
console.log("ALL TESTS COMPLETED");
|
|
362
|
+
console.log("=".repeat(80) + "\n");
|
|
363
|
+
}
|
|
364
|
+
catch (error) {
|
|
365
|
+
console.error("Fatal error during tests:", error);
|
|
366
|
+
}
|
|
367
|
+
finally {
|
|
368
|
+
await pool.shutdown();
|
|
369
|
+
process.exit(0);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
// Run the tests
|
|
373
|
+
runAllTests();
|
|
374
374
|
//# sourceMappingURL=error-classification-tests.js.map
|