---
name: comfyui_build_sdxl_refiner_workflow
kind: function
lang: py
domain: ml
version: "1.0.0"
purity: pure
signature: "def comfyui_build_sdxl_refiner_workflow(base_ckpt: str, refiner_ckpt: str, positive: str, negative: str = \"\", *, base_steps: int = 20, refiner_steps: int = 5, cfg: float = 7.0, seed: int = 0, width: int = 1024, height: int = 1024) -> dict"
description: "Construye el dict de un workflow ComfyUI SDXL base+refiner en API format: dos KSamplerAdvanced encadenados que comparten el total de pasos. El base arranca el ruido y devuelve el latente con ruido sobrante (return_with_leftover_noise=enable), el refiner lo recoge (add_noise=disable) y lo termina. Pura, sin red ni I/O. Hermana de comfyui_build_txt2img_workflow."
tags: [comfyui, ml, image-generation, sdxl, refiner, stable-diffusion, workflow]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: []
params:
  - name: base_ckpt
    desc: "Nombre del checkpoint base SDXL tal como lo ve el servidor (ej. 'sd_xl_base_1.0.safetensors'). En CheckpointLoaderSimple."
  - name: refiner_ckpt
    desc: "Nombre del checkpoint refiner SDXL (ej. 'sd_xl_refiner_1.0.safetensors')."
  - name: positive
    desc: "Prompt positivo: lo que se quiere ver. Se usa para el CLIP del base y el del refiner."
  - name: negative
    desc: "Prompt negativo: lo que se quiere evitar. Por defecto cadena vacia."
  - name: base_steps
    desc: "Pasos que ejecuta el sampler base (del 0 a base_steps). keyword-only."
  - name: refiner_steps
    desc: "Pasos que ejecuta el refiner (de base_steps al total). El total es base_steps + refiner_steps. keyword-only."
  - name: cfg
    desc: "Classifier-free guidance scale (compartido por ambos samplers). keyword-only."
  - name: seed
    desc: "Semilla de ruido (compartida por ambos samplers). keyword-only."
  - name: width
    desc: "Ancho del latente/imagen en px (SDXL nativo 1024). keyword-only."
  - name: height
    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)", "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"
---

## Ejemplo

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

wf = comfyui_build_sdxl_refiner_workflow(
    base_ckpt="sd_xl_base_1.0.safetensors",
    refiner_ckpt="sd_xl_refiner_1.0.safetensors",
    positive="a majestic lion on a cliff at sunset, ultra detailed",
    negative="blurry, low quality",
    base_steps=20, refiner_steps=5,
    seed=42,
)
# wf["3"]["inputs"]["steps"] == 25            # total = base + refiner
# wf["3"]["inputs"]["end_at_step"] == 20      # base corta en base_steps
# wf["15"]["inputs"]["start_at_step"] == 20   # refiner arranca ahi
# wf["15"]["inputs"]["latent_image"] == ["3", 0]  # encadenado del base
```

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 uses el pipeline oficial SDXL de dos etapas (checkpoint base + checkpoint
refiner) para pulir el detalle final. Si solo tienes un checkpoint SDXL completo
(sin refiner separado) usa `comfyui_build_txt2img_workflow` con width/height 1024
— el refiner separado solo merece la pena con `sd_xl_refiner_*`.

## Gotchas

- Es API format (nodos numerados), NO el formato de la UI de ComfyUI.
- Los dos KSamplerAdvanced comparten `steps` = base_steps + refiner_steps. El
  base va de 0 a base_steps con `return_with_leftover_noise=enable` (no decodifica);
  el refiner va de base_steps a 10000 (= "hasta el final") con `add_noise=disable`.
- El VAE de salida es el del refiner (`["14", 2]`). Ambos checkpoints SDXL traen
  el mismo VAE, asi que el resultado no cambia; para un VAE externo cambia esa
  conexion en el nodo '8'.
- SDXL es nativo a 1024x1024: bajar mucho la resolucion degrada el resultado.
- Es pura: NO valida que los checkpoints existan en el servidor. Valida antes con
  `comfyui_validate_workflow` (necesitas ambos: base y refiner descargados).
