---
name: comfyui_build_ipadapter_workflow
kind: function
lang: py
domain: ml
version: "1.0.0"
purity: pure
signature: "def comfyui_build_ipadapter_workflow(prompt: str, ref_image: str, *, base_checkpoint: str, mode: str = 'style', weight: float = 0.8, negative: str = '', preset: str | None = None, weight_type: str | None = None, start_at: float = 0.0, end_at: float = 1.0, weight_faceidv2: float = 1.0, lora_strength: float = 0.6, combine_embeds: str = 'concat', embeds_scaling: str = 'V only', provider: str = 'CPU', steps: int = 20, cfg: float = 7.0, width: int = 512, height: int = 512, seed: int = 0, sampler_name: str = 'euler', scheduler: str = 'normal', filename_prefix: str = 'ipadapter') -> dict"
description: "Construye un workflow ComfyUI txt2img + IPAdapter (custom node cubiq/IPAdapter_plus) en API format. mode='style' usa IPAdapterUnifiedLoader+IPAdapter para transferir estilo/composicion de una imagen de referencia; mode='faceid' usa IPAdapterUnifiedLoaderFaceID+IPAdapterFaceID (insightface + .bin FaceID + su LoRA) para imponer un rostro consistente. Repunta el KSampler a la salida MODEL de la rama IPAdapter. Pura: sin red ni I/O."
tags: [comfyui, comfyui-skill, ipadapter, faceid, ml, stable-diffusion, workflow]
uses_functions: [comfyui_build_txt2img_workflow_py_ml]
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: []
params:
  - name: prompt
    desc: "Prompt positivo (texto del resultado deseado)."
  - name: ref_image
    desc: "Nombre del archivo de imagen de referencia en input/ del servidor ComfyUI (lo carga LoadImage). En faceid debe contener una cara nitida; en style es la imagen de estilo."
  - name: base_checkpoint
    desc: "Checkpoint SD1.5/SDXL. Debe casar con los modelos IPAdapter (modelos SD1.5 con checkpoints SD1.5). keyword-only."
  - name: mode
    desc: "'style' (transfiere estilo/composicion) o 'faceid' (rostro consistente). keyword-only."
  - name: weight
    desc: "Peso de la influencia IPAdapter (0..1+). 0.8 buen punto de partida; sube para mas parecido, baja para mas libertad del prompt."
  - name: negative
    desc: "Prompt negativo."
  - name: preset
    desc: "Preset del UnifiedLoader. None => default por modo ('STANDARD (medium strength)' style, 'FACEID PLUS V2' faceid)."
  - name: weight_type
    desc: "Tipo de ponderacion del nodo IPAdapter/FaceID. None => default por modo ('standard' style, 'linear' faceid)."
  - name: start_at
    desc: "Fraccion del sampling donde empieza a aplicar IPAdapter (0..1)."
  - name: end_at
    desc: "Fraccion del sampling donde deja de aplicar (0..1)."
  - name: weight_faceidv2
    desc: "Peso del embedding FaceID v2 (solo mode='faceid')."
  - name: lora_strength
    desc: "Fuerza de la LoRA FaceID que carga el UnifiedLoaderFaceID (solo mode='faceid')."
  - name: combine_embeds
    desc: "Combinacion de embeddings si hay varias caras ('concat'|'add'|'subtract'|'average'|'norm average'). Solo faceid."
  - name: embeds_scaling
    desc: "Escalado de embeddings ('V only'|'K+V'|...). Solo faceid."
  - name: provider
    desc: "Backend de insightface ('CPU'|'CUDA'|...). CPU por defecto para no competir por VRAM. Solo faceid."
  - name: steps
    desc: "Pasos de sampling (pasa a la base txt2img)."
  - name: cfg
    desc: "Classifier-free guidance scale (pasa a la base)."
  - name: width
    desc: "Ancho en px, multiplo de 8 (pasa a la base)."
  - name: height
    desc: "Alto en px, multiplo de 8 (pasa a la base)."
  - name: seed
    desc: "Semilla del KSampler (pasa a la base)."
  - name: sampler_name
    desc: "Nombre del sampler (pasa a la base)."
  - name: scheduler
    desc: "Scheduler del sampler (pasa a la base)."
  - name: filename_prefix
    desc: "Prefijo del PNG generado por SaveImage."
output: "dict en API format listo para comfyui_submit_workflow: base txt2img + LoadImage + rama IPAdapter del modo elegido, con el KSampler repuntado a la salida MODEL de esa rama."
tested: true
tests: ["mode='style': nodos LoadImage/IPAdapterUnifiedLoader/IPAdapter + conexiones + KSampler repuntado + defaults", "mode='faceid': nodos UnifiedLoaderFaceID/IPAdapterFaceID + conexiones + provider CPU + defaults", "mode invalido lanza ValueError", "ref_image vacia lanza ValueError", "override de preset y weight_type", "determinismo"]
test_file_path: "python/functions/ml/tests/test_comfyui_build_ipadapter_workflow.py"
file_path: "python/functions/ml/comfyui_build_ipadapter_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_ipadapter_workflow import comfyui_build_ipadapter_workflow
from ml.comfyui_submit_workflow import comfyui_submit_workflow

# Estilo: transfiere el look de una imagen de referencia
wf = comfyui_build_ipadapter_workflow(
    "a fantasy castle on a hill", "example.png",
    base_checkpoint="IMG_dreamshaper_8.safetensors", mode="style", weight=0.8)
resp = comfyui_submit_workflow(wf)

# FaceID: rostro consistente a partir de una cara de referencia
wf = comfyui_build_ipadapter_workflow(
    "portrait of a knight in armor, cinematic", "showcase_char.png",
    base_checkpoint="IMG_dreamshaper_8.safetensors", mode="faceid", weight=0.9)
resp = comfyui_submit_workflow(wf)
print(resp["prompt_id"])
```

## Cuando usarla

Cuando quieras condicionar una generacion por una **imagen de referencia**, no solo
texto. Dos casos: `mode='style'` para clonar el estilo/composicion de una imagen
(image prompt), y `mode='faceid'` para generar un personaje con un **rostro
concreto y consistente** (el modelo extrae el embedding facial con insightface).
La referencia se sube primero a `input/` del servidor (LoadImage la lee por nombre).

## Gotchas

- **Modelos SD1.5 ↔ checkpoints SD1.5.** Los modelos descargados son SD1.5
  (`ip-adapter*_sd15`, `ip-adapter-faceid-plusv2_sd15`); usalos con un checkpoint
  SD1.5 (IMG_dreamshaper_8). Mezclar con SDXL hace fallar el UnifiedLoader.
- **La clave `ipadapter` debe estar en `extra_model_paths.yaml`.** El custom node
  registra la carpeta `models/ipadapter`; si los modelos viven en otra ruta (ej.
  `/mnt/2tb`), esa clave los mapea. Sin ella `ipadapter_file` sale vacio.
- **faceid usa insightface (`buffalo_l`) + la LoRA FaceID.** El UnifiedLoaderFaceID
  carga la LoRA `SD15_ip-adapter-faceid-plusv2_lora.safetensors` (debe estar en
  `models/loras/`). `provider='CPU'` por defecto: insightface en CPU no compite por
  los 8GB de VRAM; pon `'CUDA'` solo si tienes onnxruntime-gpu instalado.
- **La referencia debe existir en `input/`.** Es un nombre de archivo, no una ruta:
  sube la imagen antes (POST /upload/image o copiala a `~/ComfyUI/input/`).
- Pura: construye el dict, no valida que los modelos existan ni hace red. Valida con
  `comfyui_validate_workflow` y envia con `comfyui_submit_workflow`.
- En 8GB usa resolucion modesta (512x512) en SD1.5; faceid + LoRA + insightface
  caben con `--lowvram`, pero sube la VRAM si combinas con multi-LoRA pesado.
