Files
fn_registry/python/functions/ml/comfyui_batch_generate.py
T
egutierrez cda36408d0 feat(ml): modelos con prefijo de categoría (IMG_/VIDEO_/3D_) + refs actualizadas
Renombra los 13 checkpoints/diffusion models de ComfyUI prefijando la
categoría al inicio del nombre, para que en el dropdown de carga el usuario
distinga de inmediato imagen/vídeo/3D y no cargue un modelo en el nodo
equivocado. Misma operación que se hizo con los LoRAs (report 0197) pero
sobre los modelos.

Clasificación:
- IMG_: dreamshaper_8, juggernaut_xl_v11, v1-5-pruned-emaonly-fp16,
  flux1-dev-fp8-e4m3fn, flux1-schnell-fp8-e4m3fn
- VIDEO_: svd, ltx-video-2b-v0.9.5, wan2.1_t2v_1.3B_fp16
- 3D_: stable_zero123, sv3d_p, hunyuan3d-dit-v2-mini, hunyuan3d-dit-v2-mv,
  hy3dgen/hunyuan3d-dit-v2-0-fp16 (mantiene subcarpeta)

A diferencia de los LoRAs aquí solo se PREFIJA la categoría conservando el
nombre completo (versión/arquitectura). Archivos físicos renombrados en
~/ComfyUI/models/checkpoints, /mnt/2tb/comfyui_models/{checkpoints,
diffusion_models} y la subcarpeta hy3dgen/. Mapa de reversión en
~/ComfyUI/models/checkpoints/_ckpt_rename_map.json.

Actualiza todas las refs (ckpt_name/unet_name + defaults + prosa) en los
builders gamedev/vídeo/3D, style presets, pipelines, tests y los workflows
de ComfyUI. Arregla de paso el default roto de comfyui_text_to_3d_oneshot
(apuntaba a v1-5-pruned-emaonly.safetensors inexistente; ahora al real
IMG_v1-5-pruned-emaonly-fp16.safetensors).

No tocados (justificado): repo-paths de HuggingFace en comfyui_install_3d_model
(<repo>/model.fp16.safetensors son rutas de descarga, no nombres de dropdown)
y el mock de stable-diffusion.cpp en test_genconfig_to_sdcpp_args.

Verificado: dropdowns CheckpointLoaderSimple + UNETLoader listan los nombres
con prefijo; 1 generación real con IMG_juggernaut_xl_v11 (node_errors vacío,
pixelart_00003_.png); 327 tests comfyui verdes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 18:24:52 +02:00

92 lines
3.5 KiB
Python

"""Encola N variantes de un workflow ComfyUI, una por seed, y recoge los prompt_ids.
Funcion impura: hace red (POST /prompt por variante, via comfyui_submit_workflow).
Compone comfyui_submit_workflow.
Para cada seed de la lista, copia el workflow (deepcopy, no muta el original),
parchea el campo de semilla de los nodos sampler (KSampler.seed, KSamplerAdvanced.
noise_seed, SamplerCustom.noise_seed — en general cualquier input "seed"/"noise_seed")
y lo encola. Util para barridos de re-roll: misma escena, varias semillas, una sola
llamada. Devuelve los prompt_ids en el mismo orden que la lista de seeds; cada uno
se sigue con comfyui_wait_result.
"""
import copy
import os
import sys
_THIS_DIR = os.path.dirname(os.path.abspath(__file__))
if _THIS_DIR not in sys.path:
sys.path.insert(0, _THIS_DIR)
from comfyui_submit_workflow import comfyui_submit_workflow # noqa: E402
# Campos de semilla conocidos en los nodos sampler de ComfyUI.
_SEED_KEYS = ("seed", "noise_seed")
def _patch_seed(workflow: dict, seed: int) -> dict:
"""Copia el workflow y fija `seed` en todos los inputs de semilla (no muta el original)."""
wf = copy.deepcopy(workflow)
for node in wf.values():
inputs = node.get("inputs")
if not isinstance(inputs, dict):
continue
for key in _SEED_KEYS:
if key in inputs:
inputs[key] = seed
return wf
def comfyui_batch_generate(
workflow: dict,
*,
seeds: list | None = None,
server: str = "127.0.0.1:8188",
) -> dict:
"""Encola una variante del workflow por cada seed y devuelve los prompt_ids.
Args:
workflow: dict en API format (resultado de un builder). No se muta: cada
variante es una copia profunda con la semilla parcheada.
seeds: lista de semillas (int). Cada una produce una variante encolada. Si
es None o vacia, se encola el workflow tal cual una sola vez (sin
parchear semilla). keyword-only.
server: host:port del servidor ComfyUI sin esquema. keyword-only.
Returns:
dict con:
- ok (bool): True si TODAS las variantes se encolaron sin error.
- prompt_ids (list[str]): prompt_id de cada variante encolada, en orden.
- count (int): numero de variantes encoladas con exito.
- error (str): primer error encontrado; cadena vacia si todo OK. Si una
variante falla, se detiene el barrido y se devuelven los prompt_ids ya
encolados.
"""
out = {"ok": False, "prompt_ids": [], "count": 0, "error": ""}
variants = [(s, _patch_seed(workflow, s)) for s in seeds] if seeds else [(None, workflow)]
for seed, wf in variants:
try:
resp = comfyui_submit_workflow(wf, server=server)
except RuntimeError as exc:
label = "tal cual" if seed is None else f"seed={seed}"
out["error"] = f"variante {label} fallo al encolar: {exc}"
return out
out["prompt_ids"].append(resp["prompt_id"])
out["count"] = len(out["prompt_ids"])
out["ok"] = True
return out
if __name__ == "__main__":
from comfyui_build_txt2img_workflow import comfyui_build_txt2img_workflow
wf = comfyui_build_txt2img_workflow(
ckpt_name="IMG_v1-5-pruned-emaonly-fp16.safetensors",
positive="a red apple on a wooden table, sharp focus",
negative="blurry, low quality",
)
res = comfyui_batch_generate(wf, seeds=[1, 2])
print(f"ok={res['ok']} count={res['count']} ids={res['prompt_ids']} error={res['error']!r}")