---
name: comfyui_stream_progress
kind: function
lang: py
domain: ml
version: "1.1.0"
purity: impure
signature: "def comfyui_stream_progress(prompt_id: str, *, server: str = \"127.0.0.1:8188\", client_id: str | None = None, timeout: float = 300.0) -> dict"
description: "Sigue en vivo el progreso de un prompt ComfyUI por WebSocket ws://<server>/ws?clientId= (eventos progress paso/total, executing por nodo, execution_success/execution_error) en vez de hacer polling. Alternativa en-vivo a comfyui_wait_result. Si websocket-client NO esta en el interprete que ejecuta (el venv del registry no lo trae; el de ComfyUI si), cae limpiamente a polling de /history reutilizando comfyui_wait_result y marca method='polling'. Devuelve {ok, completed, steps_seen, last_node, method, error}. Impura: WebSocket o HTTP."
tags: [comfyui, ml, progress, websocket, stream, http]
uses_functions: [comfyui_wait_result_py_ml]
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: []
params:
  - name: prompt_id
    desc: "id devuelto por comfyui_submit_workflow, el prompt cuyo progreso seguir."
  - name: server
    desc: "host:port del servidor ComfyUI sin esquema (default '127.0.0.1:8188')."
  - name: client_id
    desc: "clientId para registrar el socket; si None se genera un uuid4 hex."
  - name: timeout
    desc: "maximo de segundos a esperar a que el prompt complete (default 300)."
output: "dict con ok (bool), completed (bool, True si el prompt termino), steps_seen (int, mensajes 'progress' vistos por WS; 0 en fallback de polling), last_node (str, ultimo nodo en ejecucion visto), method (str, 'websocket' o 'polling'), error (str, vacio si OK)."
tested: false
tests: []
test_file_path: ""
file_path: "python/functions/ml/comfyui_stream_progress.py"
---

## Ejemplo

```python
import sys, os, uuid
sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions"))
from ml.comfyui_build_txt2img_workflow import comfyui_build_txt2img_workflow
from ml.comfyui_submit_workflow import comfyui_submit_workflow
from ml.comfyui_stream_progress import comfyui_stream_progress

cid = uuid.uuid4().hex   # MISMO clientId en submit y en stream para ver progress en vivo
wf = comfyui_build_txt2img_workflow(
    ckpt_name="IMG_dreamshaper_8.safetensors",
    positive="an ornate brass clockwork dragon", steps=25, seed=424242)
pid = comfyui_submit_workflow(wf, client_id=cid)["prompt_id"]

res = comfyui_stream_progress(pid, client_id=cid, timeout=300)
# {'ok': True, 'completed': True, 'steps_seen': 13, 'last_node': '9',
#  'method': 'websocket', 'error': ''}
```

Si NO compartes el `client_id`, el seguimiento sigue funcionando (detecta el fin por
`/history`) pero `steps_seen` sale 0: ComfyUI envia los eventos `progress` al socket
del `clientId` que encolo el prompt, no a otro.

Para WebSocket real hay que ejecutarlo con un interprete que tenga `websocket-client`
(el venv de ComfyUI lo trae). Con el venv del registry (`./fn run`) cae a polling
automaticamente y devuelve `method='polling'`.

## Cuando usarla

Cuando quieres feedback en vivo de una generacion larga (hires-fix, vídeo, 3D
multi-vista) en lugar de esperar a ciegas con `comfyui_wait_result`: ver por que
nodo va el grafo (`last_node`) y cuantos pasos de sampler han pasado
(`steps_seen`). Util para barras de progreso o para detectar un cuelgue (si
`steps_seen` no avanza). Para el caso simple "solo dime cuando esta listo",
`comfyui_wait_result` (polling) basta y es mas portable.

## Gotchas

- `websocket-client` NO esta en el venv del registry, asi que `./fn run
  comfyui_stream_progress <pid>` cae a polling (`method='polling'`, `steps_seen=0`).
  Para el WebSocket real, ejecutalo con el python de ComfyUI (que si lo trae). El
  fallback es transparente: mismo dict de retorno.
- `steps_seen` cuenta mensajes `progress` del WS, no el "step N/total" exacto del
  sampler; sirve como senal de avance, no como porcentaje preciso. En trabajos con
  nodos cacheados (que completan en <1s) puede salir 0 con `completed=True`: no hubo
  pasos que emitir, el fin se detecto por el chequeo de `/history`.
- **Carrera submit/WS (v1.1.0):** un prompt rapido o cacheado puede terminar antes de
  que el WS reciba su `execution_success`. La funcion se defiende: comprueba `/history`
  al entrar (si ya termino, retorna ya) y en cada ventana de recv sin eventos (detecta
  el fin sin esperar al timeout). Por eso NO se cuelga 300s en trabajos veloces.
- **`client_id` debe coincidir con el del submit para ver `progress`.** ComfyUI
  enruta los eventos `progress`/`executing` al socket cuyo `clientId` encolo el
  prompt (`send_sync(..., sid=client_id)`). Si llamas a esta funcion con un
  `client_id` distinto (o None, que genera uno nuevo) NO recibiras esos eventos de
  ese prompt y `steps_seen` saldra 0 — aunque `completed` se detecta igual por el
  chequeo de `/history`. Para barra de progreso real: genera un `cid`, pasalo a
  `comfyui_submit_workflow(..., client_id=cid)` Y a esta funcion.
- En fallo de conexion del WS degrada al fallback de polling en vez de lanzar; si
  tambien falla el polling, devuelve `ok=False` con el motivo en `error`.
- Los frames binarios del WS (previews de imagen en vivo) se ignoran; esta funcion
  solo sigue el progreso, no descarga la imagen (para eso, `comfyui_fetch_output_image`).

## Capability growth log

- v1.1.0 (24/06/2026) — robustez ante la carrera submit/WS: pre-check de `/history` al
  entrar + re-check de `/history` en cada ventana de recv sin eventos y al agotar el
  timeout. Evita el cuelgue hasta timeout cuando un trabajo cacheado/rapido completa
  antes de que el WS reciba `execution_success`. Smoke previo lo destapo (prompt
  cacheado completaba en ~0.7s y la v1.0.0 esperaba 180s en vano).
