# ComfyUI — Generación de imágenes por API HTTP y por la UI (CDP) Tag: `comfyui`. Grupo de funciones para controlar [ComfyUI](https://github.com/comfyanonymous/ComfyUI) (motor de Stable Diffusion basado en grafos de nodos) de dos formas complementarias: - **Por su API HTTP** (`/prompt`, `/history`, `/object_info`): construir un workflow en "API format", encolarlo, esperar el resultado. Headless, scriptable, sin navegador. - **Por su UI web vía CDP**: operar la pestaña de ComfyUI ya abierta en el navegador diario (cargar un workflow en el grafo visual, editar widgets en vivo, encolar como si pulsaras "Queue Prompt", exportar el grafo, refrescar combos). Lo que el usuario ve, el agente lo toca. Todas las funciones de UI componen la primitiva de transport [`cdp_eval_py_browser`](../../python/functions/browser/cdp_eval.md) — no reinventan CDP. Filtro MCP: `mcp__registry__fn_search query="" tag="comfyui"`. ## Dos caminos, mismo motor ``` API HTTP (dominio ml) UI web vía CDP (dominio browser) ────────────────────── ─────────────────────────────── build_txt2img_workflow (dict API format) load_workflow_ui (dict -> grafo visual) │ set_node_widget_ui (tuning en vivo) ▼ queue_prompt_ui (= botón Queue Prompt) submit_workflow (POST /prompt -> id) export_workflow_ui (grafo -> dict API format) ▼ refresh_nodes_ui (recarga combos) wait_result (poll /history -> PNG) object_info (catálogo de nodos) download_model (dominio ml) -> baja checkpoints ``` El **API format** (dict de nodos numerados que produce `build_txt2img_workflow` y consume `submit_workflow`) es el puente entre ambos mundos: `load_workflow_ui` lo carga en la UI y `export_workflow_ui` lo recupera de la UI, así que puedes mezclar libremente API y navegador. ## Funciones del grupo ### Por API HTTP — dominio `ml` | ID | Firma corta | Qué hace | |---|---|---| | [comfyui_build_txt2img_workflow_py_ml](../../python/functions/ml/comfyui_build_txt2img_workflow.md) | `build_txt2img_workflow(ckpt_name, positive, negative='', *, steps, cfg, width, height, seed, ...) -> dict` | Construye el dict del workflow txt2img básico (Checkpoint → CLIPTextEncode×2 + EmptyLatent → KSampler → VAEDecode → SaveImage) en API format. **Pura**. | | [comfyui_object_info_py_ml](../../python/functions/ml/comfyui_object_info.md) | `object_info(server='127.0.0.1:8188', node_class=None, timeout) -> dict` | Catálogo de nodos del server: inputs, tipos y enums (lista de checkpoints/samplers visibles). Para validar antes de enviar. Impura. | | [comfyui_submit_workflow_py_ml](../../python/functions/ml/comfyui_submit_workflow.md) | `submit_workflow(workflow, server, client_id, timeout) -> dict` | Encola un workflow API format vía POST /prompt; devuelve `prompt_id` + posición en cola. HTTP 400 propaga la validación por nodo. Impura. | | [comfyui_wait_result_py_ml](../../python/functions/ml/comfyui_wait_result.md) | `wait_result(prompt_id, server, timeout, poll_interval) -> dict` | Sondea GET /history/{prompt_id} hasta que termina; devuelve los outputs (PNGs con filename/subfolder/type). Impura. | | [comfyui_download_model_py_ml](../../python/functions/ml/comfyui_download_model.md) | `download_model(url, dest_subdir='checkpoints', *, comfyui_dir, filename, token, overwrite, timeout_s) -> dict` | Descarga un checkpoint/LoRA/VAE a `models//`. Soporta Civitai (token) y HuggingFace. Valida que no sea HTML de error ni `.safetensors` corrupto. Impura. | | [comfyui_interrupt_queue_py_ml](../../python/functions/ml/comfyui_interrupt_queue.md) | `interrupt_queue(server='127.0.0.1:8188') -> dict` | Corta la generación en curso (POST `/interrupt`) y lee la cola (GET `/queue`) → `{ok, interrupted, queue_running, queue_pending, error}`. Freno de mano; degrada limpio en fallo de red. Impura. | | [comfyui_batch_generate_py_ml](../../python/functions/ml/comfyui_batch_generate.md) | `batch_generate(workflow, *, seeds=None, server='127.0.0.1:8188') -> dict` | Encola N variantes (una por seed), parcheando el campo de semilla de los nodos sampler sin mutar el original → `{ok, prompt_ids, count, error}`. Re-roll en una llamada. Compone `submit_workflow`. Impura. | ### Builders, validación e import — dominio `ml` (P0, issue 0064) | ID | Firma corta | Qué hace | |---|---|---| | [comfyui_build_img2img_workflow_py_ml](../../python/functions/ml/comfyui_build_img2img_workflow.md) | `build_img2img_workflow(ckpt_name, init_image, positive, negative='', *, denoise=0.6, steps, cfg, seed, ...) -> dict` | Builder img2img (Checkpoint + LoadImage → VAEEncode → KSampler con `denoise` → VAEDecode → SaveImage). **Pura**. | | [comfyui_build_upscale_workflow_py_ml](../../python/functions/ml/comfyui_build_upscale_workflow.md) | `build_upscale_workflow(image, *, model_name='4x-UltraSharp.pth', method='model') -> dict` | Builder upscale: `method='model'` (ESRGAN: UpscaleModelLoader + ImageUpscaleWithModel) o `method='latent'` (ImageScaleBy x2 sin modelo). **Pura**. | | [comfyui_inject_lora_py_ml](../../python/functions/ml/comfyui_inject_lora.md) | `inject_lora(workflow, lora_name, *, strength_model=1.0, strength_clip=1.0, model_node=None, clip_node=None) -> dict` | Inserta un LoraLoader en un workflow ya construido, reconectando model/clip de la fuente a sus consumidores. Encadenable. **Pura** (no muta la entrada). | | [comfyui_validate_workflow_py_ml](../../python/functions/ml/comfyui_validate_workflow.md) | `validate_workflow(workflow, server='127.0.0.1:8188', timeout) -> dict` | Cruza class_type y nombres de modelo contra `/object_info`; devuelve `{valid, missing_nodes, missing_models}` ANTES de encolar. Compone `object_info`. Impura. | | [comfyui_import_workflow_json_py_ml](../../python/functions/ml/comfyui_import_workflow_json.md) | `import_workflow_json(source, *, server, timeout) -> dict` | Lee un workflow JSON de URL o path local; normaliza UI graph → API format (widgets vía `object_info`); passthrough si ya es API. Impura. | | [comfyui_import_workflow_png_py_ml](../../python/functions/ml/comfyui_import_workflow_png.md) | `import_workflow_png(png_path_or_url, *, timeout) -> dict` | Extrae el workflow embebido en los chunks `prompt` (API) / `workflow` (UI) de un PNG de ComfyUI (tEXt/zTXt/iTXt, stdlib). Path o URL. Impura. | | [comfyui_download_workflow_py_ml](../../python/functions/ml/comfyui_download_workflow.md) | `download_workflow(source, dest=None, *, server, civitai_token, hf_token, timeout) -> dict` | **Dispatcher**: descarga un workflow de CUALQUIER fuente (Google Drive, GitHub, Civitai, HuggingFace, URL directa o path local) y lo normaliza a API format. Detecta el tipo por la URL y delega; tras bajar compone `import_workflow_json`/`import_workflow_png`. Catálogo de fuentes: `reports/0080`. Impura. | | [comfyui_read_png_metadata_py_ml](../../python/functions/ml/comfyui_read_png_metadata.md) | `read_png_metadata(png_path) -> dict` | Lee los parámetros de generación (modelo, seed, steps, cfg, sampler, prompts) de un PNG generado por ComfyUI. Impura (I/O disco). | | [comfyui_fetch_output_image_py_ml](../../python/functions/ml/comfyui_fetch_output_image.md) | `fetch_output_image(filename, *, subfolder='', type_='output', server, dest_dir='.', timeout) -> dict` | Descarga el PNG generado vía GET `/view` a disco local (`wait_result` solo da metadata). Impura. | ### Potencia y assets de internet — dominio `ml` (P1, issue 0064) | ID | Firma corta | Qué hace | |---|---|---| | [comfyui_build_inpaint_workflow_py_ml](../../python/functions/ml/comfyui_build_inpaint_workflow.md) | `build_inpaint_workflow(ckpt_name, image, mask, positive, negative='', *, denoise=1.0, steps, cfg, seed, ...) -> dict` | Builder inpaint: CheckpointLoaderSimple + LoadImage + LoadImageMask → VAEEncodeForInpaint → KSampler → VAEDecode → SaveImage. Regenera solo la zona enmascarada. **Pura**. | | [comfyui_build_controlnet_workflow_py_ml](../../python/functions/ml/comfyui_build_controlnet_workflow.md) | `build_controlnet_workflow(ckpt_name, control_image, cn_name, positive, negative='', *, strength=1.0, steps, cfg, seed, width, height) -> dict` | Builder ControlNet: ControlNetLoader + ControlNetApply inyectan el mapa de control sobre el condicionamiento positivo. **Pura**. | | [comfyui_build_sdxl_refiner_workflow_py_ml](../../python/functions/ml/comfyui_build_sdxl_refiner_workflow.md) | `build_sdxl_refiner_workflow(base_ckpt, refiner_ckpt, positive, negative='', *, base_steps=20, refiner_steps=5, cfg, seed, width=1024, height=1024) -> dict` | SDXL base+refiner: dos KSamplerAdvanced encadenados (base con `return_with_leftover_noise`, refiner termina). **Pura**. | | [comfyui_search_civitai_models_py_ml](../../python/functions/ml/comfyui_search_civitai_models.md) | `search_civitai_models(query, *, types='Checkpoint', base_model=None, sort, limit=20, token=None) -> dict` | Busca modelos/LoRAs en la API pública de Civitai → `{ok, items:[{name, type, base_model, version_id, download_url, nsfw}], count, error}`. Sin token funciona. Impura. | | [comfyui_install_custom_node_py_ml](../../python/functions/ml/comfyui_install_custom_node.md) | `install_custom_node(repo_url, *, comfyui_dir, pip_install=True, restart=False) -> dict` | git clone en `custom_nodes/` + pip/uv install de requirements en el venv de ComfyUI. NO reinicia el server (restart=False). Impura. | | [comfyui_resolve_workflow_deps_py_ml](../../python/functions/ml/comfyui_resolve_workflow_deps.md) | `resolve_workflow_deps(workflow, server='127.0.0.1:8188') -> dict` | Para un workflow ajeno: valida y traduce lo que falta en acciones (`{missing_nodes, missing_models, suggestions}`). Compone `validate_workflow`. Impura. | | [comfyui_list_installed_models_py_ml](../../python/functions/ml/comfyui_list_installed_models.md) | `list_installed_models(folder=None, comfyui_dir='~/ComfyUI') -> dict` | Lista modelos por carpeta resolviendo la ruta real de `extra_model_paths.yaml` (`/mnt/2tb/comfyui_models/`) + la nativa. Escaneo de FS, no depende del server. Impura. | ### Retoque pro y oneshot — dominio `ml` + `pipelines` (P0, lote report 0093) Builders que envuelven custom-nodes "pro" ya instalados (Impact-Pack, UltimateSDUpscale) y la promoción del flujo txt2img a una sola llamada. Los class_types se verificaron contra el `/object_info` del server vivo (FaceDetailer, UltralyticsDetectorProvider, UltimateSDUpscale). | ID | Firma corta | Qué hace | |---|---|---| | [comfyui_build_facedetailer_workflow_py_ml](../../python/functions/ml/comfyui_build_facedetailer_workflow.md) | `build_facedetailer_workflow(base_workflow_or_image, ckpt_name, positive, negative='', *, bbox_model='face_yolov8m.pt', denoise=0.5, ...) -> dict` | Builder **FaceDetailer** (Impact-Pack): detecta caras con `UltralyticsDetectorProvider` (YOLO bbox) y las regenera para recuperar detalle (el pain #1 de retratos). Acepta el nombre de una imagen en `input/` (str) o un workflow base (dict): toma la imagen del `VAEDecode` y reutiliza el `CheckpointLoaderSimple`. No usa SAM (no instalado). **Pura**. | | [comfyui_build_hires_fix_workflow_py_ml](../../python/functions/ml/comfyui_build_hires_fix_workflow.md) | `build_hires_fix_workflow(ckpt_name, positive, negative='', *, first_pass=(768,768), upscale_by=1.5, denoise=0.4, steps=20, ...) -> dict` | Builder **hires fix** de 2 pasadas: genera base (KSampler) y la amplía re-difundiéndola por tiles con `UltimateSDUpscale` + Remacri (`denoise<1` = añade detalle real). Distinto de `build_upscale_workflow` (ESRGAN puro, sin re-difusión). **Pura**. | | [comfyui_txt2img_oneshot_py_pipelines](../../python/functions/pipelines/comfyui_txt2img_oneshot.md) | `txt2img_oneshot(prompt, *, ckpt='dreamshaper_8.safetensors', negative='', server, dest=None, wait_timeout, **gen) -> dict` | **Pipeline** texto → PNG en disco en una llamada: build_txt2img + submit + wait + fetch_output_image → `{ok, image_path, prompt_id, error}`. Promoción de la secuencia (issue 0087). Impuro. | ### Vídeo (txt2video) — dominio `ml` (tag `video-generation`) ComfyUI ≥ 0.26.0 trae soporte nativo para **vídeo por difusión**. `build_video_workflow` cubre los dos modelos que caben en 8 GB: **LTX-Video 2B v0.9.5** (`model='ltx'`, checkpoint todo-en-uno + VAE temporal + scheduler propio — validado end-to-end en `reports/0084`, clip real de 65 frames, pico ~7.7 GB) y **Wan2.1 T2V 1.3B** (`model='wan'`, diffusion + umt5 + vae aparte — plantilla nativa canónica). El resultado es un `.mp4` vía `CreateVideo → SaveVideo`. | ID | Firma corta | Qué hace | |---|---|---| | [comfyui_build_video_workflow_py_ml](../../python/functions/ml/comfyui_build_video_workflow.md) | `build_video_workflow(prompt, *, model='ltx', negative='', width=512, height=320, num_frames=65, steps=20, seed=0, fps=24) -> dict` | Builder txt2video para LTX-Video 2B (`model='ltx'`, 12 nodos LTXV*) o Wan2.1 1.3B (`model='wan'`, UNETLoader+VAELoader+ModelSamplingSD3). Nombres de modelo reales, defaults conservadores 8 GB. **Pura**. | ### Imagen → 3D (Hunyuan3D-2 nativo) — dominio `ml` + `pipelines` (tag `img-to-3d`) ComfyUI ≥ 0.26.0 trae **soporte nativo de Hunyuan3D-2** (sin custom node): una imagen se reconstruye en una malla 3D GLB con un grafo de 9 nodos (`LoadImage → ImageOnlyCheckpointLoader → CLIPVisionEncode → Hunyuan3Dv2Conditioning → EmptyLatentHunyuan3Dv2 → KSampler → VAEDecodeHunyuan3D → VoxelToMeshBasic → SaveGLB`). El checkpoint es self-contained (DiT de forma + VAE 3D + encoder de imagen en un `.safetensors`). Salida **shape-only** (sin color/textura). Detalle y benchmark en `reports/0069-2026-06-23-comfyui-img-to-3d.md`. Para mejorar la cara trasera/laterales, genera vistas novel-view desde 1 imagen (`generate_views_from_image`, reports `0073`); para VER el GLB resultante interactivo dentro de un nodo de la UI, monta el visor `Load3D` (`build_view_3d_workflow`, report `0079`). | ID | Firma corta | Qué hace | |---|---|---| | [comfyui_build_image_to_3d_workflow_py_ml](../../python/functions/ml/comfyui_build_image_to_3d_workflow.md) | `build_image_to_3d_workflow(image_name, ckpt_name='hunyuan3d-dit-v2-mini.safetensors', *, resolution, steps, cfg, seed, octree_resolution, num_chunks, threshold, ..., watertight=False) -> dict` | Builder del workflow imagen→3D de 9 nodos (Hunyuan3D-2 nativo) en API format. El SaveGLB produce un `.glb`. `watertight=True` usa `VoxelToMesh` (`algorithm='surface net'`) en vez de `VoxelToMeshBasic` → malla estanca de raíz (default conserva el comportamiento histórico). **Pura**. | | [comfyui_generate_views_from_image_py_ml](../../python/functions/ml/comfyui_generate_views_from_image.md) | `generate_views_from_image(image_name, *, method='auto', server, azimuths=(90,180,270), elevation, dest_dir, validate_only=False, ...) -> dict` | Sintetiza vistas novel-view (back/left/right) desde 1 imagen con StableZero123/SV3D nativos, para alimentar el 3D multi-vista. **Honesta**: si el nodo+checkpoint no están, devuelve `ok=False` con la acción y NO encola. `validate_only=True` valida sin tocar GPU. Impura. | | [comfyui_build_view_3d_workflow_py_ml](../../python/functions/ml/comfyui_build_view_3d_workflow.md) | `build_view_3d_workflow(model_file, *, animation=False, width, height) -> dict` | Monta el visor 3D nativo `Load3D` (o `Load3DAdvanced` con `animation=True`) para VER un GLB/OBJ existente, orbitando con el ratón, sin ejecutar el grafo. `model_file` relativo a `input/3d/`. Cárgalo con `load_workflow_ui`. **Pura**. | | [comfyui_fetch_output_mesh_py_ml](../../python/functions/ml/comfyui_fetch_output_mesh.md) | `fetch_output_mesh(prompt_id, *, server, dest=None, timeout) -> dict` | Localiza la malla en `/history/{prompt_id}` (el SaveGLB la expone bajo la clave `"3d"`, no `"images"`) y la baja via GET `/view` a disco. Hermana de `fetch_output_image`. Impura. | | [comfyui_install_3d_model_py_ml](../../python/functions/ml/comfyui_install_3d_model.md) | `install_3d_model(variant='mini', *, hf_token=None, comfyui_dir) -> dict` | Instala el checkpoint Hunyuan3D-2 (mini/standard/mv) en `checkpoints/`. Cascada: ya-instalado → cache de HF → descarga. Resuelve la ruta real via `extra_model_paths.yaml`. Impura. | | [comfyui_image_to_3d_oneshot_py_pipelines](../../python/functions/pipelines/comfyui_image_to_3d_oneshot.md) | `image_to_3d_oneshot(image_path, *, server, variant='mini', dest=None, wait_timeout, **gen) -> dict` | **Pipeline** imagen en disco → malla GLB en una llamada: upload + build + submit + wait + fetch. Promoción de la secuencia (issue 0087). Impuro. | | [comfyui_text_to_3d_oneshot_py_pipelines](../../python/functions/pipelines/comfyui_text_to_3d_oneshot.md) | `text_to_3d_oneshot(prompt, *, server, ckpt_name='v1-5-pruned-emaonly.safetensors', negative='', textured=False, variant='mini', dest=None, ...) -> dict` | **Pipeline** prompt de texto → malla 3D GLB en una llamada: txt2img (SD) + fetch + upload + build 3D (nativo o `textured=True` multi-vista PBR) + submit + wait + fetch_mesh. Promoción de la secuencia texto→imagen→3D (issue 0087). Impuro. | | [comfyui_build_textured_3d_multiview_workflow_py_ml](../../python/functions/ml/comfyui_build_textured_3d_multiview_workflow.md) | `build_textured_3d_multiview_workflow(image_name, *, ckpt='hunyuan3d-dit-v2-mv.safetensors', views=6, octree=384, max_faces=50000, upscale_model='4x_foolhardy_Remacri.pth') -> dict` | Builder imagen→malla 3D **con textura PBR** vía el wrapper Hunyuan3DWrapper (kijai): 4/6 vistas + delight + sample multi-vista + upscale Remacri + bake sobre UV (19 nodos). Cobertura de atlas 32.93% (report 0082). **Pura**. En 8 GB ejecutar en 2 fases (shape→`/free`→paint). | | [comfyui_simplify_mesh_py_ml](../../python/functions/ml/comfyui_simplify_mesh.md) | `simplify_mesh(in_path, *, target_faces=80000, weld=True, out_path=None) -> dict` | **Post-proceso**: decima un GLB/OBJ/PLY denso (suelda cube-soup + quadric edge collapse de pymeshlab), conservando vertex colors o textura+UV. 964k→80k caras, 34.7→1.43 MB medido (report 0090). `weld=True` es clave: sin él la cube-soup de `VoxelToMeshBasic` no decima. Impura (trimesh+pymeshlab+scipy). | | [comfyui_make_watertight_py_ml](../../python/functions/ml/comfyui_make_watertight.md) | `make_watertight(in_path, *, method='voxel', pitch=None, out_path=None) -> dict` | **Post-proceso**: hace estanca una malla. `method='voxel'` (voxeliza+fill+marching cubes) garantiza `is_watertight=True` a costa de más caras y de descartar la apariencia; `method='repair'` (fill_holes+fix_normals) conserva detalle pero no garantiza estanqueidad. La vía de raíz es `VoxelToMesh surface net` (report 0088). Impura. | | [comfyui_mesh_cleanup_oneshot_py_pipelines](../../python/functions/pipelines/comfyui_mesh_cleanup_oneshot.md) | `mesh_cleanup_oneshot(in_path, *, target_faces=80000, watertight=True, method='repair', out_path=None) -> dict` | **Pipeline** de limpieza en una llamada: `simplify_mesh` → (si `watertight`) `make_watertight`. Capitaliza el "80k caras + estanco" del report 0088. `method='voxel'` garantiza estanqueidad; `method='repair'` conserva caras. Reporta `{in_faces, simplified_faces, final_faces, is_watertight}`. Impuro. | ### Por la UI web (CDP) — dominio `browser` | ID | Firma corta | Qué hace | |---|---|---| | [comfyui_load_workflow_ui_py_browser](../../python/functions/browser/comfyui_load_workflow_ui.md) | `load_workflow_ui(workflow, *, port=9222, server_url_substr='8188', filename, timeout_s) -> dict` | Carga un workflow API format en el grafo visual (`app.loadApiJson`). Impura (CDP + muta UI). | | [comfyui_set_node_widget_ui_py_browser](../../python/functions/browser/comfyui_set_node_widget_ui.md) | `set_node_widget_ui(node, widget_name, value, *, match='type', port, server_url_substr, timeout_s) -> dict` | Edita en vivo un widget de un nodo (texto del CLIPTextEncode, steps/seed/cfg del KSampler). Localiza por type/id/title. Impura. | | [comfyui_queue_prompt_ui_py_browser](../../python/functions/browser/comfyui_queue_prompt_ui.md) | `queue_prompt_ui(*, port, server_url_substr, timeout_s) -> dict` | Encola el grafo actual (`app.queuePrompt(0)`), = botón "Queue Prompt". Impura (dispara GPU). | | [comfyui_export_workflow_ui_py_browser](../../python/functions/browser/comfyui_export_workflow_ui.md) | `export_workflow_ui(*, port, server_url_substr, api_format=True, save_path, timeout_s) -> dict` | Exporta el grafo actual: API format (`graphToPrompt().output`) o UI graph (`graph.serialize()`); opcional a disco. Impura. | | [comfyui_refresh_nodes_ui_py_browser](../../python/functions/browser/comfyui_refresh_nodes_ui.md) | `refresh_nodes_ui(*, port, server_url_substr, timeout_s) -> dict` | Refresca los combos (checkpoints/loras/vae) sin recargar la página (`app.refreshComboInNodes`). Impura. | ## Ejemplo canónico end-to-end (build → load → tune → queue → resultado) Combina API + UI: construyes el workflow por API, lo cargas en la UI del usuario, ajustas el prompt y los pasos en vivo, encolas y esperas el PNG. Requiere el server en `127.0.0.1:8188` y la pestaña de ComfyUI abierta en un Chrome con `--remote-debugging-port=9222`. ```python import sys, os, time, glob sys.path.insert(0, os.path.join("python", "functions")) from ml.comfyui_build_txt2img_workflow import comfyui_build_txt2img_workflow from browser.comfyui_load_workflow_ui import comfyui_load_workflow_ui from browser.comfyui_set_node_widget_ui import comfyui_set_node_widget_ui from browser.comfyui_queue_prompt_ui import comfyui_queue_prompt_ui # 1. Construir (API format, función pura) con un prefijo de salida localizable. prefix = f"demo_{int(time.time())}" wf = comfyui_build_txt2img_workflow( ckpt_name="dreamshaper_8.safetensors", positive="placeholder", steps=8, seed=111, filename_prefix=prefix, ) # 2. Cargar el grafo en la UI del navegador del usuario. comfyui_load_workflow_ui(wf) # {'ok': True, 'loaded': True} # 3. Tuning en vivo: prompt (widget de texto) + pasos (widget numérico). comfyui_set_node_widget_ui("CLIPTextEncode", "text", "a green glass bottle on a marble shelf", match="type") comfyui_set_node_widget_ui("KSampler", "steps", 12, match="type") # 4. Encolar (= pulsar "Queue Prompt") y localizar el PNG nuevo en output/. comfyui_queue_prompt_ui() # {'ok': True, 'queued': True} before = set(glob.glob(os.path.expanduser("~/ComfyUI/output/*.png"))) while True: new = [p for p in set(glob.glob(os.path.expanduser("~/ComfyUI/output/*.png"))) - before if prefix in os.path.basename(p)] if new: print("PNG generado:", new[0]); break time.sleep(1.5) ``` Variante 100% headless (sin navegador): cambia los pasos 2-4 por `comfyui_submit_workflow(wf)` → `comfyui_wait_result(prompt_id)`. Misma capacidad, sin UI. ## Ejemplo canónico imagen → 3D (Hunyuan3D-2 nativo) Una imagen de un objeto → su malla GLB, en una sola llamada. Requiere el server en `127.0.0.1:8188` y el checkpoint mini instalado (lo hace `install_3d_model` la primera vez, reutilizando la cache de HF; ~60 s de GPU por reconstrucción en una RTX 3070). ```python import sys, os sys.path.insert(0, os.path.join("python", "functions")) from ml.comfyui_install_3d_model import comfyui_install_3d_model from pipelines.comfyui_image_to_3d_oneshot import comfyui_image_to_3d_oneshot # 1. Asegurar el checkpoint (instantáneo si ya está; reused_cache=True). comfyui_install_3d_model("mini") # 2. Imagen en disco -> malla GLB en /tmp/meshes. res = comfyui_image_to_3d_oneshot( os.path.expanduser("~/ComfyUI/input/3d_src_robot_00001_.png"), dest="/tmp/meshes", variant="mini", seed=42, ) print(res["mesh_path"], res["faces"]) # /tmp/meshes/3d_mesh_00001_.glb 1668040 ``` Para tunear nodo a nodo en vez del oneshot: `build_image_to_3d_workflow(image_name)` → `submit_workflow` → `wait_result` → `fetch_output_mesh(prompt_id, dest=...)`. ## Fronteras - **No es un grupo de generación genérica de imágenes**: cubre ComfyUI concretamente (su API y su frontend litegraph). Para otros backends (Automatic1111, diffusers) harían falta otras funciones. - **Los builders cubren txt2img, img2img, upscale (ESRGAN y hires-fix con re-difusión), LoRA stacks, inpaint, ControlNet, SDXL refiner, FaceDetailer, vídeo (LTX/Wan) y 3D texturizado multi-vista** (`build_txt2img_workflow`, `build_img2img_workflow`, `build_upscale_workflow`, `build_hires_fix_workflow`, `inject_lora`, `build_inpaint_workflow`, `build_controlnet_workflow`, `build_sdxl_refiner_workflow`, `build_facedetailer_workflow`, `build_video_workflow`, `build_textured_3d_multiview_workflow`). Lo que aún NO tiene builder propio (IPAdapter, multi-ControlNet avanzado) se monta en la UI a mano y se captura con `export_workflow_ui`, o se importa de internet con `import_workflow_json`/`import_workflow_png`, se resuelven sus dependencias con `resolve_workflow_deps` (instala nodos con `install_custom_node`, descubre modelos con `search_civitai_models`) y se valida con `validate_workflow` antes de encolar. - **Los 13 builders puros tienen tests de estructura** (`python/functions/ml/tests/test_comfyui_build_*.py` + `test_comfyui_inject_lora.py`): verifican los `class_type` esperados, que los parámetros se reflejan en los nodos, la validez de las conexiones `[node_id, output_index]` y la pureza de `inject_lora`. Son tests offline (no tocan GPU ni server); las funciones impuras del grupo (todo lo que habla con el server, el navegador o Civitai/HuggingFace) no se cubren con unit tests por diseño — se validan con el server vivo. - **Control de cola**: `interrupt_queue` corta la generación en curso + lee `/queue`; `batch_generate` encola N variantes por seed (re-roll). No vacían la cola entera (eso es `POST /queue {"clear": true}`). - **Las funciones `*_ui` requieren la pestaña abierta y el navegador con CDP** (puerto 9222 por defecto). Sin target que matchee `server_url_substr`, devuelven `ok=False`. Para automatización desatendida sin navegador, usa el camino API (`submit_workflow` + `wait_result`). - **`download_model` no gestiona el catálogo del server**: tras bajar un modelo, llama `refresh_nodes_ui` (o recarga la página) para que ComfyUI lo vea en los combos. - **El camino imagen→3D nativo es shape-only**: los nodos nativos de Hunyuan3D-2 (`build_image_to_3d_workflow`, `fetch_output_mesh`, `install_3d_model`, `image_to_3d_oneshot`) reconstruyen la FORMA, sin color ni textura horneada. Para **textura PBR** está `build_textured_3d_multiview_workflow`, que usa el wrapper de kijai (requiere `custom_rasterizer` CUDA + `ComfyUI_essentials` + el upscaler Remacri) y debe ejecutarse en 2 fases en 8 GB (shape→`/free`→paint). Detalle y cobertura medida en `reports/0082`; shape-only y comparación vs la app local en `reports/0069-2026-06-23-comfyui-img-to-3d.md`. - **Estanqueidad de la malla**: el default de `build_image_to_3d_workflow` (`VoxelToMeshBasic`) da malla NO estanca; con `watertight=True` (`VoxelToMesh surface-net`) sale estanca de raíz. Si ya tienes el GLB en disco, `mesh_cleanup_oneshot` decima + cierra en una llamada (`method='voxel'` garantiza `is_watertight=True`; `method='repair'` conserva caras sin garantía). Ver `reports/0088`. - La primitiva de transport CDP es [`cdp_eval`](../../python/functions/browser/cdp_eval.md) (grupo navegador): si necesitas leer/escribir algo del grafo que estas funciones no cubren, compón `cdp_eval` directamente antes de inventar nada.