---
name: comfyui_build_controlnet_workflow
kind: function
lang: py
domain: ml
version: "1.0.0"
purity: pure
signature: "def comfyui_build_controlnet_workflow(ckpt_name: str, control_image: str, cn_name: str, positive: str, negative: str = \"\", *, strength: float = 1.0, steps: int = 20, cfg: float = 7.0, seed: int = 0, width: int = 512, height: int = 512) -> dict"
description: "Construye el dict de un workflow ComfyUI txt2img guiado por ControlNet en API format: CheckpointLoaderSimple + EmptyLatentImage + LoadImage (mapa de control) + ControlNetLoader -> ControlNetApply (inyecta el control sobre el condicionamiento positivo) -> KSampler -> VAEDecode -> SaveImage. Pura, sin red ni I/O. Hermana de comfyui_build_txt2img_workflow."
tags: [comfyui, ml, image-generation, controlnet, stable-diffusion, workflow]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: []
params:
  - name: ckpt_name
    desc: "Nombre del checkpoint tal como lo ve el servidor ComfyUI (ej. 'dreamshaper_8.safetensors'). Debe estar en la lista de CheckpointLoaderSimple de comfyui_object_info."
  - name: control_image
    desc: "Nombre del archivo de la imagen de control dentro de input/ del servidor (mapa canny/depth/openpose preprocesado); lo carga el nodo LoadImage."
  - name: cn_name
    desc: "Nombre del modelo ControlNet en models/controlnet/ tal como lo lista comfyui_object_info para ControlNetLoader (control_net_name)."
  - name: positive
    desc: "Prompt positivo: lo que se quiere ver en la imagen."
  - name: negative
    desc: "Prompt negativo: lo que se quiere evitar. Por defecto cadena vacia."
  - name: strength
    desc: "Fuerza con la que el ControlNet condiciona la generacion (0.0 = nula, 1.0 = plena). keyword-only."
  - name: steps
    desc: "Pasos de sampling del KSampler. keyword-only."
  - name: cfg
    desc: "Classifier-free guidance scale. keyword-only."
  - name: seed
    desc: "Semilla del KSampler. 0 es determinista; cambiar para variar. keyword-only."
  - name: width
    desc: "Ancho del latente/imagen en px (multiplo de 8). keyword-only."
  - name: height
    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", "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"
---

## Ejemplo

```python
import sys, os
sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions"))
from ml.comfyui_build_controlnet_workflow import comfyui_build_controlnet_workflow

wf = comfyui_build_controlnet_workflow(
    ckpt_name="dreamshaper_8.safetensors",
    control_image="pose_canny.png",          # mapa de control en input/
    cn_name="control_v11p_sd15_canny_fp16.safetensors",   # modelo en models/controlnet/
    positive="a knight in shining armor, dramatic lighting",
    negative="blurry, low quality",
    strength=0.8,
    seed=42,
)
# wf["13"]["class_type"] == "ControlNetApply"
# wf["13"]["inputs"]["conditioning"] == ["6", 0]   # aplica sobre el positivo
# wf["3"]["inputs"]["positive"] == ["13", 0]        # KSampler usa el cond condicionado
```

El bloque se lanza con el python del venv. `./fn run` directo no aplica (firma con
`*` keyword-only); usa el import o un heredoc.

## Cuando usarla

Cuando quieras controlar la composicion de la imagen con una guia estructural
(bordes canny, profundidad depth, pose openpose, scribble) en lugar de dejar la
composicion al azar del prompt. Necesitas el mapa de control ya preprocesado en
`input/` y el modelo ControlNet adecuado descargado en `models/controlnet/`.

## Gotchas

- Es API format (nodos numerados), NO el formato de la UI de ComfyUI.
- `control_image` debe ser el mapa de control YA preprocesado (ej. salida de un
  preprocesador canny/depth). Este builder NO incluye el nodo preprocesador; si
  pasas una foto normal, el ControlNet la usara tal cual.
- Usa el nodo clasico `ControlNetApply` (un solo `strength`). Para ControlNet
  avanzado con `start_percent`/`end_percent` necesitas `ControlNetApplyAdvanced`
  (no cubierto aqui): montalo en la UI y captura con `comfyui_export_workflow_ui`.
- `cn_name` debe corresponder a la version del checkpoint (un ControlNet de SD1.5
  no sirve con un checkpoint SDXL). Valida antes con `comfyui_validate_workflow`.
- Es pura: NO valida que los modelos existan en el servidor. Valida antes.
