refactor(media): share the ComfyUI JSON request helpers

Queue, interrupt and prompt requests now go through one JSON helper, so the
cancel path reads like the rest of the client.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
This commit is contained in:
Claude 2026-10-03 13:45:50 +00:00
parent 4217e4b4af
commit 3b97805f20
No known key found for this signature in database

View File

@ -75,6 +75,7 @@ export interface ComfyUiProvider extends ImageGenerationProvider, VideoGeneratio
}
type MediaKind = "image" | "video";
type ReadBody<T> = (response: Response, signal: AbortSignal) => Promise<T>;
interface OutputFile {
filename: string;
@ -259,20 +260,18 @@ class ComfyUiClient implements ComfyUiProvider {
}
private async submit(graph: ComfyWorkflow, signal: AbortSignal): Promise<string> {
const body = JSON.stringify({ prompt: graph, client_id: this.clientId });
return this.send("prompt", { method: "POST", headers: JSON_HEADERS, body }, signal, {
read: async (response, readSignal) => {
if (response.status === 400) {
const rejection = await readJson(response, readSignal).catch(() => undefined);
throw new Error(describeWorkflowRejection(rejection));
}
const result = await readOkJson(response, readSignal);
const promptId = isRecord(result) ? result.prompt_id : undefined;
if (typeof promptId !== "string" || !promptId) {
throw new Error("ComfyUI did not accept the workflow");
}
return promptId;
},
const payload = { prompt: graph, client_id: this.clientId };
return this.postJson("prompt", payload, signal, async (response, readSignal) => {
if (response.status === 400) {
const rejection = await readJson(response, readSignal).catch(() => undefined);
throw new Error(describeWorkflowRejection(rejection));
}
const result = await readOkJson(response, readSignal);
const promptId = isRecord(result) ? result.prompt_id : undefined;
if (typeof promptId !== "string" || !promptId) {
throw new Error("ComfyUI did not accept the workflow");
}
return promptId;
});
}
@ -291,29 +290,20 @@ class ComfyUiClient implements ComfyUiProvider {
/** Best effort: drops the prompt from the queue, or interrupts it when it is ours and running. */
private async cancelPrompt(promptId: string): Promise<void> {
const options = { timeoutMs: CANCEL_TIMEOUT_MS, read: discardBody };
try {
const body = JSON.stringify({ delete: [promptId] });
await this.send("queue", { method: "POST", headers: JSON_HEADERS, body }, undefined, options);
const queue = await this.send(
await this.postJson(
"queue",
{ headers: { Accept: "application/json" } },
{ delete: [promptId] },
undefined,
{
timeoutMs: CANCEL_TIMEOUT_MS,
read: readOkJson,
},
discardBody,
CANCEL_TIMEOUT_MS,
);
const queue = await this.getJson("queue", undefined, CANCEL_TIMEOUT_MS);
const running =
isRecord(queue) && Array.isArray(queue.queue_running) ? queue.queue_running : [];
if (running.some((entry) => Array.isArray(entry) && entry[1] === promptId)) {
const interrupt = JSON.stringify({ prompt_id: promptId });
await this.send(
"interrupt",
{ method: "POST", headers: JSON_HEADERS, body: interrupt },
undefined,
options,
);
const interrupt = { prompt_id: promptId };
await this.postJson("interrupt", interrupt, undefined, discardBody, CANCEL_TIMEOUT_MS);
}
} catch {
// The prompt then finishes on its own; nothing waits for it.
@ -379,10 +369,20 @@ class ComfyUiClient implements ComfyUiProvider {
});
}
private getJson(path: string, signal: AbortSignal | undefined): Promise<unknown> {
return this.send(path, { headers: { Accept: "application/json" } }, signal, {
read: readOkJson,
});
private getJson(path: string, signal: AbortSignal | undefined, timeoutMs?: number) {
const init = { headers: { Accept: "application/json" } };
return this.send(path, init, signal, { timeoutMs, read: readOkJson });
}
private postJson<T>(
path: string,
payload: unknown,
signal: AbortSignal | undefined,
read: ReadBody<T>,
timeoutMs?: number,
): Promise<T> {
const init = { method: "POST", headers: JSON_HEADERS, body: JSON.stringify(payload) };
return this.send(path, init, signal, { timeoutMs, read });
}
/** One request, with the timeout covering the response body too. */
@ -394,10 +394,7 @@ class ComfyUiClient implements ComfyUiProvider {
body?: string | Uint8Array<ArrayBuffer>;
},
signal: AbortSignal | undefined,
options: {
timeoutMs?: number;
read: (response: Response, signal: AbortSignal) => Promise<T>;
},
options: { timeoutMs?: number; read: ReadBody<T> },
): Promise<T> {
const timeout = AbortSignal.timeout(options.timeoutMs ?? REQUEST_TIMEOUT_MS);
const requestSignal = signal ? AbortSignal.any([signal, timeout]) : timeout;