feat(ml): auto-commit con 26 cambios

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-24 04:02:54 +02:00
parent ff41f4f053
commit c0b2dce3b0
26 changed files with 112 additions and 13 deletions
@@ -39,7 +39,7 @@ params:
desc: "Alto del latente/imagen en px (multiplo de 8). keyword-only."
output: "dict en API format con node_ids como claves (CheckpointLoaderSimple '4', EmptyLatentImage '5', LoadImage '10', ControlNetLoader '12', CLIPTextEncode '6'/'7', ControlNetApply '13', KSampler '3', VAEDecode '8', SaveImage '9'). Listo para comfyui_submit_workflow."
tested: true
tests: ["usa ControlNetLoader+ControlNetApply", "control_image, modelo cn y strength reflejados"]
tests: ["usa ControlNetLoader+ControlNetApply", "control_image, modelo cn y strength reflejados", "determinismo: misma entrada -> mismo dict (builder puro)"]
test_file_path: "python/functions/ml/tests/test_comfyui_build_controlnet_workflow.py"
file_path: "python/functions/ml/comfyui_build_controlnet_workflow.py"
---
@@ -47,7 +47,7 @@ params:
desc: "Prefijo del PNG final que escribe SaveImage. keyword-only."
output: "dict en API format listo para comfyui_submit_workflow. En modo dict contiene los nodos del workflow base mas los del detailer (node_ids prefijados 'fd_' para no colisionar); el SaveImage 'fd_save' produce la imagen con las caras regeneradas."
tested: true
tests: ["modo imagen monta UltralyticsDetectorProvider + FaceDetailer + SaveImage", "modo workflow reutiliza VAEDecode y CheckpointLoaderSimple del base y conserva sus nodos", "normaliza bbox_model corto a prefijo bbox/", "dict sin VAEDecode lanza ValueError"]
tests: ["modo imagen monta UltralyticsDetectorProvider + FaceDetailer + SaveImage", "modo workflow reutiliza VAEDecode y CheckpointLoaderSimple del base y conserva sus nodos", "normaliza bbox_model corto a prefijo bbox/", "dict sin VAEDecode lanza ValueError", "determinismo: misma entrada -> mismo dict (builder puro)"]
test_file_path: "python/functions/ml/tests/test_comfyui_build_facedetailer_workflow.py"
file_path: "python/functions/ml/comfyui_build_facedetailer_workflow.py"
---
@@ -47,7 +47,7 @@ params:
desc: "Prefijo del PNG final que escribe SaveImage. keyword-only."
output: "dict en API format listo para comfyui_submit_workflow. node_ids: '4' CheckpointLoaderSimple, '5' EmptyLatentImage, '6'/'7' CLIPTextEncode, '3' KSampler (base), '8' VAEDecode, '11' UpscaleModelLoader, '12' UltimateSDUpscale, '9' SaveImage."
tested: true
tests: ["cadena base (KSampler) + UltimateSDUpscale + SaveImage", "denoise de la 2a pasada <1 (re-difusion parcial)", "first_pass refleja width/height en EmptyLatentImage", "upscale_model llega a UpscaleModelLoader"]
tests: ["cadena base (KSampler) + UltimateSDUpscale + SaveImage", "denoise de la 2a pasada <1 (re-difusion parcial)", "first_pass refleja width/height en EmptyLatentImage", "upscale_model llega a UpscaleModelLoader", "determinismo: misma entrada -> mismo dict (builder puro)"]
test_file_path: "python/functions/ml/tests/test_comfyui_build_hires_fix_workflow.py"
file_path: "python/functions/ml/comfyui_build_hires_fix_workflow.py"
---
@@ -43,7 +43,7 @@ params:
desc: "Si False (default, retro-compatible) el nodo '8' es VoxelToMeshBasic (malla NO estanca). Si True usa VoxelToMesh con algorithm='surface net', que produce una malla estanca/manifold de raiz sin post-proceso. keyword-only."
output: "dict en API format con node_ids '1'..'9' como claves; cada valor tiene class_type + inputs. Listo para comfyui_submit_workflow. El nodo '9' (SaveGLB) produce el archivo .glb en el output del servidor. El nodo '8' es VoxelToMeshBasic (watertight=False) o VoxelToMesh surface-net (watertight=True)."
tested: true
tests: ["cadena de 9 nodos Hunyuan3D-2 nativos", "imagen, checkpoint, seed reflejados y SaveGLB presente", "watertight=True usa VoxelToMesh surface-net; default conserva VoxelToMeshBasic"]
tests: ["cadena de 9 nodos Hunyuan3D-2 nativos", "imagen, checkpoint, seed reflejados y SaveGLB presente", "watertight=True usa VoxelToMesh surface-net; default conserva VoxelToMeshBasic", "determinismo: misma entrada -> mismo dict (builder puro)"]
test_file_path: "python/functions/ml/tests/test_comfyui_build_image_to_3d_workflow.py"
file_path: "python/functions/ml/comfyui_build_image_to_3d_workflow.py"
---
@@ -37,7 +37,7 @@ params:
desc: "Scheduler del sampler (ej. 'normal', 'karras'). keyword-only."
output: "dict en API format con node_ids como claves (CheckpointLoaderSimple '4', LoadImage '10', VAEEncode '11', CLIPTextEncode '6'/'7', KSampler '3', VAEDecode '8', SaveImage '9'). Listo para comfyui_submit_workflow."
tested: true
tests: ["usa VAEEncode/LoadImage y no EmptyLatentImage", "denoise e init_image reflejados"]
tests: ["usa VAEEncode/LoadImage y no EmptyLatentImage", "denoise e init_image reflejados", "determinismo: misma entrada -> mismo dict (builder puro)"]
test_file_path: "python/functions/ml/tests/test_comfyui_build_img2img_workflow.py"
file_path: "python/functions/ml/comfyui_build_img2img_workflow.py"
---
@@ -39,7 +39,7 @@ params:
desc: "Scheduler del sampler (ej. 'normal', 'karras'). keyword-only."
output: "dict en API format con node_ids como claves (CheckpointLoaderSimple '4', LoadImage '10', LoadImageMask '12', VAEEncodeForInpaint '11', CLIPTextEncode '6'/'7', KSampler '3', VAEDecode '8', SaveImage '9'). Listo para comfyui_submit_workflow."
tested: true
tests: ["usa LoadImageMask+VAEEncodeForInpaint", "imagen base, mascara, seed y denoise reflejados"]
tests: ["usa LoadImageMask+VAEEncodeForInpaint", "imagen base, mascara, seed y denoise reflejados", "determinismo: misma entrada -> mismo dict (builder puro)"]
test_file_path: "python/functions/ml/tests/test_comfyui_build_inpaint_workflow.py"
file_path: "python/functions/ml/comfyui_build_inpaint_workflow.py"
---
@@ -37,7 +37,7 @@ params:
desc: "Alto del latente/imagen en px (SDXL nativo 1024). keyword-only."
output: "dict en API format con node_ids como claves (CheckpointLoaderSimple base '4' y refiner '14', EmptyLatentImage '5', CLIPTextEncode base '6'/'7' y refiner '16'/'17', KSamplerAdvanced base '3' y refiner '15', VAEDecode '8', SaveImage '9'). Listo para comfyui_submit_workflow."
tested: true
tests: ["dos KSamplerAdvanced encadenados", "base emite ruido sobrante y refiner lo recoge (start/end_at_step compartidos)"]
tests: ["dos KSamplerAdvanced encadenados", "base emite ruido sobrante y refiner lo recoge (start/end_at_step compartidos)", "determinismo: misma entrada -> mismo dict (builder puro)"]
test_file_path: "python/functions/ml/tests/test_comfyui_build_sdxl_refiner_workflow.py"
file_path: "python/functions/ml/comfyui_build_sdxl_refiner_workflow.py"
---
@@ -29,7 +29,7 @@ params:
desc: "Modelo de upscale ESRGAN en upscale_models/ para mejorar las vistas antes del bake (factor dominante de cobertura). Cadena vacia desactiva el upscale. keyword-only."
output: "dict en API format listo para comfyui_submit_workflow. node_ids '1'..'19'; los nodos de upscale ('13'..'15') solo presentes si upscale_model esta activo. El SaveGLB-equivalente Hy3DExportMesh produce un .glb texturizado en output/3D/."
tested: true
tests: ["estructura completa shape+paint+upscale (18 class_types)", "params imagen/ckpt/octree/max_faces reflejados", "6 vistas configuran 6 azimuths/elevations", "4 vistas configuran 4 azimuths", "sin upscale omite nodos Remacri y el bake toma del sample", "views invalido lanza ValueError"]
tests: ["estructura completa shape+paint+upscale (18 class_types)", "params imagen/ckpt/octree/max_faces reflejados", "6 vistas configuran 6 azimuths/elevations", "4 vistas configuran 4 azimuths", "sin upscale omite nodos Remacri y el bake toma del sample", "views invalido lanza ValueError", "determinismo: misma entrada -> mismo dict (builder puro)"]
test_file_path: "python/functions/ml/tests/test_comfyui_build_textured_3d_multiview_workflow.py"
file_path: "python/functions/ml/comfyui_build_textured_3d_multiview_workflow.py"
---
@@ -39,7 +39,7 @@ params:
desc: "Prefijo del PNG que SaveImage escribe en output/. keyword-only."
output: "dict en API format con node_ids '3'..'9' como claves; cada valor tiene class_type + inputs. Listo para comfyui_submit_workflow."
tested: true
tests: ["class_types esperados (6 nodos)", "params seed/steps/cfg/width/height reflejados", "filename_prefix en SaveImage"]
tests: ["class_types esperados (6 nodos)", "params seed/steps/cfg/width/height reflejados", "filename_prefix en SaveImage", "determinismo: misma entrada -> mismo dict (builder puro)"]
test_file_path: "python/functions/ml/tests/test_comfyui_build_txt2img_workflow.py"
file_path: "python/functions/ml/comfyui_build_txt2img_workflow.py"
---
@@ -23,7 +23,7 @@ params:
desc: "'model' (ESRGAN via UpscaleModelLoader + ImageUpscaleWithModel) o 'latent' (reescalado de pixel x2 con ImageScaleBy, sin modelo). keyword-only."
output: "dict en API format. Con method='model': LoadImage '10' + UpscaleModelLoader '12' + ImageUpscaleWithModel '13' + SaveImage '9'. Con method='latent': LoadImage '10' + ImageScaleBy '13' + SaveImage '9'. Listo para comfyui_submit_workflow."
tested: true
tests: ["method='model' usa UpscaleModelLoader+ImageUpscaleWithModel", "method='latent' usa ImageScaleBy sin modelo"]
tests: ["method='model' usa UpscaleModelLoader+ImageUpscaleWithModel", "method='latent' usa ImageScaleBy sin modelo", "determinismo: misma entrada -> mismo dict (builder puro)"]
test_file_path: "python/functions/ml/tests/test_comfyui_build_upscale_workflow.py"
file_path: "python/functions/ml/comfyui_build_upscale_workflow.py"
---
@@ -35,7 +35,7 @@ params:
desc: "Frames por segundo del video (CreateVideo). En LTX se usa tambien como frame_rate del LTXVConditioning. keyword-only."
output: "dict en API format listo para comfyui_submit_workflow. node_ids string; cada valor con class_type + inputs. LTX devuelve 12 nodos; Wan 11. La cfg/sampler/scheduler se fijan internamente segun el modelo (LTX: cfg 3.0, euler; Wan: cfg 6.0, uni_pc/simple, shift 8.0)."
tested: true
tests: ["LTX: nodos LTXV* presentes + t5xxl fp8 + ckpt real", "Wan: UNETLoader/VAELoader/ModelSamplingSD3 + umt5 + wan_2.1_vae", "params reflejados (width/height/num_frames/steps/seed/fps)", "model invalido lanza ValueError"]
tests: ["LTX: nodos LTXV* presentes + t5xxl fp8 + ckpt real", "Wan: UNETLoader/VAELoader/ModelSamplingSD3 + umt5 + wan_2.1_vae", "params reflejados (width/height/num_frames/steps/seed/fps)", "model invalido lanza ValueError", "determinismo: misma entrada -> mismo dict (builder puro)"]
test_file_path: "python/functions/ml/tests/test_comfyui_build_video_workflow.py"
file_path: "python/functions/ml/comfyui_build_video_workflow.py"
---
@@ -25,7 +25,7 @@ params:
desc: "Alto del viewport del nodo en px. keyword-only."
output: "dict en API format con un unico nodo '1'. Con animation=False: class_type 'Load3D', inputs {model_file, image, width, height}. Con animation=True: class_type 'Load3DAdvanced', inputs {model_file, viewport_state, width, height}. Cargable con comfyui_load_workflow_ui (inyecta en la UI del navegador) o POSTeable a /prompt."
tested: true
tests: ["Load3D simple con model_file/width/height", "animation=True usa Load3DAdvanced"]
tests: ["Load3D simple con model_file/width/height", "animation=True usa Load3DAdvanced", "determinismo: misma entrada -> mismo dict (builder puro)"]
test_file_path: "python/functions/ml/tests/test_comfyui_build_view_3d_workflow.py"
file_path: "python/functions/ml/comfyui_build_view_3d_workflow.py"
---
+1 -1
View File
@@ -29,7 +29,7 @@ params:
desc: "node_id cuya salida CLIP (slot 1) alimentara el LoRA. Si None, se detecta la fuente que hoy alimenta los CLIPTextEncode.clip. keyword-only."
output: "copia del workflow con un nodo LoraLoader insertado (node_id = max id numerico + 1) y reconectado entre la fuente model/clip y sus consumidores."
tested: true
tests: ["no muta el dict de entrada (pureza)", "inserta LoraLoader con strength correcto", "reconecta KSampler.model al LoRA"]
tests: ["no muta el dict de entrada (pureza)", "inserta LoraLoader con strength correcto", "reconecta KSampler.model al LoRA", "determinismo: misma entrada -> mismo dict (builder puro)"]
test_file_path: "python/functions/ml/tests/test_comfyui_inject_lora.py"
file_path: "python/functions/ml/comfyui_inject_lora.py"
---
@@ -28,3 +28,11 @@ def test_control_image_modelo_y_strength():
apply_in = node_by_ct(wf, "ControlNetApply")["inputs"]
assert apply_in["strength"] == 0.65
assert node_by_ct(wf, "KSampler")["inputs"]["seed"] == 5
def test_determinista():
# Builder puro: misma entrada -> mismo dict (sin red, seed fijo, sin estado).
args = ("ck.safetensors", "pose.png", "control_openpose.pth", "POS", "NEG")
a = comfyui_build_controlnet_workflow(*args, strength=0.65, seed=5)
b = comfyui_build_controlnet_workflow(*args, strength=0.65, seed=5)
assert a == b
@@ -82,3 +82,11 @@ def test_invalid_base_type_raises():
comfyui_build_facedetailer_workflow(
123, ckpt_name="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)
a = comfyui_build_facedetailer_workflow("portrait_00001_.png", **kw)
b = comfyui_build_facedetailer_workflow("portrait_00001_.png", **kw)
assert a == b
@@ -48,3 +48,11 @@ def test_upscale_model_wired():
assert wf["11"]["class_type"] == "UpscaleModelLoader"
assert wf["11"]["inputs"]["model_name"] == "4x_foolhardy_Remacri.pth"
assert wf["12"]["inputs"]["upscale_model"] == ["11", 0]
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)
a = comfyui_build_hires_fix_workflow(**kw)
b = comfyui_build_hires_fix_workflow(**kw)
assert a == b
@@ -56,3 +56,10 @@ def test_watertight_usa_voxeltomesh_surface_net():
assert wf["8"]["inputs"]["voxel"] == ["7", 0]
# El resto de la cadena no cambia: SaveGLB sigue colgando del nodo "8".
assert wf["9"]["inputs"]["mesh"] == ["8", 0]
def test_determinista():
# Builder puro: misma entrada -> mismo dict (sin red, seed fijo, sin estado).
a = comfyui_build_image_to_3d_workflow("obj.png", seed=42)
b = comfyui_build_image_to_3d_workflow("obj.png", seed=42)
assert a == b
@@ -28,3 +28,11 @@ def test_denoise_y_init_image():
assert ks["denoise"] == 0.45
assert ks["seed"] == 7
assert node_by_ct(wf, "LoadImage")["inputs"]["image"] == "init.png"
def test_determinista():
# Builder puro: misma entrada -> mismo dict (sin red, seed fijo, sin estado).
args = ("ck.safetensors", "init.png", "POS", "NEG")
a = comfyui_build_img2img_workflow(*args, seed=7)
b = comfyui_build_img2img_workflow(*args, seed=7)
assert a == b
@@ -29,3 +29,11 @@ def test_base_y_mascara_se_cargan():
assert mask == "m.png"
ks = node_by_ct(wf, "KSampler")["inputs"]
assert ks["seed"] == 33 and ks["denoise"] == 0.9
def test_determinista():
# Builder puro: misma entrada -> mismo dict (sin red, seed fijo, sin estado).
args = ("ck.safetensors", "img.png", "mask.png", "POS", "NEG")
a = comfyui_build_inpaint_workflow(*args, seed=33)
b = comfyui_build_inpaint_workflow(*args, seed=33)
assert a == b
@@ -31,3 +31,11 @@ def test_base_emite_ruido_sobrante_y_refiner_lo_recoge():
# Comparten el corte de pasos: el base termina donde declara base_steps.
assert base["end_at_step"] == 20
assert refiner["start_at_step"] == 20
def test_determinista():
# Builder puro: misma entrada -> mismo dict (sin red, seed fijo, sin estado).
args = ("base.st", "ref.st", "POS", "NEG")
a = comfyui_build_sdxl_refiner_workflow(*args, seed=9)
b = comfyui_build_sdxl_refiner_workflow(*args, seed=9)
assert a == b
@@ -78,3 +78,10 @@ def test_sin_upscale_omite_nodos_remacri():
def test_views_invalido_lanza_valueerror():
with pytest.raises(ValueError):
comfyui_build_textured_3d_multiview_workflow("ref.png", views=3)
def test_determinista():
# Builder puro: misma entrada -> mismo dict (sin red ni estado).
a = comfyui_build_textured_3d_multiview_workflow("ref.png", views=6)
b = comfyui_build_textured_3d_multiview_workflow("ref.png", views=6)
assert a == b
@@ -41,3 +41,11 @@ def test_params_se_reflejan_en_los_nodos():
def test_filename_prefix_en_saveimage():
wf = comfyui_build_txt2img_workflow("ck.safetensors", "POS", filename_prefix="demo_run")
assert node_by_ct(wf, "SaveImage")["inputs"]["filename_prefix"] == "demo_run"
def test_determinista():
# Builder puro: misma entrada -> mismo dict (sin red, seed fijo, sin estado).
args = ("ck.safetensors", "POS", "NEG")
a = comfyui_build_txt2img_workflow(*args, seed=123)
b = comfyui_build_txt2img_workflow(*args, seed=123)
assert a == b
@@ -26,3 +26,10 @@ def test_method_latent_no_usa_modelo():
assert "ImageScaleBy" in cts
assert "UpscaleModelLoader" not in cts
assert node_by_ct(wf, "LoadImage")["inputs"]["image"] == "img.png"
def test_determinista():
# Builder puro: misma entrada -> mismo dict (sin red ni estado).
a = comfyui_build_upscale_workflow("img.png", model_name="4x-UltraSharp.pth", method="model")
b = comfyui_build_upscale_workflow("img.png", model_name="4x-UltraSharp.pth", method="model")
assert a == b
@@ -89,3 +89,10 @@ def test_params_se_reflejan_wan():
def test_model_invalido_lanza_valueerror():
with pytest.raises(ValueError):
comfyui_build_video_workflow("x", model="sora")
def test_determinista():
# Builder puro: misma entrada -> mismo dict (sin red, seed fijo, sin estado).
a = comfyui_build_video_workflow("a fox running", model="ltx", seed=7)
b = comfyui_build_video_workflow("a fox running", model="ltx", seed=7)
assert a == b
@@ -24,3 +24,10 @@ def test_animation_usa_load3d_advanced():
wf = comfyui_build_view_3d_workflow("mesh.glb", animation=True)
assert class_types(wf) == {"Load3DAdvanced"}
assert node_by_ct(wf, "Load3DAdvanced")["inputs"]["model_file"] == "mesh.glb"
def test_determinista():
# Builder puro: misma entrada -> mismo dict (sin red ni estado).
a = comfyui_build_view_3d_workflow("mesh.glb", width=800, height=600)
b = comfyui_build_view_3d_workflow("mesh.glb", width=800, height=600)
assert a == b
@@ -43,3 +43,11 @@ def test_reconecta_el_ksampler_al_lora():
ks = next(n for n in inj.values() if n["class_type"] == "KSampler")
# El KSampler debe tomar el modelo del LoraLoader, no ya del checkpoint.
assert ks["inputs"]["model"][0] == lid
def test_determinista():
# Builder puro: misma base + mismos args -> mismo dict (deepcopy, sin estado).
base = comfyui_build_txt2img_workflow("ck.safetensors", "POS", "NEG")
a = comfyui_inject_lora(base, "style.safetensors")
b = comfyui_inject_lora(base, "style.safetensors")
assert a == b