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.
Files changed (49) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/index.d.ts +18 -13
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +10 -7
  5. package/dist/index.js.map +1 -1
  6. package/dist/multipool/client-registry.js +152 -152
  7. package/dist/multipool/helpers.js +52 -52
  8. package/dist/multipool/helpers.js.map +1 -1
  9. package/dist/multipool/index.js +2 -2
  10. package/dist/multipool/interfaces.d.ts +29 -12
  11. package/dist/multipool/interfaces.d.ts.map +1 -1
  12. package/dist/multipool/interfaces.js +1 -1
  13. package/dist/multipool/job-state-registry.js +282 -282
  14. package/dist/multipool/multi-workflow-pool.d.ts +102 -42
  15. package/dist/multipool/multi-workflow-pool.d.ts.map +1 -1
  16. package/dist/multipool/multi-workflow-pool.js +424 -313
  17. package/dist/multipool/multi-workflow-pool.js.map +1 -1
  18. package/dist/multipool/pool-event-manager.js +27 -27
  19. package/dist/multipool/tests/client-registry-api-demo.d.ts +7 -0
  20. package/dist/multipool/tests/client-registry-api-demo.d.ts.map +1 -0
  21. package/dist/multipool/tests/client-registry-api-demo.js +136 -0
  22. package/dist/multipool/tests/client-registry-api-demo.js.map +1 -0
  23. package/dist/multipool/tests/client-registry.spec.d.ts +2 -0
  24. package/dist/multipool/tests/client-registry.spec.d.ts.map +1 -0
  25. package/dist/multipool/tests/client-registry.spec.js +191 -0
  26. package/dist/multipool/tests/client-registry.spec.js.map +1 -0
  27. package/dist/multipool/tests/error-classification-tests.js +373 -373
  28. package/dist/multipool/tests/event-forwarding-demo.d.ts +7 -0
  29. package/dist/multipool/tests/event-forwarding-demo.d.ts.map +1 -0
  30. package/dist/multipool/tests/event-forwarding-demo.js +88 -0
  31. package/dist/multipool/tests/event-forwarding-demo.js.map +1 -0
  32. package/dist/multipool/tests/helpers.spec.d.ts +2 -0
  33. package/dist/multipool/tests/helpers.spec.d.ts.map +1 -0
  34. package/dist/multipool/tests/helpers.spec.js +100 -0
  35. package/dist/multipool/tests/helpers.spec.js.map +1 -0
  36. package/dist/multipool/tests/job-queue-processor.spec.d.ts +2 -0
  37. package/dist/multipool/tests/job-queue-processor.spec.d.ts.map +1 -0
  38. package/dist/multipool/tests/job-queue-processor.spec.js +89 -0
  39. package/dist/multipool/tests/job-queue-processor.spec.js.map +1 -0
  40. package/dist/multipool/tests/job-state-registry.spec.d.ts +2 -0
  41. package/dist/multipool/tests/job-state-registry.spec.d.ts.map +1 -0
  42. package/dist/multipool/tests/job-state-registry.spec.js +143 -0
  43. package/dist/multipool/tests/job-state-registry.spec.js.map +1 -0
  44. package/dist/multipool/tests/multipool-basic.js +141 -141
  45. package/dist/multipool/tests/profiling-demo.js +87 -87
  46. package/dist/multipool/tests/profiling-demo.js.map +1 -1
  47. package/dist/multipool/tests/two-stage-edit-simulation.js +298 -298
  48. package/dist/multipool/tests/two-stage-edit-simulation.js.map +1 -1
  49. package/package.json +1 -1
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Event Forwarding Demo for MultiWorkflowPool
3
+ *
4
+ * Demonstrates that all ComfyUI client events are forwarded through PoolEventManager
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=event-forwarding-demo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-forwarding-demo.d.ts","sourceRoot":"","sources":["../../../src/multipool/tests/event-forwarding-demo.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Event Forwarding Demo for MultiWorkflowPool
3
+ *
4
+ * Demonstrates that all ComfyUI client events are forwarded through PoolEventManager
5
+ */
6
+ import { MultiWorkflowPool } from "../multi-workflow-pool.js";
7
+ import { Workflow } from "../workflow.js";
8
+ import GenerationGraph from "../../../scripts/workflows/T2I-one-obsession.json" with { type: "json" };
9
+ const GEN_HOST = "http://localhost:8188";
10
+ // Create pool
11
+ const pool = new MultiWorkflowPool({
12
+ logLevel: "warn" // Keep logs minimal to see events clearly
13
+ });
14
+ const genWorkflow = Workflow.fromAugmented(GenerationGraph);
15
+ pool.addClient(GEN_HOST, {
16
+ workflowAffinity: [genWorkflow],
17
+ priority: 1
18
+ });
19
+ console.log("\n" + "=".repeat(80));
20
+ console.log("EVENT FORWARDING DEMO - MultiWorkflowPool");
21
+ console.log("=".repeat(80));
22
+ console.log(`Generation Host: ${GEN_HOST}`);
23
+ console.log("Testing: All client events forwarded through PoolEventManager");
24
+ console.log("=".repeat(80) + "\n");
25
+ // Track all events received
26
+ const eventLog = [];
27
+ // Attach event hooks for various client events
28
+ const trackedEvents = [
29
+ "client:status",
30
+ "client:progress",
31
+ "client:executing",
32
+ "client:execution_cached",
33
+ "client:executed",
34
+ "client:execution_success",
35
+ "client:b_preview_meta"
36
+ ];
37
+ trackedEvents.forEach(eventType => {
38
+ pool.attachEventHook(eventType, (event) => {
39
+ eventLog.push({
40
+ type: event.type,
41
+ clientName: event.payload.clientName,
42
+ eventType: event.payload.eventType
43
+ });
44
+ console.log(`[Event Hook] ${event.type} from ${event.payload.clientName}`);
45
+ });
46
+ });
47
+ console.log(`✅ Attached event hooks for: ${trackedEvents.join(", ")}\n`);
48
+ await pool.init();
49
+ console.log("\n📤 Submitting job to trigger events...\n");
50
+ // Run a simple generation job
51
+ const workflow = Workflow.fromAugmented(GenerationGraph)
52
+ .input("1", "value", "test image, simple")
53
+ .input("10", "steps", 5) // Low steps for faster execution
54
+ .input("10", "seed", 42);
55
+ const jobId = await pool.submitJob(workflow);
56
+ await pool.waitForJobCompletion(jobId);
57
+ console.log("\n" + "=".repeat(80));
58
+ console.log("JOB COMPLETED - EVENT LOG SUMMARY");
59
+ console.log("=".repeat(80));
60
+ console.log(`\n📊 Total Events Captured: ${eventLog.length}\n`);
61
+ // Group events by type
62
+ const eventsByType = new Map();
63
+ eventLog.forEach(log => {
64
+ eventsByType.set(log.type, (eventsByType.get(log.type) || 0) + 1);
65
+ });
66
+ console.log("Event Type Breakdown:");
67
+ eventsByType.forEach((count, type) => {
68
+ console.log(` ${type}: ${count} events`);
69
+ });
70
+ // Verify critical events were captured
71
+ const criticalEvents = ["client:status", "client:execution_success"];
72
+ const missingEvents = criticalEvents.filter(e => !eventsByType.has(e));
73
+ if (missingEvents.length > 0) {
74
+ console.log(`\n⚠️ WARNING: Missing critical events: ${missingEvents.join(", ")}`);
75
+ }
76
+ else {
77
+ console.log("\n✅ All critical events were forwarded successfully!");
78
+ }
79
+ // Show detailed event timeline
80
+ console.log("\n📅 Event Timeline:");
81
+ eventLog.forEach((log, i) => {
82
+ console.log(` ${i + 1}. ${log.type} (${log.eventType}) from ${log.clientName}`);
83
+ });
84
+ console.log("\n" + "=".repeat(80));
85
+ await pool.shutdown();
86
+ console.log("\n✅ Event forwarding demo completed, exiting...");
87
+ process.exit(0);
88
+ //# sourceMappingURL=event-forwarding-demo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-forwarding-demo.js","sourceRoot":"","sources":["../../../src/multipool/tests/event-forwarding-demo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=helpers.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.spec.d.ts","sourceRoot":"","sources":["../../../src/multipool/tests/helpers.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,100 @@
1
+ import { describe, it, expect } from "bun:test";
2
+ import { classifyFailure } from "../helpers.js";
3
+ describe("classifyFailure", () => {
4
+ // Test for Connection Failure
5
+ it("should classify connection errors", () => {
6
+ const connectionError = new Error("Connection timed out");
7
+ expect(classifyFailure(connectionError)).toEqual({
8
+ type: "connection",
9
+ message: "Connection timed out",
10
+ });
11
+ const anotherConnectionError = { message: "connect ECONNREFUSED 127.0.0.1:8188" };
12
+ expect(classifyFailure(anotherConnectionError)).toEqual({
13
+ type: "connection",
14
+ message: "connect ECONNREFUSED 127.0.0.1:8188",
15
+ });
16
+ });
17
+ // Test for Workflow Incompatibility - Missing Model
18
+ it("should classify missing model errors as workflow_incompatibility", () => {
19
+ const missingModelError = {
20
+ bodyJSON: {
21
+ node_errors: {
22
+ "2": {
23
+ errors: [
24
+ {
25
+ type: "value_not_in_list",
26
+ details: "Value not in list: ckpt_name 'this_model_does_not_exist.safetensors' not in [...]",
27
+ },
28
+ ],
29
+ },
30
+ },
31
+ },
32
+ };
33
+ const classification = classifyFailure(missingModelError);
34
+ expect(classification.type).toBe("workflow_incompatibility");
35
+ expect(classification.message).toContain("Missing model file");
36
+ });
37
+ // Test for Workflow Incompatibility - Missing Custom Node
38
+ it("should classify missing custom node errors as workflow_incompatibility", () => {
39
+ const missingNodeError = {
40
+ bodyJSON: {
41
+ error: {
42
+ type: "invalid_prompt",
43
+ message: "Node type not found: NonExistentNode",
44
+ },
45
+ node_errors: {},
46
+ },
47
+ };
48
+ const classification = classifyFailure(missingNodeError);
49
+ expect(classification.type).toBe("workflow_incompatibility");
50
+ });
51
+ it('should classify python exception for missing nodes as workflow_incompatibility', () => {
52
+ const pythonError = {
53
+ bodyJSON: {
54
+ "exception_type": "Exception",
55
+ "exception_message": "Node type not found: ImpactWildcardEncode",
56
+ "traceback": "..."
57
+ }
58
+ };
59
+ const classification = classifyFailure(pythonError);
60
+ expect(classification.type).toBe('workflow_incompatibility');
61
+ expect(classification.message).toBe('Node type not found: ImpactWildcardEncode');
62
+ });
63
+ // Test for Transient Failure - Out of Memory
64
+ it("should classify OOM errors as transient", () => {
65
+ const oomError = {
66
+ bodyJSON: {
67
+ exception_message: "CUDA out of memory. Tried to allocate...",
68
+ },
69
+ };
70
+ expect(classifyFailure(oomError)).toEqual({
71
+ type: "transient",
72
+ message: "CUDA out of memory",
73
+ });
74
+ });
75
+ // Test for Transient Failure - Other exceptions
76
+ it("should classify other exceptions as transient", () => {
77
+ const genericError = {
78
+ bodyJSON: {
79
+ exception_message: "Something else went wrong",
80
+ },
81
+ };
82
+ expect(classifyFailure(genericError)).toEqual({
83
+ type: "transient",
84
+ message: "Something else went wrong",
85
+ });
86
+ });
87
+ // Test for Default Case
88
+ it("should default to transient for unknown errors with a response body", () => {
89
+ const unknownError = {
90
+ bodyJSON: {
91
+ some_unusual_error: "details here",
92
+ },
93
+ };
94
+ expect(classifyFailure(unknownError)).toEqual({
95
+ type: "transient",
96
+ message: '{"some_unusual_error":"details here"}',
97
+ });
98
+ });
99
+ });
100
+ //# sourceMappingURL=helpers.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.spec.js","sourceRoot":"","sources":["../../../src/multipool/tests/helpers.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=job-queue-processor.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job-queue-processor.spec.d.ts","sourceRoot":"","sources":["../../../src/multipool/tests/job-queue-processor.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,89 @@
1
+ import { describe, it, expect, beforeEach, jest } from "bun:test";
2
+ import { JobQueueProcessor } from "../job-queue-processor.js";
3
+ import { Workflow } from "../workflow.js";
4
+ import { Logger } from "../logger.js";
5
+ // Mock dependencies
6
+ const createJobStateRegistryMock = () => ({
7
+ getJobStatus: jest.fn().mockReturnValue("pending"),
8
+ setJobStatus: jest.fn(),
9
+ setPromptId: jest.fn(),
10
+ updateJobAutoSeeds: jest.fn(),
11
+ setJobFailure: jest.fn(),
12
+ });
13
+ const createClientRegistryMock = () => ({
14
+ getOptimalClient: jest.fn(),
15
+ getOptimalIdleClient: jest.fn(),
16
+ clients: new Map(),
17
+ markClientIncompatibleWithWorkflow: jest.fn(),
18
+ getAllEligibleClientsForWorkflow: jest.fn().mockReturnValue([]),
19
+ });
20
+ const createLoggerMock = () => new Logger("test", "silent");
21
+ describe("JobQueueProcessor", () => {
22
+ let jobStateRegistryMock;
23
+ let clientRegistryMock;
24
+ let loggerMock;
25
+ beforeEach(() => {
26
+ jobStateRegistryMock = createJobStateRegistryMock();
27
+ clientRegistryMock = createClientRegistryMock();
28
+ loggerMock = createLoggerMock();
29
+ });
30
+ const createProcessor = (hash = "test-hash") => new JobQueueProcessor(jobStateRegistryMock, clientRegistryMock, hash, loggerMock);
31
+ it("should enqueue a job and trigger processing", async () => {
32
+ const processor = createProcessor();
33
+ const processQueueSpy = jest.spyOn(processor, "processQueue").mockImplementation(async () => { });
34
+ await processor.enqueueJob("job-1", new Workflow({}));
35
+ expect(processor.queue).toHaveLength(1);
36
+ expect(processor.queue[0].jobId).toBe("job-1");
37
+ expect(processQueueSpy).toHaveBeenCalled();
38
+ });
39
+ it("should not process queue if already processing", async () => {
40
+ const processor = createProcessor();
41
+ processor.isProcessing = true;
42
+ const loggerSpy = jest.spyOn(loggerMock, 'debug');
43
+ await processor.processQueue();
44
+ expect(loggerSpy).toHaveBeenCalledWith(`Job queue for workflow hash test-hash is already being processed, skipping.`);
45
+ });
46
+ it("should assign a job to an optimal client and run it successfully", async () => {
47
+ const processor = createProcessor();
48
+ const workflow = new Workflow({});
49
+ workflow.uploadAssets = jest.fn().mockResolvedValue(undefined);
50
+ const jobId = "job-1";
51
+ const clientMock = {
52
+ url: "http://localhost:8188",
53
+ nodeName: "test-client",
54
+ state: "idle",
55
+ api: {
56
+ getQueue: jest.fn().mockResolvedValue({ queue_running: [], queue_pending: [] }),
57
+ ext: { queue: { queuePrompt: jest.fn().mockResolvedValue({ prompt_id: "prompt-1" }) } },
58
+ },
59
+ };
60
+ clientRegistryMock.getOptimalClient.mockReturnValue(clientMock);
61
+ processor.queue.push({ jobId, workflow, attempts: 1 });
62
+ await processor.processQueue();
63
+ expect(clientRegistryMock.getOptimalClient).toHaveBeenCalledWith(workflow);
64
+ expect(jobStateRegistryMock.setJobStatus).toHaveBeenCalledWith(jobId, "assigned", clientMock.url);
65
+ expect(workflow.uploadAssets).toHaveBeenCalledWith(clientMock.api);
66
+ expect(clientMock.api.ext.queue.queuePrompt).toHaveBeenCalled();
67
+ expect(jobStateRegistryMock.setPromptId).toHaveBeenCalledWith(jobId, "prompt-1");
68
+ expect(jobStateRegistryMock.setJobStatus).toHaveBeenCalledWith(jobId, "running");
69
+ expect(clientMock.state).toBe("busy");
70
+ });
71
+ it("should re-queue a job if no idle clients are available", async () => {
72
+ const processor = createProcessor();
73
+ const workflow = new Workflow({});
74
+ const jobId = "job-1";
75
+ clientRegistryMock.getOptimalClient.mockReturnValue(null);
76
+ processor.queue.push({ jobId, workflow, attempts: 1 });
77
+ await processor.processQueue();
78
+ expect(jobStateRegistryMock.setJobStatus).toHaveBeenCalledWith(jobId, "pending");
79
+ expect(processor.queue).toHaveLength(1);
80
+ expect(processor.isProcessing).toBe(false);
81
+ });
82
+ it("should dequeue a job", () => {
83
+ const processor = createProcessor();
84
+ processor.queue = [{ jobId: "job-1", workflow: new Workflow({}), attempts: 1 }];
85
+ processor.dequeueJob("job-1");
86
+ expect(processor.queue).toHaveLength(0);
87
+ });
88
+ });
89
+ //# sourceMappingURL=job-queue-processor.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job-queue-processor.spec.js","sourceRoot":"","sources":["../../../src/multipool/tests/job-queue-processor.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=job-state-registry.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job-state-registry.spec.d.ts","sourceRoot":"","sources":["../../../src/multipool/tests/job-state-registry.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,143 @@
1
+ import { describe, it, expect, beforeEach, jest } from "bun:test";
2
+ import { JobStateRegistry } from "../job-state-registry.js";
3
+ import { ClientRegistry } from "../client-registry.js";
4
+ import { Workflow } from "../workflow.js";
5
+ import { Logger } from "../logger.js";
6
+ describe("JobStateRegistry", () => {
7
+ let jobRegistry;
8
+ let poolMock;
9
+ let clientRegistryMock;
10
+ let loggerMock;
11
+ beforeEach(() => {
12
+ // Mock MultiWorkflowPool
13
+ poolMock = {
14
+ options: { enableProfiling: false },
15
+ queues: new Map(),
16
+ };
17
+ loggerMock = new Logger("test", "silent");
18
+ // Mock ClientRegistry
19
+ clientRegistryMock = new ClientRegistry(poolMock, loggerMock);
20
+ // Initialize JobStateRegistry
21
+ jobRegistry = new JobStateRegistry(poolMock, clientRegistryMock);
22
+ });
23
+ it("should add a new job with a pending status", () => {
24
+ const workflow = new Workflow({});
25
+ const jobId = jobRegistry.addJob(workflow);
26
+ expect(jobId).toBeString();
27
+ expect(jobRegistry.getJobStatus(jobId)).toBe("pending");
28
+ const jobState = jobRegistry.jobs.get(jobId);
29
+ expect(jobState).toBeDefined();
30
+ expect(jobState?.workflow).toBe(workflow);
31
+ expect(jobState?.resolver).toBeFunction();
32
+ expect(jobState?.resultsPromise).toBeInstanceOf(Promise);
33
+ });
34
+ it("should throw an error when getting the status of a non-existent job", () => {
35
+ expect(() => jobRegistry.getJobStatus("non-existent-job-id")).toThrow("Job with ID non-existent-job-id not found.");
36
+ });
37
+ it("should set job status", () => {
38
+ const workflow = new Workflow({});
39
+ const jobId = jobRegistry.addJob(workflow);
40
+ jobRegistry.setJobStatus(jobId, "assigned", "http://localhost:8188");
41
+ expect(jobRegistry.getJobStatus(jobId)).toBe("assigned");
42
+ const jobState = jobRegistry.jobs.get(jobId);
43
+ expect(jobState?.assignedClientUrl).toBe("http://localhost:8188");
44
+ });
45
+ it("should set prompt ID and map it to job ID", () => {
46
+ const workflow = new Workflow({});
47
+ const jobId = jobRegistry.addJob(workflow);
48
+ const promptId = "prompt-123";
49
+ jobRegistry.setPromptId(jobId, promptId);
50
+ const jobState = jobRegistry.jobs.get(jobId);
51
+ expect(jobState?.prompt_id).toBe(promptId);
52
+ expect(jobRegistry.promptIdToJobId.get(promptId)).toBe(jobId);
53
+ });
54
+ it("should complete a job and resolve the promise", async () => {
55
+ const workflow = new Workflow({});
56
+ const jobId = jobRegistry.addJob(workflow);
57
+ const promptId = "prompt-123";
58
+ jobRegistry.setPromptId(jobId, promptId);
59
+ jobRegistry.setJobStatus(jobId, "running", "http://localhost:8188");
60
+ const resultsPromise = jobRegistry.waitForResults(jobId);
61
+ // Simulate adding images
62
+ jobRegistry.addJobImages(promptId, [{ filename: "test.png", subfolder: "", type: "output" }]);
63
+ // Mock client for image URL generation
64
+ clientRegistryMock.clients.set("http://localhost:8188", {
65
+ api: { ext: { file: { getPathImage: (image) => `http://localhost:8188/view?filename=${image.filename}` } } }
66
+ });
67
+ jobRegistry.completeJob(promptId);
68
+ const results = await resultsPromise;
69
+ expect(jobRegistry.getJobStatus(jobId)).toBe("completed");
70
+ expect(results.status).toBe("completed");
71
+ expect(results.jobId).toBe(jobId);
72
+ expect(results.prompt_id).toBe(promptId);
73
+ expect(results.images).toEqual(["http://localhost:8188/view?filename=test.png"]);
74
+ });
75
+ it("should fail a job and resolve the promise with an error", async () => {
76
+ const workflow = new Workflow({});
77
+ const jobId = jobRegistry.addJob(workflow);
78
+ const promptId = "prompt-456";
79
+ jobRegistry.setPromptId(jobId, promptId);
80
+ jobRegistry.setJobStatus(jobId, "running");
81
+ const resultsPromise = jobRegistry.waitForResults(jobId);
82
+ const errorDetails = { error: "Test error" };
83
+ jobRegistry.setJobFailure(jobId, errorDetails);
84
+ const results = await resultsPromise;
85
+ expect(jobRegistry.getJobStatus(jobId)).toBe("failed");
86
+ expect(results.status).toBe("failed");
87
+ expect(results.jobId).toBe(jobId);
88
+ expect(results.prompt_id).toBe(promptId);
89
+ expect(results.error).toEqual(errorDetails);
90
+ });
91
+ it("should enable profiling when the pool option is set", () => {
92
+ poolMock.options.enableProfiling = true;
93
+ jobRegistry = new JobStateRegistry(poolMock, clientRegistryMock);
94
+ const workflow = new Workflow({});
95
+ const jobId = jobRegistry.addJob(workflow);
96
+ const jobState = jobRegistry.jobs.get(jobId);
97
+ expect(jobState?.profiler).toBeDefined();
98
+ });
99
+ describe("cancelJob", () => {
100
+ it("should cancel a pending job", async () => {
101
+ const workflow = new Workflow({});
102
+ const jobId = jobRegistry.addJob(workflow);
103
+ const resultsPromise = jobRegistry.waitForResults(jobId);
104
+ // Mock queue to check if dequeue is called
105
+ const queueMock = { dequeueJob: jest.fn() };
106
+ jobRegistry.pool.queues.set(workflow.structureHash || "general", queueMock);
107
+ await jobRegistry.cancelJob(jobId);
108
+ expect(jobRegistry.getJobStatus(jobId)).toBe("canceled");
109
+ expect(queueMock.dequeueJob).toHaveBeenCalledWith(jobId);
110
+ const result = await resultsPromise;
111
+ expect(result.status).toBe("canceled");
112
+ });
113
+ it("should cancel a running job", async () => {
114
+ const workflow = new Workflow({});
115
+ const jobId = jobRegistry.addJob(workflow);
116
+ const promptId = "prompt-789";
117
+ const clientUrl = "http://localhost:8188";
118
+ jobRegistry.setJobStatus(jobId, "running", clientUrl);
119
+ jobRegistry.setPromptId(jobId, promptId);
120
+ const resultsPromise = jobRegistry.waitForResults(jobId);
121
+ // Mock client and its API
122
+ const clientApiMock = { ext: { queue: { interrupt: jest.fn() } } };
123
+ clientRegistryMock.clients.set(clientUrl, { api: clientApiMock, state: "busy" });
124
+ // Mock queue processing
125
+ const queueMock = { processQueue: jest.fn().mockResolvedValue(undefined) };
126
+ jobRegistry.pool.queues.set(workflow.structureHash || "general", queueMock);
127
+ await jobRegistry.cancelJob(jobId);
128
+ expect(jobRegistry.getJobStatus(jobId)).toBe("canceled");
129
+ expect(clientApiMock.ext.queue.interrupt).toHaveBeenCalledWith(promptId);
130
+ expect(clientRegistryMock.clients.get(clientUrl)?.state).toBe("idle");
131
+ expect(queueMock.processQueue).toHaveBeenCalled();
132
+ const result = await resultsPromise;
133
+ expect(result.status).toBe("canceled");
134
+ });
135
+ it("should throw an error when trying to cancel a completed job", async () => {
136
+ const workflow = new Workflow({});
137
+ const jobId = jobRegistry.addJob(workflow);
138
+ jobRegistry.setJobStatus(jobId, "completed");
139
+ await expect(jobRegistry.cancelJob(jobId)).rejects.toThrow(`Cannot cancel job ${jobId} with status completed.`);
140
+ });
141
+ });
142
+ });
143
+ //# sourceMappingURL=job-state-registry.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job-state-registry.spec.js","sourceRoot":"","sources":["../../../src/multipool/tests/job-state-registry.spec.ts"],"names":[],"mappings":"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"}