From bbef8c2f0b7bf3d09dcdd0b9bc13e3aa170831ae Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Tue, 11 Aug 2026 01:02:09 -0700 Subject: [PATCH] fix: Handle missing Power Puter workflow metadata in API requests Fixes #726 --- py/power_puter.py | 2 +- src_web/comfyui/tests/power_puter.ts | 53 ++++++++++++++++++++++++++++ uv.lock | 8 +++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 uv.lock diff --git a/py/power_puter.py b/py/power_puter.py index db73f7a5..6693e8bc 100644 --- a/py/power_puter.py +++ b/py/power_puter.py @@ -336,7 +336,7 @@ def main(self, **kwargs): code = kwargs['code'] unique_id = kwargs['unique_id'] pnginfo = kwargs['extra_pnginfo'] - workflow = pnginfo["workflow"] if "workflow" in pnginfo else {"nodes": []} + workflow = pnginfo["workflow"] if pnginfo and "workflow" in pnginfo else {"nodes": []} prompt = kwargs['prompt'] dynprompt = kwargs['dynprompt'] diff --git a/src_web/comfyui/tests/power_puter.ts b/src_web/comfyui/tests/power_puter.ts index 5f0bc07c..5000c229 100644 --- a/src_web/comfyui/tests/power_puter.ts +++ b/src_web/comfyui/tests/power_puter.ts @@ -1,5 +1,7 @@ import type {LGraphNode} from "@comfyorg/frontend"; +import {api} from "scripts/api.js"; +import {app} from "scripts/app.js"; import {NodeTypesString} from "../constants"; import {ComfyUITestEnvironment} from "../testing/comfyui_env"; import {describe, should, beforeEach, expect, describeRun} from "../testing/runner.js"; @@ -55,6 +57,57 @@ describe("TestPowerPuter", async () => { } }); + await should("handle API prompts without workflow metadata", async () => { + setPowerPuterValue(powerPuter, "STRING", '"power" + " puter"'); + const prompt = await app.graphToPrompt(); + let promptId: string | undefined; + const completedPromptIds = new Set(); + const failedPrompts = new Map(); + let resolveExecution!: () => void; + let rejectExecution!: (reason: Error) => void; + const execution = new Promise((resolve, reject) => { + resolveExecution = resolve; + rejectExecution = reject; + }); + const onExecuted = (event: Event) => { + const detail = (event as CustomEvent<{node: string; prompt_id: string}>).detail; + if (detail.node !== String(displayAny.id)) return; + if (detail.prompt_id === promptId) { + resolveExecution(); + } else { + completedPromptIds.add(detail.prompt_id); + } + }; + const onExecutionError = (event: Event) => { + const detail = (event as CustomEvent<{prompt_id: string; exception_message: string}>).detail; + if (detail.prompt_id === promptId) { + rejectExecution(new Error(detail.exception_message)); + } else { + failedPrompts.set(detail.prompt_id, detail.exception_message); + } + }; + api.addEventListener("executed", onExecuted); + api.addEventListener("execution_error", onExecutionError); + + try { + const response = await api.fetchApi("/prompt", { + method: "POST", + headers: {"Content-Type": "application/json"}, + body: JSON.stringify({prompt: prompt.output}), + }); + if (!response.ok) throw new Error(await response.text()); + promptId = ((await response.json()) as {prompt_id: string}).prompt_id; + if (completedPromptIds.has(promptId)) resolveExecution(); + if (failedPrompts.has(promptId)) rejectExecution(new Error(failedPrompts.get(promptId))); + await execution; + } finally { + api.removeEventListener("executed", onExecuted); + api.removeEventListener("execution_error", onExecutionError); + } + + expect(displayAny.widgets![0]!.value).toBe("power puter"); + }); + await should("handle inputs", async () => { // TODO }); diff --git a/uv.lock b/uv.lock new file mode 100644 index 00000000..635f6f41 --- /dev/null +++ b/uv.lock @@ -0,0 +1,8 @@ +version = 1 +revision = 3 +requires-python = ">=3.14" + +[[package]] +name = "rgthree-comfy" +version = "1.0.2607232129" +source = { virtual = "." }