"""comfyui_txt2img_oneshot — prompt de texto -> PNG en disco en una sola llamada. Promocion de la secuencia repetida (issue 0087): construir el workflow txt2img -> encolar -> esperar -> descargar la imagen. Compone funciones del registry del grupo `comfyui`: comfyui_build_txt2img_workflow_py_ml (workflow de nodos en API format) comfyui_submit_workflow_py_ml (POST /prompt) comfyui_wait_result_py_ml (poll /history) comfyui_fetch_output_image_py_ml (GET /view -> disco) Pipeline impuro: red (HTTP) + escritura en disco. """ from __future__ import annotations import os import sys # Importa las funciones del registry (mismo arbol python/functions). _FUNCTIONS_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) if _FUNCTIONS_ROOT not in sys.path: sys.path.insert(0, _FUNCTIONS_ROOT) from ml.comfyui_build_txt2img_workflow import comfyui_build_txt2img_workflow from ml.comfyui_fetch_output_image import comfyui_fetch_output_image from ml.comfyui_submit_workflow import comfyui_submit_workflow from ml.comfyui_wait_result import comfyui_wait_result def comfyui_txt2img_oneshot( prompt: str, *, ckpt: str = "dreamshaper_8.safetensors", negative: str = "", server: str = "127.0.0.1:8188", dest: str | None = None, wait_timeout: float = 300.0, **gen, ) -> dict: """Genera una imagen desde un prompt de texto, end-to-end. Args: prompt: prompt positivo (lo que se quiere ver en la imagen). ckpt: checkpoint Stable Diffusion tal como lo ve el servidor (CheckpointLoaderSimple). Por defecto "dreamshaper_8.safetensors". keyword-only. negative: prompt negativo. Por defecto "". keyword-only. server: host:port del servidor ComfyUI (sin esquema). keyword-only. dest: directorio local donde guardar el PNG (None = cwd). keyword-only. wait_timeout: segundos maximos esperando a que el server termine. keyword-only. **gen: parametros de generacion pasados a comfyui_build_txt2img_workflow (steps, cfg, width, height, seed, sampler_name, scheduler, filename_prefix). Returns: dict {ok, image_path, prompt_id, error}. image_path = ruta local del PNG descargado; prompt_id = id del trabajo en ComfyUI. Si falla, ok=False y error explica en que paso. """ # 1. Construir el workflow (funcion pura del registry). workflow = comfyui_build_txt2img_workflow(ckpt, prompt, negative, **gen) # 2. Encolar. try: sub = comfyui_submit_workflow(workflow, server=server) prompt_id = sub["prompt_id"] except (RuntimeError, KeyError) as exc: return {"ok": False, "image_path": "", "prompt_id": "", "error": f"submit fallo: {exc}"} # 3. Esperar a que termine. try: outputs = comfyui_wait_result(prompt_id, server=server, timeout=wait_timeout) except (TimeoutError, RuntimeError) as exc: return {"ok": False, "image_path": "", "prompt_id": prompt_id, "error": f"wait fallo: {exc}"} # 4. Localizar el primer PNG en los outputs (nodo SaveImage -> images). img = None for node_out in outputs.values(): images = node_out.get("images") if isinstance(node_out, dict) else None if images: img = images[0] break if img is None: return {"ok": False, "image_path": "", "prompt_id": prompt_id, "error": f"el workflow no produjo imagenes (outputs={list(outputs)})"} # 5. Descargar la imagen a disco. fetched = comfyui_fetch_output_image( img["filename"], subfolder=img.get("subfolder", ""), type_=img.get("type", "output"), server=server, dest_dir=dest or ".", ) if not fetched.get("ok"): return {"ok": False, "image_path": "", "prompt_id": prompt_id, "error": f"fetch de imagen fallo: {fetched.get('error')}"} return {"ok": True, "image_path": fetched["path"], "prompt_id": prompt_id, "error": ""} if __name__ == "__main__": import json res = comfyui_txt2img_oneshot( "a red apple on a wooden table, sharp focus", negative="blurry, low quality", dest="/tmp/comfy_txt2img", steps=20, seed=42, ) print(json.dumps(res, indent=2))