Formatul API al workflow-urilor și serverul ComfyUI (/prompt, /ws, /history) în ComfyUI & Stable Diffusion / Flux

Ecosistem & Administrare Nivel: avansat

ComfyUI rulează ca server HTTP și WebSocket: un workflow exportat în format API (noduri cu class_type și inputs) se trimite la /prompt, progresul vine pe /ws, iar rezultatele se citesc din /history și /view.

Intrări & ieșiri nod
POST http://127.0.0.1:8188/prompt
{"prompt": { "<id>": {"class_type": "...", "inputs": {...}}, ... }, "client_id": "..."}

Interfața salvează workflow-urile în două formate JSON. Formatul obișnuit (File → Save) păstrează și pozițiile, culorile și grupurile, pentru redeschiderea în editor. Formatul API (File → Export Workflow (API)) păstrează doar ce trebuie executat: un obiect în care cheile sunt ID-urile nodurilor, iar fiecare nod are class_type (numele intern al nodului, de exemplu KSampler, CheckpointLoaderSimple) și inputs. O legătură între noduri se scrie ["id_nod_sursă", index_ieșire]: ["4", 1] înseamnă a doua ieșire a nodului 4 (la Load Checkpoint, CLIP).

Când apeși Run, interfața trimite același format la POST /prompt. Serverul validează graful și îl pune în coadă; răspunsul conține prompt_id și poziția în coadă, sau error și node_errors. Pe WebSocket-ul /ws vin mesaje ca status, execution_start, executing, progress și executed; execuția s-a terminat când apare un mesaj executing cu node egal cu null pentru prompt_id-ul tău. Apoi GET /history/{prompt_id} dă ieșirile, iar GET /view?filename=...&subfolder=...&type=output descarcă fișierul.

ComfyUI execută doar nodurile care duc la o ieșire (de exemplu Save Image) și, la o rulare nouă, doar nodurile ale căror intrări s-au schimbat; un graf trimis de două ori identic se execută o singură dată. Imaginile salvate de nodurile Save conțin în metadate câmpurile prompt (formatul API) și workflow (graful complet), deci un PNG generat poate fi tras înapoi în editor. Pentru Comfy Cloud și implementările găzduite există Comfy API v2 și SDK-uri oficiale; exemplele de aici sunt pentru serverul local.

Concepte cheie și caracteristici

Concept / Parametru Descriere practică
POST /prompt Trimite un workflow în format API în coadă; acceptă prompt, opțional client_id și prompt_id.
GET /prompt, GET /queue Starea cozii și a execuției curente.
/ws?clientId=... WebSocket pentru progres, nodul care rulează, erori și starea cozii.
GET /history/{prompt_id} Ieșirile unei execuții (de exemplu numele fișierelor salvate).
GET /view Descarcă o imagine după filename, subfolder și type (output, temp, input).
POST /upload/image Încarcă o imagine în folderul input, pentru nodul Load Image.
GET /object_info[/{node_class}] Descrierea tuturor nodurilor (sau a unuia): intrări, tipuri, valori implicite.
POST /interrupt, POST /free Oprește execuția curentă / eliberează memoria descărcând modelele.
--disable-metadata Argument de pornire: nodurile Save nu mai scriu prompt și workflow în fișiere.

Exemple practice și scenarii reale

Exemplul 1: Trimiterea unui workflow din Python

După basic_api_example.py din repo-ul ComfyUI (folderul script_examples); fișierul JSON este exportat cu File → Export Workflow (API).

import json
from urllib import request

with open("workflow_api.json", encoding="utf-8") as f:
    prompt = json.load(f)

prompt["3"]["inputs"]["seed"] = 5                     # nodul 3 = KSampler
prompt["6"]["inputs"]["text"] = "masterpiece best quality man"

data = json.dumps({"prompt": prompt}).encode("utf-8")
request.urlopen(request.Request("http://127.0.0.1:8188/prompt", data=data))

Exemplul 2: Fragment de workflow în format API

Din exemplul documentației: KSampler primește modelul de la nodul 4 (ieșirea 0) și latentul de la nodul 5.

"3": {
  "class_type": "KSampler",
  "inputs": {"seed": 156680208700286, "steps": 20, "cfg": 8, "sampler_name": "euler",
             "scheduler": "normal", "denoise": 1,
             "model": ["4", 0], "positive": ["6", 0], "negative": ["7", 0],
             "latent_image": ["5", 0]}
},
"4": {"class_type": "CheckpointLoaderSimple",
      "inputs": {"ckpt_name": "v1-5-pruned-emaonly-fp16.safetensors"}}

Exemplul 3: Așteptarea rezultatului pe WebSocket

Schema din websockets_api_example.py (pachetul websocket-client).

import json, uuid, websocket

client_id = str(uuid.uuid4())
ws = websocket.WebSocket()
ws.connect(f"ws://127.0.0.1:8188/ws?clientId={client_id}")
# ... POST /prompt cu {"prompt": prompt, "client_id": client_id, "prompt_id": prompt_id}
while True:
    msg = ws.recv()
    if isinstance(msg, str):
        m = json.loads(msg)
        if m["type"] == "executing" and m["data"]["node"] is None \
                and m["data"]["prompt_id"] == prompt_id:
            break          # gata; urmează GET /history/{prompt_id}

Greșeli frecvente și capcane

  • Trimiți la /prompt fișierul salvat cu File → Save. Serverul așteaptă formatul API; convertește-l deschizând fișierul în interfață și exportându-l cu Export Workflow (API).
  • Folosești titlurile afișate în loc de class_type. Titlul („Load Checkpoint”) se poate schimba; numele intern (CheckpointLoaderSimple) este cel pe care îl caută serverul. GET /object_info arată numele și intrările exacte.
  • Aștepți o imagine nouă după ce trimiți de două ori același workflow cu seed fix. ComfyUI refolosește rezultatele din cache pentru nodurile neschimbate; schimbă seed-ul sau o altă intrare.

Noțiuni și module înrudite