fix(ml): comfyui_wait_result no sale prematuro en jobs de video/3D
Exige outputs no vacios (no solo status terminal) para dar por completado un prompt: en jobs pesados ComfyUI marca la entry de /history como terminada antes de poblar outputs, lo que devolvia un dict vacio mientras el job seguia en GPU. Ahora sigue sondeando hasta que los outputs aparecen o hasta agotar el timeout. Timeout default 180s -> 600s (cubre video/3D) y timeout HTTP por-request acotado a 30s. Firma y contrato de retorno intactos. Tests nuevos (mock urllib CI-safe + live opcional contra /history real): golden, regresion del bug, edge imagen corta, timeout y error. v1.0.0 -> 1.1.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,37 +17,55 @@ import urllib.request
|
||||
def comfyui_wait_result(
|
||||
prompt_id: str,
|
||||
server: str = "127.0.0.1:8188",
|
||||
timeout: float = 180.0,
|
||||
timeout: float = 600.0,
|
||||
poll_interval: float = 1.0,
|
||||
) -> dict:
|
||||
"""Espera a que ComfyUI termine de ejecutar un prompt y devuelve sus outputs.
|
||||
|
||||
Sondea GET /history/{prompt_id} cada poll_interval segundos hasta que
|
||||
status.completed es True o status.status_str es "success"/"error", o hasta
|
||||
agotar el timeout.
|
||||
Sondea GET /history/{prompt_id} cada poll_interval segundos hasta que el
|
||||
prompt aparece en /history con un estado terminal de exito (status.completed
|
||||
True o status.status_str "success") **y** outputs ya poblados, o hasta que
|
||||
falla (status_str "error"), o hasta agotar el timeout.
|
||||
|
||||
El requisito de outputs no vacios es deliberado: en jobs pesados (video, 3D)
|
||||
ComfyUI puede exponer la entry de /history con el estado marcado como
|
||||
terminado antes de haber poblado los outputs. Devolver en ese instante daba
|
||||
un dict vacio mientras el job seguia en GPU (salida prematura). Por eso, si
|
||||
el estado dice "terminado" pero los outputs aun no estan, se sigue sondeando
|
||||
hasta que aparezcan o hasta agotar el timeout, que actua como red de
|
||||
seguridad.
|
||||
|
||||
Args:
|
||||
prompt_id: id devuelto por comfyui_submit_workflow.
|
||||
server: host:port del servidor ComfyUI (sin esquema).
|
||||
timeout: maximo de segundos a esperar antes de fallar.
|
||||
timeout: maximo de segundos a esperar antes de fallar. El default amplio
|
||||
(600s) cubre workflows largos de video/3D; para imagenes cortas el
|
||||
retorno sigue siendo inmediato en cuanto los outputs estan listos.
|
||||
poll_interval: segundos entre sondeos.
|
||||
|
||||
Returns:
|
||||
dict de outputs {node_id: {"images": [{filename, subfolder, type}, ...]}}
|
||||
tal como ComfyUI los expone en history[prompt_id]["outputs"]. Puede
|
||||
contener otros tipos de output (gifs, texto) segun los nodos del
|
||||
workflow.
|
||||
contener otros tipos de output (gifs, texto, video bajo "images" con
|
||||
"animated") segun los nodos del workflow. Siempre no vacio en caso de
|
||||
exito (un workflow sin nodo de guardado agotaria el timeout).
|
||||
|
||||
Raises:
|
||||
TimeoutError: si se agota el timeout sin que el prompt complete.
|
||||
TimeoutError: si se agota el timeout sin que el prompt complete con
|
||||
outputs (incluye el caso de un prompt_id que nunca aparece en
|
||||
/history porque sigue en cola, no existe, o el workflow no produce
|
||||
outputs).
|
||||
RuntimeError: si la ejecucion termina con status_str "error", si no se
|
||||
puede conectar, o si la respuesta no es JSON valido.
|
||||
"""
|
||||
url = f"http://{server}/history/{prompt_id}"
|
||||
# Timeout por-request acotado: una conexion colgada no debe bloquear todo el
|
||||
# presupuesto global (que ahora puede ser de varios minutos).
|
||||
req_timeout = min(timeout, 30.0) if timeout > 0 else 30.0
|
||||
deadline = time.time() + timeout
|
||||
while time.time() < deadline:
|
||||
try:
|
||||
with urllib.request.urlopen(url, timeout=timeout) as resp:
|
||||
with urllib.request.urlopen(url, timeout=req_timeout) as resp:
|
||||
hist = json.loads(resp.read())
|
||||
except urllib.error.URLError as exc:
|
||||
raise RuntimeError(
|
||||
@@ -67,8 +85,13 @@ def comfyui_wait_result(
|
||||
f"comfyui_wait_result: ejecucion fallo para {prompt_id}: "
|
||||
f"{json.dumps(status)[:500]}"
|
||||
)
|
||||
if status.get("completed") or status_str == "success":
|
||||
return entry.get("outputs", {})
|
||||
done = bool(status.get("completed")) or status_str == "success"
|
||||
outputs = entry.get("outputs") or {}
|
||||
# Solo se considera terminado cuando hay estado de exito Y outputs.
|
||||
# "done" con outputs vacios = entry creada pero aun sin resultados:
|
||||
# se sigue esperando (no salir prematuro).
|
||||
if done and outputs:
|
||||
return outputs
|
||||
time.sleep(poll_interval)
|
||||
|
||||
raise TimeoutError(
|
||||
|
||||
Reference in New Issue
Block a user