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,283 +1,283 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { JobProfiler } from "./job-profiler.js";
|
|
3
|
-
export class JobStateRegistry {
|
|
4
|
-
pool;
|
|
5
|
-
clients;
|
|
6
|
-
// Map of jobId to JobState
|
|
7
|
-
jobs = new Map();
|
|
8
|
-
// Map of prompt_id to jobId
|
|
9
|
-
promptIdToJobId = new Map();
|
|
10
|
-
constructor(pool, clients) {
|
|
11
|
-
this.pool = pool;
|
|
12
|
-
this.clients = clients;
|
|
13
|
-
}
|
|
14
|
-
addJob(workflow) {
|
|
15
|
-
// Create new job id
|
|
16
|
-
const jobId = randomUUID();
|
|
17
|
-
let resolver = null;
|
|
18
|
-
const resultsPromise = new Promise((resolve) => {
|
|
19
|
-
resolver = resolve;
|
|
20
|
-
});
|
|
21
|
-
const jobState = {
|
|
22
|
-
jobId,
|
|
23
|
-
workflow,
|
|
24
|
-
status: "pending",
|
|
25
|
-
resolver,
|
|
26
|
-
resultsPromise
|
|
27
|
-
};
|
|
28
|
-
// Initialize profiler if enabled
|
|
29
|
-
if (this.pool.options.enableProfiling) {
|
|
30
|
-
jobState.profiler = new JobProfiler(Date.now(), workflow.toJSON());
|
|
31
|
-
}
|
|
32
|
-
this.jobs.set(jobId, jobState);
|
|
33
|
-
return jobId;
|
|
34
|
-
}
|
|
35
|
-
getJobStatus(jobId) {
|
|
36
|
-
const jobState = this.jobs.get(jobId);
|
|
37
|
-
if (!jobState) {
|
|
38
|
-
throw new Error(`Job with ID ${jobId} not found.`);
|
|
39
|
-
}
|
|
40
|
-
return jobState.status;
|
|
41
|
-
}
|
|
42
|
-
async cancelJob(jobId) {
|
|
43
|
-
const jobState = this.jobs.get(jobId);
|
|
44
|
-
if (!jobState) {
|
|
45
|
-
throw new Error(`Job with ID ${jobId} not found.`);
|
|
46
|
-
}
|
|
47
|
-
if (jobState.status === "completed" || jobState.status === "canceled") {
|
|
48
|
-
throw new Error(`Cannot cancel job ${jobId} with status ${jobState.status}.`);
|
|
49
|
-
}
|
|
50
|
-
if (jobState.status === "assigned" || jobState.status === "running") {
|
|
51
|
-
// Notify assigned client to cancel the job
|
|
52
|
-
if (jobState.assignedClientUrl) {
|
|
53
|
-
const client = this.clients.clients.get(jobState.assignedClientUrl);
|
|
54
|
-
if (client) {
|
|
55
|
-
try {
|
|
56
|
-
await client.api.ext.queue.interrupt(jobState.prompt_id);
|
|
57
|
-
// Mark job as canceled
|
|
58
|
-
jobState.status = "canceled";
|
|
59
|
-
// Mark client as idle
|
|
60
|
-
client.state = "idle";
|
|
61
|
-
// Also resolve the promise to avoid hanging
|
|
62
|
-
if (jobState.resolver) {
|
|
63
|
-
const results = {
|
|
64
|
-
status: "canceled",
|
|
65
|
-
jobId: jobState.jobId,
|
|
66
|
-
prompt_id: jobState.prompt_id,
|
|
67
|
-
images: []
|
|
68
|
-
};
|
|
69
|
-
jobState.resolver(results);
|
|
70
|
-
jobState.resolver = null;
|
|
71
|
-
}
|
|
72
|
-
// Process the queue to allow next job to proceed
|
|
73
|
-
this.processQueue(jobState.workflow.structureHash);
|
|
74
|
-
}
|
|
75
|
-
catch (e) {
|
|
76
|
-
console.error(`Failed to notify client ${jobState.assignedClientUrl} to cancel job ${jobId}:`, e);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
// For pending or no_clients status, just mark as canceled
|
|
83
|
-
jobState.status = "canceled";
|
|
84
|
-
// Also resolve the promise to avoid hanging
|
|
85
|
-
if (jobState.resolver) {
|
|
86
|
-
const results = {
|
|
87
|
-
status: "canceled",
|
|
88
|
-
jobId: jobState.jobId,
|
|
89
|
-
prompt_id: jobState.prompt_id,
|
|
90
|
-
images: []
|
|
91
|
-
};
|
|
92
|
-
jobState.resolver(results);
|
|
93
|
-
jobState.resolver = null;
|
|
94
|
-
}
|
|
95
|
-
// Remove from queue if necessary
|
|
96
|
-
this.removeJobFromQueue(jobState);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
setJobStatus(jobId, newStatus, assignedClientUrl) {
|
|
100
|
-
const jobState = this.jobs.get(jobId);
|
|
101
|
-
if (!jobState) {
|
|
102
|
-
throw new Error(`Job with ID ${jobId} not found.`);
|
|
103
|
-
}
|
|
104
|
-
jobState.status = newStatus;
|
|
105
|
-
if (assignedClientUrl) {
|
|
106
|
-
jobState.assignedClientUrl = assignedClientUrl;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
updateJobAutoSeeds(jobId, autoSeeds) {
|
|
110
|
-
const jobState = this.jobs.get(jobId);
|
|
111
|
-
if (!jobState) {
|
|
112
|
-
throw new Error(`Job with ID ${jobId} not found.`);
|
|
113
|
-
}
|
|
114
|
-
jobState.autoSeeds = autoSeeds;
|
|
115
|
-
}
|
|
116
|
-
setPromptId(jobId, prompt_id) {
|
|
117
|
-
const jobState = this.jobs.get(jobId);
|
|
118
|
-
if (!jobState) {
|
|
119
|
-
throw new Error(`Job with ID ${jobId} not found.`);
|
|
120
|
-
}
|
|
121
|
-
jobState.prompt_id = prompt_id;
|
|
122
|
-
this.promptIdToJobId.set(prompt_id, jobId);
|
|
123
|
-
// Notify profiler of execution start
|
|
124
|
-
if (jobState.profiler) {
|
|
125
|
-
jobState.profiler.onExecutionStart(prompt_id);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
completeJob(prompt_id) {
|
|
129
|
-
const jobState = this.jobs.get(this.promptIdToJobId.get(prompt_id) || "");
|
|
130
|
-
if (!jobState || !jobState.prompt_id) {
|
|
131
|
-
throw new Error(`No job state found for prompt_id ${prompt_id} when completing job.`);
|
|
132
|
-
}
|
|
133
|
-
if (jobState.prompt_id === prompt_id) {
|
|
134
|
-
jobState.status = "completed";
|
|
135
|
-
// Notify profiler of completion
|
|
136
|
-
if (jobState.profiler) {
|
|
137
|
-
jobState.profiler.onExecutionComplete();
|
|
138
|
-
}
|
|
139
|
-
if (jobState.resolver) {
|
|
140
|
-
const results = {
|
|
141
|
-
status: "completed",
|
|
142
|
-
jobId: jobState.jobId,
|
|
143
|
-
prompt_id: jobState.prompt_id,
|
|
144
|
-
images: []
|
|
145
|
-
};
|
|
146
|
-
// Prepare images
|
|
147
|
-
if (jobState.images && jobState.images.length > 0 && jobState.assignedClientUrl) {
|
|
148
|
-
const client = this.clients.clients.get(jobState.assignedClientUrl);
|
|
149
|
-
if (client) {
|
|
150
|
-
for (let i = 0; i < jobState.images.length; i++) {
|
|
151
|
-
const image = jobState.images[i];
|
|
152
|
-
const imageUrl = client.api.ext.file.getPathImage(image);
|
|
153
|
-
results.images.push(imageUrl);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
// Add profiler stats if available
|
|
158
|
-
if (jobState.profiler) {
|
|
159
|
-
results.profileStats = jobState.profiler.getStats();
|
|
160
|
-
}
|
|
161
|
-
jobState.resolver(results);
|
|
162
|
-
jobState.resolver = null;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
processQueue(structureHash) {
|
|
167
|
-
let queue = this.pool.queues.get(structureHash || "general");
|
|
168
|
-
if (queue) {
|
|
169
|
-
queue.processQueue().catch(reason => {
|
|
170
|
-
console.error(`Error processing job queue for workflow hash ${structureHash}:`, reason);
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
async waitForResults(jobId) {
|
|
175
|
-
const jobState = this.jobs.get(jobId);
|
|
176
|
-
if (!jobState) {
|
|
177
|
-
throw new Error(`Job with ID ${jobId} not found.`);
|
|
178
|
-
}
|
|
179
|
-
if (!jobState.resultsPromise) {
|
|
180
|
-
throw new Error(`Job with ID ${jobId} does not have a results promise.`);
|
|
181
|
-
}
|
|
182
|
-
return jobState.resultsPromise;
|
|
183
|
-
}
|
|
184
|
-
addJobImages(prompt_id, images) {
|
|
185
|
-
const state = this.jobs.get(this.promptIdToJobId.get(prompt_id) || "");
|
|
186
|
-
if (!state) {
|
|
187
|
-
throw new Error(`No job state found for prompt_id ${prompt_id} when adding images.`);
|
|
188
|
-
}
|
|
189
|
-
if (state.prompt_id === prompt_id) {
|
|
190
|
-
state.images = [...images];
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
removeJobFromQueue(jobState) {
|
|
195
|
-
let queue = this.pool.queues.get(jobState.workflow.structureHash || "general");
|
|
196
|
-
if (queue) {
|
|
197
|
-
queue.dequeueJob(jobState.jobId);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
attachJobProgressListener(jobId, progressListener) {
|
|
201
|
-
const jobState = this.jobs.get(jobId);
|
|
202
|
-
if (!jobState) {
|
|
203
|
-
throw new Error(`Job with ID ${jobId} not found.`);
|
|
204
|
-
}
|
|
205
|
-
jobState.onProgress = progressListener;
|
|
206
|
-
}
|
|
207
|
-
attachJobPreviewListener(jobId, previewListener) {
|
|
208
|
-
const jobState = this.jobs.get(jobId);
|
|
209
|
-
if (!jobState) {
|
|
210
|
-
throw new Error(`Job with ID ${jobId} not found.`);
|
|
211
|
-
}
|
|
212
|
-
jobState.onPreview = previewListener;
|
|
213
|
-
}
|
|
214
|
-
updateJobProgress(prompt_id, value, max, nodeId) {
|
|
215
|
-
const state = this.jobs.get(this.promptIdToJobId.get(prompt_id) || "");
|
|
216
|
-
if (!state) {
|
|
217
|
-
console.warn(`No job state found for prompt_id ${prompt_id} when updating progress.`);
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
if (state.onProgress && state.prompt_id === prompt_id) {
|
|
221
|
-
state.onProgress({ value, max });
|
|
222
|
-
}
|
|
223
|
-
// Notify profiler
|
|
224
|
-
if (state.profiler && nodeId !== undefined) {
|
|
225
|
-
state.profiler.onProgress(nodeId, value, max);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
updateJobPreviewMetadata(prompt_id, metadata, blob) {
|
|
229
|
-
const state = this.jobs.get(this.promptIdToJobId.get(prompt_id) || "");
|
|
230
|
-
if (!state) {
|
|
231
|
-
console.warn(`No job state found for prompt_id ${prompt_id} when updating preview metadata.`);
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
if (state.onPreview && state.prompt_id === prompt_id) {
|
|
235
|
-
state.onPreview({ metadata, blob });
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
setJobFailure(jobId, bodyJSON) {
|
|
239
|
-
const jobState = this.jobs.get(jobId);
|
|
240
|
-
if (!jobState) {
|
|
241
|
-
throw new Error(`Job with ID ${jobId} not found.`);
|
|
242
|
-
}
|
|
243
|
-
jobState.status = "failed";
|
|
244
|
-
// Notify profiler of completion (even on failure)
|
|
245
|
-
if (jobState.profiler) {
|
|
246
|
-
jobState.profiler.onExecutionComplete();
|
|
247
|
-
}
|
|
248
|
-
if (jobState.resolver) {
|
|
249
|
-
const results = {
|
|
250
|
-
status: "failed",
|
|
251
|
-
jobId: jobState.jobId,
|
|
252
|
-
prompt_id: jobState.prompt_id,
|
|
253
|
-
images: [],
|
|
254
|
-
error: bodyJSON
|
|
255
|
-
};
|
|
256
|
-
// Add profiler stats even on failure
|
|
257
|
-
if (jobState.profiler) {
|
|
258
|
-
results.profileStats = jobState.profiler.getStats();
|
|
259
|
-
}
|
|
260
|
-
jobState.resolver(results);
|
|
261
|
-
jobState.resolver = null;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
/**
|
|
265
|
-
* Track node execution start for profiling
|
|
266
|
-
*/
|
|
267
|
-
onNodeExecuting(prompt_id, nodeId) {
|
|
268
|
-
const state = this.jobs.get(this.promptIdToJobId.get(prompt_id) || "");
|
|
269
|
-
if (state?.profiler && state.prompt_id === prompt_id) {
|
|
270
|
-
state.profiler.onNodeExecuting(nodeId);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
/**
|
|
274
|
-
* Track cached nodes for profiling
|
|
275
|
-
*/
|
|
276
|
-
onCachedNodes(prompt_id, nodeIds) {
|
|
277
|
-
const state = this.jobs.get(this.promptIdToJobId.get(prompt_id) || "");
|
|
278
|
-
if (state?.profiler && state.prompt_id === prompt_id) {
|
|
279
|
-
state.profiler.onCachedNodes(nodeIds);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { JobProfiler } from "./job-profiler.js";
|
|
3
|
+
export class JobStateRegistry {
|
|
4
|
+
pool;
|
|
5
|
+
clients;
|
|
6
|
+
// Map of jobId to JobState
|
|
7
|
+
jobs = new Map();
|
|
8
|
+
// Map of prompt_id to jobId
|
|
9
|
+
promptIdToJobId = new Map();
|
|
10
|
+
constructor(pool, clients) {
|
|
11
|
+
this.pool = pool;
|
|
12
|
+
this.clients = clients;
|
|
13
|
+
}
|
|
14
|
+
addJob(workflow) {
|
|
15
|
+
// Create new job id
|
|
16
|
+
const jobId = randomUUID();
|
|
17
|
+
let resolver = null;
|
|
18
|
+
const resultsPromise = new Promise((resolve) => {
|
|
19
|
+
resolver = resolve;
|
|
20
|
+
});
|
|
21
|
+
const jobState = {
|
|
22
|
+
jobId,
|
|
23
|
+
workflow,
|
|
24
|
+
status: "pending",
|
|
25
|
+
resolver,
|
|
26
|
+
resultsPromise
|
|
27
|
+
};
|
|
28
|
+
// Initialize profiler if enabled
|
|
29
|
+
if (this.pool.options.enableProfiling) {
|
|
30
|
+
jobState.profiler = new JobProfiler(Date.now(), workflow.toJSON());
|
|
31
|
+
}
|
|
32
|
+
this.jobs.set(jobId, jobState);
|
|
33
|
+
return jobId;
|
|
34
|
+
}
|
|
35
|
+
getJobStatus(jobId) {
|
|
36
|
+
const jobState = this.jobs.get(jobId);
|
|
37
|
+
if (!jobState) {
|
|
38
|
+
throw new Error(`Job with ID ${jobId} not found.`);
|
|
39
|
+
}
|
|
40
|
+
return jobState.status;
|
|
41
|
+
}
|
|
42
|
+
async cancelJob(jobId) {
|
|
43
|
+
const jobState = this.jobs.get(jobId);
|
|
44
|
+
if (!jobState) {
|
|
45
|
+
throw new Error(`Job with ID ${jobId} not found.`);
|
|
46
|
+
}
|
|
47
|
+
if (jobState.status === "completed" || jobState.status === "canceled") {
|
|
48
|
+
throw new Error(`Cannot cancel job ${jobId} with status ${jobState.status}.`);
|
|
49
|
+
}
|
|
50
|
+
if (jobState.status === "assigned" || jobState.status === "running") {
|
|
51
|
+
// Notify assigned client to cancel the job
|
|
52
|
+
if (jobState.assignedClientUrl) {
|
|
53
|
+
const client = this.clients.clients.get(jobState.assignedClientUrl);
|
|
54
|
+
if (client) {
|
|
55
|
+
try {
|
|
56
|
+
await client.api.ext.queue.interrupt(jobState.prompt_id);
|
|
57
|
+
// Mark job as canceled
|
|
58
|
+
jobState.status = "canceled";
|
|
59
|
+
// Mark client as idle
|
|
60
|
+
client.state = "idle";
|
|
61
|
+
// Also resolve the promise to avoid hanging
|
|
62
|
+
if (jobState.resolver) {
|
|
63
|
+
const results = {
|
|
64
|
+
status: "canceled",
|
|
65
|
+
jobId: jobState.jobId,
|
|
66
|
+
prompt_id: jobState.prompt_id,
|
|
67
|
+
images: []
|
|
68
|
+
};
|
|
69
|
+
jobState.resolver(results);
|
|
70
|
+
jobState.resolver = null;
|
|
71
|
+
}
|
|
72
|
+
// Process the queue to allow next job to proceed
|
|
73
|
+
this.processQueue(jobState.workflow.structureHash);
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
console.error(`Failed to notify client ${jobState.assignedClientUrl} to cancel job ${jobId}:`, e);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
// For pending or no_clients status, just mark as canceled
|
|
83
|
+
jobState.status = "canceled";
|
|
84
|
+
// Also resolve the promise to avoid hanging
|
|
85
|
+
if (jobState.resolver) {
|
|
86
|
+
const results = {
|
|
87
|
+
status: "canceled",
|
|
88
|
+
jobId: jobState.jobId,
|
|
89
|
+
prompt_id: jobState.prompt_id,
|
|
90
|
+
images: []
|
|
91
|
+
};
|
|
92
|
+
jobState.resolver(results);
|
|
93
|
+
jobState.resolver = null;
|
|
94
|
+
}
|
|
95
|
+
// Remove from queue if necessary
|
|
96
|
+
this.removeJobFromQueue(jobState);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
setJobStatus(jobId, newStatus, assignedClientUrl) {
|
|
100
|
+
const jobState = this.jobs.get(jobId);
|
|
101
|
+
if (!jobState) {
|
|
102
|
+
throw new Error(`Job with ID ${jobId} not found.`);
|
|
103
|
+
}
|
|
104
|
+
jobState.status = newStatus;
|
|
105
|
+
if (assignedClientUrl) {
|
|
106
|
+
jobState.assignedClientUrl = assignedClientUrl;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
updateJobAutoSeeds(jobId, autoSeeds) {
|
|
110
|
+
const jobState = this.jobs.get(jobId);
|
|
111
|
+
if (!jobState) {
|
|
112
|
+
throw new Error(`Job with ID ${jobId} not found.`);
|
|
113
|
+
}
|
|
114
|
+
jobState.autoSeeds = autoSeeds;
|
|
115
|
+
}
|
|
116
|
+
setPromptId(jobId, prompt_id) {
|
|
117
|
+
const jobState = this.jobs.get(jobId);
|
|
118
|
+
if (!jobState) {
|
|
119
|
+
throw new Error(`Job with ID ${jobId} not found.`);
|
|
120
|
+
}
|
|
121
|
+
jobState.prompt_id = prompt_id;
|
|
122
|
+
this.promptIdToJobId.set(prompt_id, jobId);
|
|
123
|
+
// Notify profiler of execution start
|
|
124
|
+
if (jobState.profiler) {
|
|
125
|
+
jobState.profiler.onExecutionStart(prompt_id);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
completeJob(prompt_id) {
|
|
129
|
+
const jobState = this.jobs.get(this.promptIdToJobId.get(prompt_id) || "");
|
|
130
|
+
if (!jobState || !jobState.prompt_id) {
|
|
131
|
+
throw new Error(`No job state found for prompt_id ${prompt_id} when completing job.`);
|
|
132
|
+
}
|
|
133
|
+
if (jobState.prompt_id === prompt_id) {
|
|
134
|
+
jobState.status = "completed";
|
|
135
|
+
// Notify profiler of completion
|
|
136
|
+
if (jobState.profiler) {
|
|
137
|
+
jobState.profiler.onExecutionComplete();
|
|
138
|
+
}
|
|
139
|
+
if (jobState.resolver) {
|
|
140
|
+
const results = {
|
|
141
|
+
status: "completed",
|
|
142
|
+
jobId: jobState.jobId,
|
|
143
|
+
prompt_id: jobState.prompt_id,
|
|
144
|
+
images: []
|
|
145
|
+
};
|
|
146
|
+
// Prepare images
|
|
147
|
+
if (jobState.images && jobState.images.length > 0 && jobState.assignedClientUrl) {
|
|
148
|
+
const client = this.clients.clients.get(jobState.assignedClientUrl);
|
|
149
|
+
if (client) {
|
|
150
|
+
for (let i = 0; i < jobState.images.length; i++) {
|
|
151
|
+
const image = jobState.images[i];
|
|
152
|
+
const imageUrl = client.api.ext.file.getPathImage(image);
|
|
153
|
+
results.images.push(imageUrl);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Add profiler stats if available
|
|
158
|
+
if (jobState.profiler) {
|
|
159
|
+
results.profileStats = jobState.profiler.getStats();
|
|
160
|
+
}
|
|
161
|
+
jobState.resolver(results);
|
|
162
|
+
jobState.resolver = null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
processQueue(structureHash) {
|
|
167
|
+
let queue = this.pool.queues.get(structureHash || "general");
|
|
168
|
+
if (queue) {
|
|
169
|
+
queue.processQueue().catch(reason => {
|
|
170
|
+
console.error(`Error processing job queue for workflow hash ${structureHash}:`, reason);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
async waitForResults(jobId) {
|
|
175
|
+
const jobState = this.jobs.get(jobId);
|
|
176
|
+
if (!jobState) {
|
|
177
|
+
throw new Error(`Job with ID ${jobId} not found.`);
|
|
178
|
+
}
|
|
179
|
+
if (!jobState.resultsPromise) {
|
|
180
|
+
throw new Error(`Job with ID ${jobId} does not have a results promise.`);
|
|
181
|
+
}
|
|
182
|
+
return jobState.resultsPromise;
|
|
183
|
+
}
|
|
184
|
+
addJobImages(prompt_id, images) {
|
|
185
|
+
const state = this.jobs.get(this.promptIdToJobId.get(prompt_id) || "");
|
|
186
|
+
if (!state) {
|
|
187
|
+
throw new Error(`No job state found for prompt_id ${prompt_id} when adding images.`);
|
|
188
|
+
}
|
|
189
|
+
if (state.prompt_id === prompt_id) {
|
|
190
|
+
state.images = [...images];
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
removeJobFromQueue(jobState) {
|
|
195
|
+
let queue = this.pool.queues.get(jobState.workflow.structureHash || "general");
|
|
196
|
+
if (queue) {
|
|
197
|
+
queue.dequeueJob(jobState.jobId);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
attachJobProgressListener(jobId, progressListener) {
|
|
201
|
+
const jobState = this.jobs.get(jobId);
|
|
202
|
+
if (!jobState) {
|
|
203
|
+
throw new Error(`Job with ID ${jobId} not found.`);
|
|
204
|
+
}
|
|
205
|
+
jobState.onProgress = progressListener;
|
|
206
|
+
}
|
|
207
|
+
attachJobPreviewListener(jobId, previewListener) {
|
|
208
|
+
const jobState = this.jobs.get(jobId);
|
|
209
|
+
if (!jobState) {
|
|
210
|
+
throw new Error(`Job with ID ${jobId} not found.`);
|
|
211
|
+
}
|
|
212
|
+
jobState.onPreview = previewListener;
|
|
213
|
+
}
|
|
214
|
+
updateJobProgress(prompt_id, value, max, nodeId) {
|
|
215
|
+
const state = this.jobs.get(this.promptIdToJobId.get(prompt_id) || "");
|
|
216
|
+
if (!state) {
|
|
217
|
+
console.warn(`No job state found for prompt_id ${prompt_id} when updating progress.`);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (state.onProgress && state.prompt_id === prompt_id) {
|
|
221
|
+
state.onProgress({ value, max });
|
|
222
|
+
}
|
|
223
|
+
// Notify profiler
|
|
224
|
+
if (state.profiler && nodeId !== undefined) {
|
|
225
|
+
state.profiler.onProgress(nodeId, value, max);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
updateJobPreviewMetadata(prompt_id, metadata, blob) {
|
|
229
|
+
const state = this.jobs.get(this.promptIdToJobId.get(prompt_id) || "");
|
|
230
|
+
if (!state) {
|
|
231
|
+
console.warn(`No job state found for prompt_id ${prompt_id} when updating preview metadata.`);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (state.onPreview && state.prompt_id === prompt_id) {
|
|
235
|
+
state.onPreview({ metadata, blob });
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
setJobFailure(jobId, bodyJSON) {
|
|
239
|
+
const jobState = this.jobs.get(jobId);
|
|
240
|
+
if (!jobState) {
|
|
241
|
+
throw new Error(`Job with ID ${jobId} not found.`);
|
|
242
|
+
}
|
|
243
|
+
jobState.status = "failed";
|
|
244
|
+
// Notify profiler of completion (even on failure)
|
|
245
|
+
if (jobState.profiler) {
|
|
246
|
+
jobState.profiler.onExecutionComplete();
|
|
247
|
+
}
|
|
248
|
+
if (jobState.resolver) {
|
|
249
|
+
const results = {
|
|
250
|
+
status: "failed",
|
|
251
|
+
jobId: jobState.jobId,
|
|
252
|
+
prompt_id: jobState.prompt_id,
|
|
253
|
+
images: [],
|
|
254
|
+
error: bodyJSON
|
|
255
|
+
};
|
|
256
|
+
// Add profiler stats even on failure
|
|
257
|
+
if (jobState.profiler) {
|
|
258
|
+
results.profileStats = jobState.profiler.getStats();
|
|
259
|
+
}
|
|
260
|
+
jobState.resolver(results);
|
|
261
|
+
jobState.resolver = null;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Track node execution start for profiling
|
|
266
|
+
*/
|
|
267
|
+
onNodeExecuting(prompt_id, nodeId) {
|
|
268
|
+
const state = this.jobs.get(this.promptIdToJobId.get(prompt_id) || "");
|
|
269
|
+
if (state?.profiler && state.prompt_id === prompt_id) {
|
|
270
|
+
state.profiler.onNodeExecuting(nodeId);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Track cached nodes for profiling
|
|
275
|
+
*/
|
|
276
|
+
onCachedNodes(prompt_id, nodeIds) {
|
|
277
|
+
const state = this.jobs.get(this.promptIdToJobId.get(prompt_id) || "");
|
|
278
|
+
if (state?.profiler && state.prompt_id === prompt_id) {
|
|
279
|
+
state.profiler.onCachedNodes(nodeIds);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
283
|
//# sourceMappingURL=job-state-registry.js.map
|