---
name: comfyui_build_flux_workflow
kind: function
lang: py
domain: ml
version: "1.1.0"
purity: pure
signature: "def comfyui_build_flux_workflow(prompt: str, *, variant: str = \"schnell\", width: int = 1024, height: int = 1024, steps: int | None = None, guidance: float = 3.5, seed: int = 0, unet_name: str | None = None, clip_l_name: str = \"clip_l.safetensors\", t5xxl_name: str = \"t5xxl_fp8_e4m3fn_scaled.safetensors\", vae_name: str = \"ae.safetensors\", weight_dtype: str = \"default\", sampler_name: str = \"euler\", scheduler: str = \"simple\", filename_prefix: str = \"flux\", available: dict | None = None) -> dict"
description: "Construye el dict de un workflow ComfyUI para Flux (schnell o dev) en API format (nodos numerados con class_type + inputs, conexiones como [node_id, output_index]). A diferencia de SD1.5/SDXL, Flux carga por separado UNETLoader + DualCLIPLoader (clip_l + t5xxl, type flux) + VAELoader y muestrea con el camino custom-advanced: RandomNoise + KSamplerSelect + BasicScheduler -> BasicGuider -> SamplerCustomAdvanced -> VAEDecode -> SaveImage. variant=schnell (~4 pasos, sin FluxGuidance) o dev (~20 pasos, con FluxGuidance). Validacion opcional de modelos via 'available'. Pura, sin red ni I/O. Hermana de comfyui_build_txt2img_workflow."
tags: [comfyui, flux, ml, txt2img, workflow, image-generation]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: []
params:
  - name: prompt
    desc: "Prompt positivo: lo que se quiere ver. Flux ignora el negativo, por eso no se codifica."
  - name: variant
    desc: "'schnell' (rapido, ~4 pasos, sin FluxGuidance) o 'dev' (~20 pasos, con FluxGuidance). Determina el unet y los steps por defecto. 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."
  - name: steps
    desc: "Pasos de sampling (BasicScheduler). Si None, default por variante: schnell=4, dev=20. keyword-only."
  - name: guidance
    desc: "Valor del nodo FluxGuidance. Solo se aplica en variant=dev; en schnell se ignora (la guia va fija dentro del modelo distilado). dev responde a 3.0-4.0. keyword-only."
  - name: seed
    desc: "Semilla de RandomNoise. 0 es determinista; cambiar para variar la imagen. keyword-only."
  - name: unet_name
    desc: "Nombre del modelo de difusion en UNETLoader (unet_name de /object_info). Si None, default por variante (IMG_flux1-schnell-fp8-e4m3fn.safetensors / IMG_flux1-dev-fp8-e4m3fn.safetensors). keyword-only."
  - name: clip_l_name
    desc: "Nombre del encoder CLIP-L en DualCLIPLoader (clip_name2). Por defecto 'clip_l.safetensors'. keyword-only."
  - name: t5xxl_name
    desc: "Nombre del encoder T5-XXL en DualCLIPLoader (clip_name1). Por defecto 't5xxl_fp8_e4m3fn_scaled.safetensors'. keyword-only."
  - name: vae_name
    desc: "Nombre del VAE en VAELoader (vae_name). Por defecto 'ae.safetensors', el autoencoder de Flux. keyword-only."
  - name: weight_dtype
    desc: "dtype de carga del UNET (uno de 'default', 'fp8_e4m3fn', 'fp8_e4m3fn_fast', 'fp8_e5m2'). Los modelos ya son fp8; 'default' los carga tal cual. keyword-only."
  - name: sampler_name
    desc: "Nombre del sampler para KSamplerSelect (Flux usa 'euler'). keyword-only."
  - name: scheduler
    desc: "Scheduler para BasicScheduler (Flux usa 'simple'). keyword-only."
  - name: filename_prefix
    desc: "Prefijo del PNG que SaveImage escribe en output/. keyword-only."
  - name: available
    desc: "Mapa opcional para validar que los modelos existen en el servidor, con claves opcionales 'unet', 'clip', 'vae' (cada una lista de nombres de /object_info). Si se pasa y un modelo elegido falta, lanza FileNotFoundError indicando que falta y donde colocarlo. None = sin validacion. keyword-only."
output: "dict en API format con node_ids string como claves (UNETLoader '12', DualCLIPLoader '11', VAELoader '10', EmptyLatentImage '5', CLIPTextEncode '6', FluxGuidance '21' solo en dev, RandomNoise '25', KSamplerSelect '16', BasicScheduler '17', BasicGuider '22', SamplerCustomAdvanced '13', VAEDecode '8', SaveImage '9'). Listo para comfyui_submit_workflow."
tested: true
tests: ["class_types esperados del camino custom-advanced", "schnell: sin nodo FluxGuidance, BasicGuider consume CLIPTextEncode directo", "dev: nodo FluxGuidance presente con guidance, BasicGuider lo consume", "steps default por variante (schnell=4, dev=20)", "width/height/seed reflejados en sus nodos", "available: FileNotFoundError si falta un modelo", "variant invalido -> ValueError", "determinismo: misma entrada -> mismo dict (builder puro)"]
test_file_path: "python/functions/ml/tests/test_comfyui_build_flux_workflow.py"
file_path: "python/functions/ml/comfyui_build_flux_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_flux_workflow import comfyui_build_flux_workflow
from ml.comfyui_submit_workflow import comfyui_submit_workflow
from ml.comfyui_wait_result import comfyui_wait_result
from ml.comfyui_fetch_output_image import comfyui_fetch_output_image

# Flux schnell: rapido, ~4 pasos, sin FluxGuidance.
wf = comfyui_build_flux_workflow(
    "a red apple on a wooden table, sharp focus, studio light",
    variant="schnell",
    width=1024,
    height=1024,
    seed=42,
)
# wf["12"]["class_type"] == "UNETLoader"          # modelo de difusion suelto
# wf["11"]["inputs"]["type"] == "flux"            # DualCLIPLoader en modo flux
# "21" not in wf                                  # schnell no lleva FluxGuidance
# wf["22"]["inputs"]["conditioning"] == ["6", 0]  # BasicGuider <- CLIPTextEncode

sub = comfyui_submit_workflow(wf, server="127.0.0.1:8188")
out = comfyui_wait_result(sub["prompt_id"], server="127.0.0.1:8188")
img = out["9"]["images"][0]
res = comfyui_fetch_output_image(img["filename"], subfolder=img["subfolder"],
                                 server="127.0.0.1:8188", dest_dir="/tmp")
print(res["path"])  # PNG en disco

# Flux dev: ~20 pasos, con FluxGuidance.
wf_dev = comfyui_build_flux_workflow("a misty forest at dawn", variant="dev",
                                     guidance=3.5, width=768, height=1024)
# wf_dev["21"]["class_type"] == "FluxGuidance"
# wf_dev["22"]["inputs"]["conditioning"] == ["21", 0]
```

O lanzable directo con: `./fn run comfyui_build_flux_workflow` (imprime el JSON del workflow schnell de ejemplo).

## Cuando usarla

Cuando vayas a generar txt2img con un modelo Flux (schnell o dev) y necesites el
dict del workflow para `comfyui_submit_workflow`. Usala en lugar de
`comfyui_build_txt2img_workflow` siempre que el modelo NO sea un checkpoint
todo-en-uno SD1.5/SDXL sino Flux con UNET + text encoders + VAE por separado.
Flux schnell es ideal en GPU de poca VRAM (8GB) por el fp8 y los ~4 pasos; dev
da mejor calidad a cambio de mas tiempo.

## Gotchas

- Es API format (nodos numerados), NO el formato de la UI de ComfyUI (graph con
  links). No se puede pegar en la UI tal cual; es el formato que acepta POST
  /prompt.
- Camino de muestreo custom-advanced (RandomNoise + KSamplerSelect +
  BasicScheduler -> BasicGuider -> SamplerCustomAdvanced), el patron oficial de
  Flux. NO usa KSampler ni cfg; la guia va por FluxGuidance (solo en dev).
- schnell es destilado: NO lleva FluxGuidance y practicamente ignora el prompt
  negativo. dev SI lleva FluxGuidance (nodo '21'); subir `guidance` aumenta la
  adherencia al prompt.
- Los modelos (unet/clip_l/t5xxl/vae) deben existir en el servidor. Esta funcion
  es pura y no toca disco: por defecto NO valida. Pasa `available` (las listas de
  /object_info) para que valide y lance FileNotFoundError con la carpeta destino
  si falta alguno, ANTES de enviar nada a la GPU. Sin `available`, un modelo
  ausente lo detecta `comfyui_submit_workflow` (HTTP 400 con detalle).
- `width`/`height` deben ser multiplos de 8 (EmptyLatentImage). Flux trabaja bien
  a 1024x1024; tamanos grandes suben mucho la VRAM en 8GB.
- Los `clip_name1`/`clip_name2` del DualCLIPLoader van en orden t5xxl, clip_l
  (igual que el template oficial). El modo flux carga ambos; el orden no afecta
  al resultado.

## Capability growth log

- v1.1.0 (27/06/2026) — refactor al camino custom-advanced (SamplerCustomAdvanced
  + BasicGuider), nuevo parametro `variant` (schnell/dev con steps por defecto),
  FluxGuidance solo en dev, y `available` para validar modelos faltantes con
  error claro (FileNotFoundError) sin romper la pureza.
