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>
This commit is contained in:
2026-06-27 18:24:52 +02:00
parent 10dbc510b7
commit cda36408d0
166 changed files with 436 additions and 436 deletions
@@ -14,7 +14,7 @@ from ml.comfyui_build_txt2img_workflow import comfyui_build_txt2img_workflow #
def test_image_mode_builds_detailer_chain():
wf = comfyui_build_facedetailer_workflow(
"portrait_00001_.png",
ckpt_name="dreamshaper_8.safetensors",
ckpt_name="IMG_dreamshaper_8.safetensors",
positive="detailed face",
seed=42,
)
@@ -33,13 +33,13 @@ def test_image_mode_builds_detailer_chain():
def test_workflow_mode_reuses_base_nodes():
base = comfyui_build_txt2img_workflow(
ckpt_name="dreamshaper_8.safetensors",
ckpt_name="IMG_dreamshaper_8.safetensors",
positive="portrait of a woman",
seed=7,
)
wf = comfyui_build_facedetailer_workflow(
base,
ckpt_name="dreamshaper_8.safetensors",
ckpt_name="IMG_dreamshaper_8.safetensors",
positive="detailed face",
denoise=0.45,
)
@@ -56,13 +56,13 @@ def test_workflow_mode_reuses_base_nodes():
def test_normalizes_short_bbox_model():
wf = comfyui_build_facedetailer_workflow(
"x.png", ckpt_name="dreamshaper_8.safetensors", positive="face",
"x.png", ckpt_name="IMG_dreamshaper_8.safetensors", positive="face",
bbox_model="face_yolov8m.pt",
)
assert wf["fd_det"]["inputs"]["model_name"] == "bbox/face_yolov8m.pt"
# Un nombre ya prefijado se respeta.
wf2 = comfyui_build_facedetailer_workflow(
"x.png", ckpt_name="dreamshaper_8.safetensors", positive="face",
"x.png", ckpt_name="IMG_dreamshaper_8.safetensors", positive="face",
bbox_model="bbox/face_yolov8m.pt",
)
assert wf2["fd_det"]["inputs"]["model_name"] == "bbox/face_yolov8m.pt"
@@ -72,7 +72,7 @@ def test_dict_without_vaedecode_raises():
with pytest.raises(ValueError):
comfyui_build_facedetailer_workflow(
{"1": {"class_type": "LoadImage", "inputs": {"image": "x.png"}}},
ckpt_name="dreamshaper_8.safetensors",
ckpt_name="IMG_dreamshaper_8.safetensors",
positive="face",
)
@@ -80,13 +80,13 @@ def test_dict_without_vaedecode_raises():
def test_invalid_base_type_raises():
with pytest.raises(ValueError):
comfyui_build_facedetailer_workflow(
123, ckpt_name="dreamshaper_8.safetensors", positive="face",
123, ckpt_name="IMG_dreamshaper_8.safetensors", positive="face",
)
def test_determinista():
# Builder puro: misma entrada -> mismo dict (sin red, seed fijo, sin estado).
kw = dict(ckpt_name="dreamshaper_8.safetensors", positive="detailed face", seed=42)
kw = dict(ckpt_name="IMG_dreamshaper_8.safetensors", positive="detailed face", seed=42)
a = comfyui_build_facedetailer_workflow("portrait_00001_.png", **kw)
b = comfyui_build_facedetailer_workflow("portrait_00001_.png", **kw)
assert a == b
@@ -30,14 +30,14 @@ def test_loaders_separados_de_flux():
# Flux carga UNET + dos text encoders + VAE por separado (no checkpoint unico).
wf = comfyui_build_flux_workflow(
"POS",
unet="flux1-schnell-fp8-e4m3fn.safetensors",
unet="IMG_flux1-schnell-fp8-e4m3fn.safetensors",
clip_l="clip_l.safetensors",
t5xxl="t5xxl_fp8_e4m3fn_scaled.safetensors",
vae="ae.safetensors",
weight_dtype="fp8_e4m3fn",
)
unet = node_by_ct(wf, "UNETLoader")["inputs"]
assert unet["unet_name"] == "flux1-schnell-fp8-e4m3fn.safetensors"
assert unet["unet_name"] == "IMG_flux1-schnell-fp8-e4m3fn.safetensors"
assert unet["weight_dtype"] == "fp8_e4m3fn"
dual = node_by_ct(wf, "DualCLIPLoader")["inputs"]
assert dual["type"] == "flux"
@@ -10,7 +10,7 @@ from ml.comfyui_build_hires_fix_workflow import comfyui_build_hires_fix_workflow
def test_base_ksampler_and_ultimate_upscale_present():
wf = comfyui_build_hires_fix_workflow(
ckpt_name="dreamshaper_8.safetensors",
ckpt_name="IMG_dreamshaper_8.safetensors",
positive="a fox",
seed=42,
)
@@ -26,14 +26,14 @@ def test_ultimate_upscale_provee_batch_size():
# /object_info marca batch_size como input REQUERIDO de UltimateSDUpscale.
# Sin el, el submit fallaba con node_errors. Regresion guard.
wf = comfyui_build_hires_fix_workflow(
ckpt_name="dreamshaper_8.safetensors", positive="x",
ckpt_name="IMG_dreamshaper_8.safetensors", positive="x",
)
assert wf["12"]["inputs"]["batch_size"] == 1
def test_second_pass_denoise_is_partial():
wf = comfyui_build_hires_fix_workflow(
ckpt_name="dreamshaper_8.safetensors", positive="x", denoise=0.4,
ckpt_name="IMG_dreamshaper_8.safetensors", positive="x", denoise=0.4,
)
# La base re-genera entera (denoise=1.0); la 2a pasada solo anade detalle (<1).
assert wf["3"]["inputs"]["denoise"] == 1.0
@@ -43,7 +43,7 @@ def test_second_pass_denoise_is_partial():
def test_first_pass_dims_reflected():
wf = comfyui_build_hires_fix_workflow(
ckpt_name="dreamshaper_8.safetensors", positive="x", first_pass=(640, 960),
ckpt_name="IMG_dreamshaper_8.safetensors", positive="x", first_pass=(640, 960),
)
assert wf["5"]["inputs"]["width"] == 640
assert wf["5"]["inputs"]["height"] == 960
@@ -51,7 +51,7 @@ def test_first_pass_dims_reflected():
def test_upscale_model_wired():
wf = comfyui_build_hires_fix_workflow(
ckpt_name="dreamshaper_8.safetensors", positive="x",
ckpt_name="IMG_dreamshaper_8.safetensors", positive="x",
upscale_model="4x_foolhardy_Remacri.pth",
)
assert wf["11"]["class_type"] == "UpscaleModelLoader"
@@ -61,7 +61,7 @@ def test_upscale_model_wired():
def test_determinista():
# Builder puro: misma entrada -> mismo dict (sin red, seed fijo, sin estado).
kw = dict(ckpt_name="dreamshaper_8.safetensors", positive="a fox", seed=42)
kw = dict(ckpt_name="IMG_dreamshaper_8.safetensors", positive="a fox", seed=42)
a = comfyui_build_hires_fix_workflow(**kw)
b = comfyui_build_hires_fix_workflow(**kw)
assert a == b
@@ -30,12 +30,12 @@ def test_cadena_hunyuan3d_nativa():
def test_imagen_checkpoint_y_salida_glb():
wf = comfyui_build_image_to_3d_workflow(
"robot.png", ckpt_name="hunyuan3d-dit-v2-mini.safetensors", seed=42
"robot.png", ckpt_name="3D_hunyuan3d-dit-v2-mini.safetensors", seed=42
)
assert node_by_ct(wf, "LoadImage")["inputs"]["image"] == "robot.png"
assert (
node_by_ct(wf, "ImageOnlyCheckpointLoader")["inputs"]["ckpt_name"]
== "hunyuan3d-dit-v2-mini.safetensors"
== "3D_hunyuan3d-dit-v2-mini.safetensors"
)
assert node_by_ct(wf, "KSampler")["inputs"]["seed"] == 42
# SaveGLB es el nodo de salida: produce la malla .glb.
@@ -26,7 +26,7 @@ def test_estructura_y_nodos_svd():
assert ct in cts, f"falta {ct} en SVD img2vid"
# El checkpoint SVD es todo-en-uno cargado con el loader image-only.
assert node_by_ct(wf, "ImageOnlyCheckpointLoader")["inputs"]["ckpt_name"] == (
"svd.safetensors"
"VIDEO_svd.safetensors"
)
# La imagen base entra por LoadImage.
assert node_by_ct(wf, "LoadImage")["inputs"]["image"] == "example.png"
@@ -23,7 +23,7 @@ def _node_id(wf, class_type):
def test_style_mode_nodos_y_conexiones():
wf = comfyui_build_ipadapter_workflow(
"a castle, oil painting", "ref.png",
base_checkpoint="dreamshaper_8.safetensors", mode="style", weight=0.75,
base_checkpoint="IMG_dreamshaper_8.safetensors", mode="style", weight=0.75,
)
assert_api_format(wf)
cts = class_types(wf)
@@ -54,7 +54,7 @@ def test_style_mode_nodos_y_conexiones():
def test_faceid_mode_nodos_y_conexiones():
wf = comfyui_build_ipadapter_workflow(
"a knight portrait", "face.png",
base_checkpoint="dreamshaper_8.safetensors", mode="faceid",
base_checkpoint="IMG_dreamshaper_8.safetensors", mode="faceid",
weight=0.9, lora_strength=0.7,
)
assert_api_format(wf)
@@ -27,7 +27,7 @@ def _recipe_base(**over):
"slug": "portrait_cinematic_sdxl",
"version": "1.0.0",
"base_workflow": "txt2img",
"checkpoint": "juggernaut_xl_v11.safetensors",
"checkpoint": "IMG_juggernaut_xl_v11.safetensors",
"loras": [],
"params": {"steps": 30, "cfg": 5.5, "sampler_name": "dpmpp_2m",
"scheduler": "karras", "width": 832, "height": 1216},
@@ -34,7 +34,7 @@ def test_ltx_estructura_y_nodos():
assert clip["type"] == "ltxv"
assert clip["clip_name"] == "t5xxl_fp8_e4m3fn_scaled.safetensors"
assert node_by_ct(wf, "CheckpointLoaderSimple")["inputs"]["ckpt_name"] == (
"ltx-video-2b-v0.9.5.safetensors"
"VIDEO_ltx-video-2b-v0.9.5.safetensors"
)
@@ -55,7 +55,7 @@ def test_wan_estructura_y_nodos():
):
assert ct in cts, f"falta {ct} en Wan"
assert node_by_ct(wf, "UNETLoader")["inputs"]["unet_name"] == (
"wan2.1_t2v_1.3B_fp16.safetensors"
"VIDEO_wan2.1_t2v_1.3B_fp16.safetensors"
)
assert node_by_ct(wf, "CLIPLoader")["inputs"]["type"] == "wan"
assert node_by_ct(wf, "VAELoader")["inputs"]["vae_name"] == "wan_2.1_vae.safetensors"
@@ -23,7 +23,7 @@ def _seed_skill(lib, slug="demo_skill", version="1.0.0", **extra):
sdir = os.path.join(lib, slug)
os.makedirs(os.path.join(sdir, "versions"), exist_ok=True)
recipe = {"schema_version": 1, "slug": slug, "version": version,
"base_workflow": "txt2img", "checkpoint": "dreamshaper_8.safetensors",
"base_workflow": "txt2img", "checkpoint": "IMG_dreamshaper_8.safetensors",
"params": {"steps": 28, "cfg": 6.0}, **extra}
with open(os.path.join(sdir, "recipe.json"), "w", encoding="utf-8") as f:
json.dump(recipe, f)
@@ -19,7 +19,7 @@ from _comfyui_wf_assert import assert_api_format, class_types, node_by_ct
def _base():
return comfyui_build_txt2img_workflow("dreamshaper_8.safetensors", "POS", "NEG")
return comfyui_build_txt2img_workflow("IMG_dreamshaper_8.safetensors", "POS", "NEG")
def _count_ct(wf, ct):
@@ -41,7 +41,7 @@ def _recipe_txt2img(**over):
"slug": "demo_skill",
"version": "1.0.0",
"base_workflow": "txt2img",
"checkpoint": "dreamshaper_8.safetensors",
"checkpoint": "IMG_dreamshaper_8.safetensors",
"loras": [],
"params": {"steps": 28, "cfg": 6.0, "width": 512, "height": 768},
"prompt_scaffold": {