ComfyUI – API Call via PHP

Image

ComfyUI – API Call via PHP

In questo articolo fortemente tecnico capiremo come inviare un workflow e un prompt a ComfyUI tramite PHP. Prima di tutto abbiamo la necessità di impostare un ecosistema server in locale per far girare PHP. Darò per scontato che il mio lettore sia un programmatore PHP di livello intermedio.

Il mio ambiente preferito per lo sviluppo in PHP è Laragon. All’interno della cartella C>laragon>www>comfyui>www posizionerò i file:

Il ruolo di questi file sarà:

  • index.php
    È l’interfaccia web. Mostra le textarea per prompt positivo e negativo, invia i dati a generate.php, controlla lo stato della generazione e mostra l’immagine finale.
  • generate.php
    È il backend che prende i prompt dal form, legge workflow_api.json, sostituisce i testi nei nodi giusti, converte il workflow nel formato API di ComfyUI e lo invia a ComfyUI con POST /prompt.
  • status.php
    Controlla se la generazione è finita. Interroga history/{prompt_id}, legge le immagini prodotte e costruisce gli URL /view per mostrarle nel browser.
  • workflow_api.json
    È il workflow di ComfyUI: descrive i nodi, i collegamenti e i parametri della pipeline di generazione. In pratica è il “template” tecnico che la tua app usa per dire a ComfyUI cosa eseguire.

Avviamo ComfyUI e verifichiamo il punto di esposizione delle API che sarà in questo caso http://127.0.0.1:8000

Lasciamo l’are di lavoro, vuota, non serve caricare nessun Worflow perché quello sarà inviato a ComfyUI tramite API.

Passiamo al codice dell’applicazione.

workflow_api.json

JSON
{
  "id": "bf11bf13-0fba-4b6b-9a8d-1680bf10cb09",
  "revision": 0,
  "last_node_id": 31,
  "last_link_id": 66,
  "nodes": [
    {
      "id": 5,
      "type": "EmptyLatentImage",
      "pos": [
        424.4159660965347,
        639.6974018396478
      ],
      "size": [
        378,
        144
      ],
      "flags": {},
      "order": 0,
      "mode": 0,
      "inputs": [],
      "outputs": [
        {
          "name": "LATENT",
          "type": "LATENT",
          "slot_index": 0,
          "links": [
            55
          ]
        }
      ],
      "properties": {
        "Node name for S&R": "EmptyLatentImage"
      },
      "widgets_values": [
        512,
        512,
        1
      ]
    },
    {
      "id": 7,
      "type": "CLIPTextEncode",
      "pos": [
        292.7442742020039,
        348.82157427473624
      ],
      "size": [
        510.328125,
        216.71875
      ],
      "flags": {},
      "order": 5,
      "mode": 0,
      "inputs": [
        {
          "name": "clip",
          "type": "CLIP",
          "link": 39
        }
      ],
      "outputs": [
        {
          "name": "CONDITIONING",
          "type": "CONDITIONING",
          "slot_index": 0,
          "links": [
            56
          ]
        }
      ],
      "properties": {
        "Node name for S&R": "CLIPTextEncode"
      },
      "widgets_values": [
        ""
      ]
    },
    {
      "id": 14,
      "type": "KSamplerSelect",
      "pos": [
        544.3999868532744,
        -183.43982912695446
      ],
      "size": [
        378,
        82.65625
      ],
      "flags": {},
      "order": 1,
      "mode": 0,
      "inputs": [],
      "outputs": [
        {
          "name": "SAMPLER",
          "type": "SAMPLER",
          "links": [
            59
          ]
        }
      ],
      "properties": {
        "Node name for S&R": "KSamplerSelect"
      },
      "widgets_values": [
        "euler_ancestral_cfg_pp"
      ]
    },
    {
      "id": 22,
      "type": "SDTurboScheduler",
      "pos": [
        544.315563264503,
        -343.0669877968183
      ],
      "size": [
        378,
        112
      ],
      "flags": {},
      "order": 3,
      "mode": 0,
      "inputs": [
        {
          "name": "model",
          "type": "MODEL",
          "link": 45
        }
      ],
      "outputs": [
        {
          "name": "SIGMAS",
          "type": "SIGMAS",
          "slot_index": 0,
          "links": [
            60
          ]
        }
      ],
      "properties": {
        "Node name for S&R": "SDTurboScheduler"
      },
      "widgets_values": [
        1,
        1
      ]
    },
    {
      "id": 20,
      "type": "CheckpointLoaderSimple",
      "pos": [
        -226.47404675674875,
        -72.85890189522229
      ],
      "size": [
        412.4375,
        130.65625
      ],
      "flags": {},
      "order": 2,
      "mode": 0,
      "inputs": [],
      "outputs": [
        {
          "name": "MODEL",
          "type": "MODEL",
          "slot_index": 0,
          "links": [
            45,
            58
          ]
        },
        {
          "name": "CLIP",
          "type": "CLIP",
          "slot_index": 1,
          "links": [
            38,
            39
          ]
        },
        {
          "name": "VAE",
          "type": "VAE",
          "slot_index": 2,
          "links": [
            63
          ]
        }
      ],
      "properties": {
        "Node name for S&R": "CheckpointLoaderSimple",
        "models": [
          {
            "name": "sd_xl_turbo_1.0_fp16.safetensors",
            "url": "https://huggingface.co/stabilityai/sdxl-turbo/resolve/main/sd_xl_turbo_1.0_fp16.safetensors",
            "directory": "checkpoints"
          }
        ]
      },
      "widgets_values": [
        "sd_xl_turbo_1.0_fp16.safetensors"
      ]
    },
    {
      "id": 30,
      "type": "SaveImage",
      "pos": [
        1927.2175348197215,
        -129.1549547157644
      ],
      "size": [
        872.4375,
        1013.765625
      ],
      "flags": {},
      "order": 8,
      "mode": 0,
      "inputs": [
        {
          "name": "images",
          "type": "IMAGE",
          "link": 65
        }
      ],
      "outputs": [],
      "properties": {},
      "widgets_values": [
        "api_call_"
      ]
    },
    {
      "id": 31,
      "type": "VAEDecode",
      "pos": [
        1565.3525469788833,
        103.67263233712231
      ],
      "size": [
        252,
        72
      ],
      "flags": {},
      "order": 7,
      "mode": 0,
      "inputs": [
        {
          "name": "samples",
          "type": "LATENT",
          "link": 64
        },
        {
          "name": "vae",
          "type": "VAE",
          "link": 63
        }
      ],
      "outputs": [
        {
          "name": "IMAGE",
          "type": "IMAGE",
          "slot_index": 0,
          "links": [
            65
          ]
        }
      ],
      "properties": {
        "Node name for S&R": "VAEDecode"
      },
      "widgets_values": []
    },
    {
      "id": 29,
      "type": "SamplerCustom",
      "pos": [
        1049.759589239149,
        40.138958284003934
      ],
      "size": [
        426.234375,
        292
      ],
      "flags": {},
      "order": 6,
      "mode": 0,
      "showAdvanced": true,
      "inputs": [
        {
          "name": "model",
          "type": "MODEL",
          "link": 58
        },
        {
          "name": "positive",
          "type": "CONDITIONING",
          "link": 57
        },
        {
          "name": "negative",
          "type": "CONDITIONING",
          "link": 56
        },
        {
          "name": "sampler",
          "type": "SAMPLER",
          "link": 59
        },
        {
          "name": "sigmas",
          "type": "SIGMAS",
          "link": 60
        },
        {
          "name": "latent_image",
          "type": "LATENT",
          "link": 55
        }
      ],
      "outputs": [
        {
          "name": "output",
          "type": "LATENT",
          "slot_index": 0,
          "links": [
            64
          ]
        },
        {
          "name": "denoised_output",
          "type": "LATENT",
          "links": null
        }
      ],
      "properties": {
        "Node name for S&R": "SamplerCustom"
      },
      "widgets_values": [
        true,
        166912596748480,
        "randomize",
        1
      ]
    },
    {
      "id": 6,
      "type": "CLIPTextEncode",
      "pos": [
        297.42913173876855,
        91.39986083838619
      ],
      "size": [
        507.40625,
        197.171875
      ],
      "flags": {},
      "order": 4,
      "mode": 0,
      "inputs": [
        {
          "name": "clip",
          "type": "CLIP",
          "link": 38
        }
      ],
      "outputs": [
        {
          "name": "CONDITIONING",
          "type": "CONDITIONING",
          "slot_index": 0,
          "links": [
            57
          ]
        }
      ],
      "properties": {
        "Node name for S&R": "CLIPTextEncode"
      },
      "widgets_values": [
        ""
      ]
    }
  ],
  "links": [
    [
      38,
      20,
      1,
      6,
      0,
      "CLIP"
    ],
    [
      39,
      20,
      1,
      7,
      0,
      "CLIP"
    ],
    [
      45,
      20,
      0,
      22,
      0,
      "MODEL"
    ],
    [
      55,
      5,
      0,
      29,
      5,
      "LATENT"
    ],
    [
      56,
      7,
      0,
      29,
      2,
      "CONDITIONING"
    ],
    [
      57,
      6,
      0,
      29,
      1,
      "CONDITIONING"
    ],
    [
      58,
      20,
      0,
      29,
      0,
      "MODEL"
    ],
    [
      59,
      14,
      0,
      29,
      3,
      "SAMPLER"
    ],
    [
      60,
      22,
      0,
      29,
      4,
      "SIGMAS"
    ],
    [
      63,
      20,
      2,
      31,
      1,
      "VAE"
    ],
    [
      64,
      29,
      0,
      31,
      0,
      "LATENT"
    ],
    [
      65,
      31,
      0,
      30,
      0,
      "IMAGE"
    ]
  ],
  "groups": [],
  "config": {},
  "extra": {
    "ds": {
      "scale": 0.8429752066115733,
      "offset": [
        1193.2480222038573,
        622.6618163837708
      ]
    },
    "frontendVersion": "1.39.19",
    "workflowRendererVersion": "Vue"
  },
  "version": 0.4
}

index.php

PHP
<?php
?>
<!doctype html>
<html lang="it">
<head>
  <meta charset="utf-8">
  <title>ComfyUI Web Interface</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    body {
      font-family: Arial, sans-serif;
      max-width: 900px;
      margin: 30px auto;
      padding: 0 16px;
      background: #f7f7f7;
      color: #222;
    }
    .card {
      background: #fff;
      border-radius: 12px;
      padding: 20px;
      box-shadow: 0 2px 10px rgba(0,0,0,.08);
    }
    label {
      display: block;
      margin: 14px 0 6px;
      font-weight: bold;
    }
    textarea {
      width: 100%;
      min-height: 120px;
      padding: 12px;
      font-size: 15px;
      border: 1px solid #ccc;
      border-radius: 8px;
      resize: vertical;
      box-sizing: border-box;
    }
    button {
      margin-top: 18px;
      padding: 12px 18px;
      border: 0;
      border-radius: 8px;
      background: #2563eb;
      color: #fff;
      font-size: 15px;
      cursor: pointer;
    }
    button:disabled {
      opacity: .6;
      cursor: not-allowed;
    }
    .status {
      margin-top: 18px;
      padding: 12px;
      border-radius: 8px;
      background: #eef2ff;
      white-space: pre-wrap;
    }
    .error {
      background: #fee2e2;
      color: #991b1b;
    }
    .success {
      background: #dcfce7;
      color: #166534;
    }
    .preview {
      margin-top: 20px;
    }
    .preview img {
      max-width: 100%;
      border-radius: 10px;
      box-shadow: 0 2px 10px rgba(0,0,0,.12);
    }
  </style>
</head>
<body>
  <div class="card">
    <h1>Generatore immagini con ComfyUI</h1>

    <label for="positive">Prompt positivo</label>
    <textarea id="positive" placeholder="Descrivi quello che vuoi generare..."></textarea>

    <label for="negative">Prompt negativo</label>
    <textarea id="negative" placeholder="Cosa vuoi evitare..."></textarea>

    <button id="generateBtn">Genera immagine</button>

    <div id="status" class="status" style="display:none;"></div>
    <div id="preview" class="preview"></div>
  </div>

  <script>
    const generateBtn = document.getElementById('generateBtn');
    const positiveEl = document.getElementById('positive');
    const negativeEl = document.getElementById('negative');
    const statusEl = document.getElementById('status');
    const previewEl = document.getElementById('preview');

    function setStatus(message, type = '') {
      statusEl.style.display = 'block';
      statusEl.className = 'status ' + type;
      statusEl.textContent = message;
    }

    async function pollStatus(promptId) {
      let attempts = 0;
      const maxAttempts = 120;

      while (attempts < maxAttempts) {
        attempts++;

        const res = await fetch('status.php?prompt_id=' + encodeURIComponent(promptId));
        const data = await res.json();

        if (!res.ok) {
          throw new Error(data.error || 'Errore durante il controllo stato');
        }

        if (data.done) {
          setStatus('Generazione completata.', 'success');

          if (data.images && data.images.length > 0) {
            previewEl.innerHTML = data.images.map(img =>
              `<div><img src="${img.url}" alt="Immagine generata"></div>`
            ).join('');
          } else {
            previewEl.innerHTML = '<p>Nessuna immagine trovata nella history.</p>';
          }

          return;
        }

        setStatus('ComfyUI sta generando... tentativo ' + attempts);
        await new Promise(resolve => setTimeout(resolve, 2000));
      }

      throw new Error('Timeout: la generazione non è terminata in tempo.');
    }

    generateBtn.addEventListener('click', async () => {
      previewEl.innerHTML = '';
      generateBtn.disabled = true;

      try {
        setStatus('Invio workflow a ComfyUI...');

        const formData = new FormData();
        formData.append('positive', positiveEl.value);
        formData.append('negative', negativeEl.value);

        const res = await fetch('generate.php', {
          method: 'POST',
          body: formData
        });

        const data = await res.json();

        if (!res.ok) {
          const details = data.details
            ? (typeof data.details === 'string'
                ? data.details
                : JSON.stringify(data.details, null, 2))
            : '';

          throw new Error((data.error || 'Errore durante l\'invio a ComfyUI') + (details ? '\n\n' + details : ''));
        }
        
        if (!data.prompt_id) {
          throw new Error('ComfyUI non ha restituito prompt_id');
        }

        setStatus('Workflow inviato. Prompt ID: ' + data.prompt_id);
        await pollStatus(data.prompt_id);

      } catch (err) {
        setStatus(err.message, 'error');
      } finally {
        generateBtn.disabled = false;
      }
    });
  </script>
</body>
</html>

generate.php

PHP
<?php

header('Content-Type: application/json; charset=utf-8');

$comfyBase = 'http://127.0.0.1:8000';
$workflowPath = __DIR__ . '/workflow_api.json';

$positive = trim($_POST['positive'] ?? '');
$negative = trim($_POST['negative'] ?? '');

if ($positive === '') {
    http_response_code(400);
    echo json_encode(['error' => 'Il prompt positivo è obbligatorio.'], JSON_UNESCAPED_UNICODE);
    exit;
}

if (!file_exists($workflowPath)) {
    http_response_code(500);
    echo json_encode(['error' => 'workflow_api.json non trovato in: ' . $workflowPath], JSON_UNESCAPED_UNICODE);
    exit;
}

$raw = file_get_contents($workflowPath);
$workflow = json_decode($raw, true);

if ($workflow === null) {
    http_response_code(500);
    echo json_encode(['error' => 'workflow_api.json non è un JSON valido.'], JSON_UNESCAPED_UNICODE);
    exit;
}

if (!isset($workflow['nodes']) || !is_array($workflow['nodes'])) {
    http_response_code(500);
    echo json_encode(['error' => 'Formato workflow non valido: manca "nodes".'], JSON_UNESCAPED_UNICODE);
    exit;
}

$linksById = [];
if (!empty($workflow['links']) && is_array($workflow['links'])) {
    foreach ($workflow['links'] as $link) {
        $linksById[$link[0]] = $link;
    }
}

$prompt = [];

foreach ($workflow['nodes'] as $node) {
    $nodeId = (string)$node['id'];
    $inputs = [];

    if (!empty($node['inputs']) && is_array($node['inputs'])) {
        foreach ($node['inputs'] as $input) {
            if (isset($input['link']) && $input['link'] !== null) {
                $linkId = $input['link'];

                if (isset($linksById[$linkId])) {
                    $link = $linksById[$linkId];
                    $fromNodeId = (string)$link[1];
                    $fromSlot = (int)$link[2];
                    $inputs[$input['name']] = [$fromNodeId, $fromSlot];
                }
            }
        }
    }

    $widgets = $node['widgets_values'] ?? [];

    switch ($node['type']) {
        case 'CLIPTextEncode':
            $inputs['text'] = $widgets[0] ?? '';
            break;

        case 'EmptyLatentImage':
            $inputs['width'] = (int)($widgets[0] ?? 512);
            $inputs['height'] = (int)($widgets[1] ?? 512);
            $inputs['batch_size'] = (int)($widgets[2] ?? 1);
            break;

        case 'CheckpointLoaderSimple':
            $inputs['ckpt_name'] = $widgets[0] ?? '';
            break;

        case 'KSamplerSelect':
            $inputs['sampler_name'] = $widgets[0] ?? 'euler';
            break;

        case 'SDTurboScheduler':
            $inputs['steps'] = (int)($widgets[0] ?? 1);
            $inputs['denoise'] = (float)($widgets[1] ?? 1);
            break;

        case 'SamplerCustom':
            $inputs['add_noise'] = (bool)($widgets[0] ?? true);
            $inputs['noise_seed'] = (int)($widgets[1] ?? 0);
            $inputs['cfg'] = (float)($widgets[3] ?? 1);
            break;

        case 'SaveImage':
            $inputs['filename_prefix'] = $widgets[0] ?? 'ComfyUI';
            break;
    }

    $prompt[$nodeId] = [
        'class_type' => $node['type'],
        'inputs' => $inputs,
    ];
}

if (!isset($prompt['6'])) {
    http_response_code(500);
    echo json_encode(['error' => 'Nodo 6 non trovato nel workflow.'], JSON_UNESCAPED_UNICODE);
    exit;
}

if (!isset($prompt['7'])) {
    http_response_code(500);
    echo json_encode(['error' => 'Nodo 7 non trovato nel workflow.'], JSON_UNESCAPED_UNICODE);
    exit;
}

$prompt['6']['inputs']['text'] = $positive;
$prompt['7']['inputs']['text'] = $negative;

$payload = json_encode([
    'prompt' => $prompt,
    'client_id' => uniqid('laragon_', true)
], JSON_UNESCAPED_UNICODE);

if ($payload === false) {
    http_response_code(500);
    echo json_encode(['error' => 'Errore nella serializzazione JSON del payload.'], JSON_UNESCAPED_UNICODE);
    exit;
}

$ch = curl_init($comfyBase . '/prompt');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
    CURLOPT_POSTFIELDS => $payload,
    CURLOPT_TIMEOUT => 30,
]);

$response = curl_exec($ch);

if ($response === false) {
    http_response_code(500);
    echo json_encode([
        'error' => 'Errore cURL verso ComfyUI: ' . curl_error($ch)
    ], JSON_UNESCAPED_UNICODE);
    exit;
}

$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($response, true);

if ($httpCode >= 400) {
    http_response_code($httpCode);
    echo json_encode([
        'error' => 'ComfyUI ha restituito un errore.',
        'http_code' => $httpCode,
        'details' => $data ?: $response,
        'sent_prompt' => $prompt
    ], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
    exit;
}

echo json_encode($data ?: ['raw' => $response], JSON_UNESCAPED_UNICODE);

status.php

PHP
<?php

header('Content-Type: application/json');

$comfyBase = 'http://127.0.0.1:8000';
$promptId = $_GET['prompt_id'] ?? '';

if ($promptId === '') {
    http_response_code(400);
    echo json_encode(['error' => 'prompt_id mancante.']);
    exit;
}

$ch = curl_init($comfyBase . '/history/' . urlencode($promptId));
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 20,
]);

$response = curl_exec($ch);

if ($response === false) {
    http_response_code(500);
    echo json_encode(['error' => 'Errore cURL verso ComfyUI: ' . curl_error($ch)]);
    exit;
}

$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($response, true);

if ($httpCode >= 400 || !is_array($data)) {
    http_response_code(500);
    echo json_encode([
        'error' => 'Risposta non valida da ComfyUI.',
        'details' => $response
    ]);
    exit;
}

if (!isset($data[$promptId])) {
    echo json_encode([
        'done' => false,
        'images' => []
    ]);
    exit;
}

$entry = $data[$promptId];
$images = [];

if (isset($entry['outputs']) && is_array($entry['outputs'])) {
    foreach ($entry['outputs'] as $nodeId => $output) {
        if (isset($output['images']) && is_array($output['images'])) {
            foreach ($output['images'] as $img) {
                $filename = $img['filename'] ?? '';
                $subfolder = $img['subfolder'] ?? '';
                $type = $img['type'] ?? 'output';

                $url = $comfyBase . '/view?filename=' . urlencode($filename)
                    . '&subfolder=' . urlencode($subfolder)
                    . '&type=' . urlencode($type);

                $images[] = [
                    'filename' => $filename,
                    'subfolder' => $subfolder,
                    'type' => $type,
                    'url' => $url
                ];
            }
        }
    }
}

echo json_encode([
    'done' => !empty($images),
    'images' => $images
]);

A grandi linee funziona così:

ComfyUI non ragiona in termini di “campi del form”, ma in termini di nodi del workflow. Ogni nodo ha un ID, per esempio 6 o 7, che lo identifica dentro il JSON del workflow.

Nel tuo caso il backend fa questo passaggio:

  1. legge workflow_api.json
  2. trova i nodi con gli ID che ti interessano
  3. modifica i valori giusti dentro quei nodi
  4. invia il workflow aggiornato a ComfyUI

Come “agganciamo” il punto giusto

Nel workflow ci sono nodi diversi: caricamento modello, prompt positivo, prompt negativo, sampler, salvataggio immagine, ecc.

Per esempio:

  • nodo 6 = prompt positivo
  • nodo 7 = prompt negativo

Quindi nel PHP facciamo qualcosa del tipo:

$prompt['6']['inputs']['text'] = $positive;
$prompt['7']['inputs']['text'] = $negative;

Qui l’ID del nodo serve come “indirizzo” per dire:

  • questo testo va nel nodo del prompt positivo
  • quest’altro va nel nodo del prompt negativo

In pratica non cerchiamo “una textarea nel workflow”, ma un nodo preciso per ID.

Cosa succede dopo

Dopo aver aggiornato quei nodi, il backend manda tutto a ComfyUI con POST /prompt.

A quel punto ComfyUI:

  1. riceve il workflow completo
  2. legge i collegamenti tra i nodi
  3. esegue il grafo nell’ordine corretto
  4. passa i dati da un nodo all’altro

Per esempio, in modo semplificato:

  • il nodo checkpoint carica il modello
  • il nodo prompt positivo codifica il testo positivo
  • il nodo prompt negativo codifica il testo negativo
  • il sampler usa modello + prompt + latent
  • il nodo finale salva l’immagine

Perché servono anche i link tra nodi

Non basta sapere l’ID del nodo: serve anche sapere come i nodi sono collegati.

Il workflow contiene infatti:

  • i nodi
  • i link

I link dicono cose tipo:

  • l’output del nodo 20 entra nel nodo 6
  • l’output del nodo 6 entra nel sampler
  • l’output del sampler entra nel decoder
  • il decoder entra nel save image

Quindi gli ID servono per:

  • identificare il nodo corretto da modificare
  • ricostruire i collegamenti tra i pezzi del workflow

Flusso completo

Riassunto del ciclo:

  • index.php raccoglie il prompt
  • generate.php apre il workflow JSON
  • cerca i nodi giusti tramite ID
  • sostituisce i valori dei campi utili
  • invia il workflow a ComfyUI
  • ComfyUI esegue il grafo
  • restituisce un prompt_id
  • status.php usa quel prompt_id per chiedere a ComfyUI se il job è finito
  • quando trova l’output, recupera i file immagine

Differenza tra node id e prompt_id

Sono due cose diverse:

  • node ID: identifica un blocco dentro il workflow, per esempio il nodo del prompt positivo
  • prompt_id: identifica una specifica esecuzione del workflow inviata a ComfyUI

Quindi:

  • con il node ID decidi dove scrivere i dati nel workflow
  • con il prompt_id controlli lo stato del job dopo l’invio

Pensa al workflow come a una catena di montaggio.

  • gli ID dei nodi sono i numeri delle singole macchine
  • i link sono i nastri trasportatori tra le macchine
  • il backend cambia il contenuto di alcune macchine, per esempio il testo del prompt
  • ComfyUI avvia la linea
  • il prompt_id è il numero di pratica di quella specifica lavorazione

Se l’articolo ti è piaciuto restiamo in contatto su linkedin a: https://www.linkedin.com/in/andreatonin/

Banner

Releated Posts

ComfyUI – Reference Conditioning

In ComfyUI (soprattutto con modelli come Flux) il Reference Conditioning è un nodo che permette di usare una…

DiByAndrea Tonin Apr 9, 2026

ComfyUI per generare anime: NewBie Image Exp0.1

NewBie Image (spesso indicato come NewBie-image-Exp0.1) è un modello text-to-image in stile anime/ACG pensato per generare illustrazioni con…

DiByAndrea Tonin Apr 9, 2026

Comfy UI: OpenPose

Quando si parla di “OpenPose” in giro per ComfyUI, spesso si intende una cosa molto concreta: prendere una…

DiByAndrea Tonin Apr 9, 2026

L’AI in Cooperativa come scelta organizzativa

Negli ultimi mesi sono stato molto impegnato con docenze in cooperative anche molto diverse tra loro. In aula…

DiByAndrea Tonin Apr 9, 2026