---
name: comfyui_build_img2vid_workflow
kind: function
lang: py
domain: ml
version: "1.0.0"
purity: pure
signature: "def comfyui_build_img2vid_workflow(image: str, *, ckpt: str = \"VIDEO_svd.safetensors\", width: int = 1024, height: int = 576, video_frames: int = 14, motion_bucket_id: int = 127, fps: int = 6, augmentation_level: float = 0.0, steps: int = 20, cfg: float = 2.5, min_cfg: float = 1.0, seed: int = 0, sampler_name: str = \"euler\", scheduler: str = \"karras\", filename_prefix: str = \"comfy_svd\") -> dict"
description: "Construye el dict de un workflow ComfyUI img2vid (Stable Video Diffusion) en API format a partir de una imagen estatica. Cadena: ImageOnlyCheckpointLoader(VIDEO_svd.safetensors -> MODEL, CLIP_VISION, VAE) + LoadImage -> SVD_img2vid_Conditioning(positive, negative, latent) -> VideoLinearCFGGuidance -> KSampler(denoise 1.0) -> VAEDecode -> SaveAnimatedWEBP. SVD no usa prompt de texto: el condicionamiento sale de la imagen via CLIP_VISION del checkpoint todo-en-uno. Movimiento via motion_bucket_id y fps. Pura, sin red ni I/O. Hermana de comfyui_build_video_workflow (txt2video LTX/Wan)."
tags: [comfyui, svd, img2vid, video, ml, workflow]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: []
params:
  - name: image
    desc: "Nombre del archivo de imagen base en la carpeta input/ del servidor ComfyUI (lo que carga LoadImage). Es el frame inicial del que SVD deriva el clip."
  - name: ckpt
    desc: "Nombre del checkpoint SVD tal como lo ve el servidor. Por defecto 'VIDEO_svd.safetensors' (todo-en-uno: UNet + VAE + CLIP image encoder). keyword-only."
  - name: width
    desc: "Ancho del video en px (multiplo de 8; SVD base entrena a 1024). keyword-only."
  - name: height
    desc: "Alto del video en px (multiplo de 8; SVD base entrena a 576). keyword-only."
  - name: video_frames
    desc: "Numero de frames del clip. VIDEO_svd.safetensors es el modelo de 14 frames; la variante xt llega a 25. keyword-only."
  - name: motion_bucket_id
    desc: "Intensidad de movimiento (1-255 util; 127 por defecto). Mas alto = mas movimiento. keyword-only."
  - name: fps
    desc: "Frames por segundo con que se condiciona (SVD_img2vid_Conditioning) y se guarda el clip (SaveAnimatedWEBP, alli como float). keyword-only."
  - name: augmentation_level
    desc: "Ruido anadido a la imagen base (0.0 = fiel; subirlo da mas libertad/movimiento a costa de fidelidad). keyword-only."
  - name: steps
    desc: "Pasos de sampling del KSampler. keyword-only."
  - name: cfg
    desc: "Guidance scale del ultimo frame (VideoLinearCFGGuidance interpola de min_cfg al primero hasta cfg al ultimo). SVD usa cfg baja (~2.5). keyword-only."
  - name: min_cfg
    desc: "Guidance scale del primer frame para VideoLinearCFGGuidance. keyword-only."
  - name: seed
    desc: "Semilla del sampler. 0 es determinista; cambiar para variar el clip. keyword-only."
  - name: sampler_name
    desc: "Algoritmo del KSampler. Por defecto 'euler'. keyword-only."
  - name: scheduler
    desc: "Scheduler del KSampler. Por defecto 'karras'. keyword-only."
  - name: filename_prefix
    desc: "Prefijo del archivo de salida (.webp animado de SaveAnimatedWEBP). keyword-only."
output: "dict en API format listo para comfyui_submit_workflow. node_ids string; cada valor con class_type + inputs. Devuelve 7 nodos: ImageOnlyCheckpointLoader, LoadImage, SVD_img2vid_Conditioning, VideoLinearCFGGuidance, KSampler, VAEDecode y SaveAnimatedWEBP. El denoise del KSampler se fija a 1.0 (genera desde el latente condicionado, no es img2img)."
tested: true
tests: ["estructura: 7 nodos SVD presentes + ckpt VIDEO_svd.safetensors + image en LoadImage", "cableado: clip_vision/vae [15,1]/[15,2], cond->KSampler 0/1/2, model post VideoLinearCFGGuidance, denoise 1.0", "params reflejados (width/height/video_frames/motion_bucket_id/fps/augmentation_level/steps/cfg/min_cfg/seed/filename_prefix) + fps float en SaveAnimatedWEBP", "determinismo: misma entrada -> mismo dict (builder puro)"]
test_file_path: "python/functions/ml/tests/test_comfyui_build_img2vid_workflow.py"
file_path: "python/functions/ml/comfyui_build_img2vid_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_img2vid_workflow import comfyui_build_img2vid_workflow

wf = comfyui_build_img2vid_workflow(
    "example.png",
    width=1024, height=576, video_frames=14,
    motion_bucket_id=127, fps=6, steps=20, seed=42,
)
# wf["12"]["class_type"] == "SVD_img2vid_Conditioning"
# wf["30"]["class_type"] == "SaveAnimatedWEBP"
# -> comfyui_submit_workflow(wf) para encolar el clip (necesita GPU)
```

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

## Cuando usarla

Antes de enviar una generacion de video img2vid (animar una imagen estatica) a
ComfyUI: construye aqui el dict del workflow SVD y pasalo a
`comfyui_submit_workflow`. Usala cuando partes de UNA imagen y quieres un clip
corto derivado de ella (SVD no toma prompt de texto). Para texto -> video usa la
hermana `comfyui_build_video_workflow` (LTX/Wan). Verifica el workflow contra el
servidor con `comfyui_validate_workflow` antes de encolar.

## Gotchas

- Es API format (nodos numerados), NO el formato de la UI de ComfyUI. Es lo que
  acepta POST /prompt.
- SVD NO usa prompts de texto. El condicionamiento sale de la imagen base via el
  CLIP_VISION del checkpoint todo-en-uno; por eso no hay nodos CLIPTextEncode.
- El checkpoint `VIDEO_svd.safetensors` debe existir y ser visible para el servidor
  (carpeta de checkpoints o extra_model_paths) o ComfyUI rechaza el workflow con
  HTTP 400 al enviarlo. Esta funcion es pura y no valida contra el servidor.
- La imagen `image` debe estar en la carpeta input/ del servidor (subela antes con
  el endpoint de upload o el nodo LoadImage de la UI). El validador estructural NO
  comprueba la existencia de la imagen (image no es un input de modelo).
- VRAM 8GB: SVD es pesado. Con los defaults (1024x576, 14 frames) el modelo base
  puede acercarse al techo de 8GB. Si da OOM, bajar resolucion (768x448) o
  video_frames. La generacion real (submit) es un paso posterior con GPU; este
  builder solo arma el dict y se valida de forma estructural (offline).
- `VIDEO_svd.safetensors` es el modelo de 14 frames. La variante `svd_xt` admite 25;
  con el base, video_frames > 14 puede degradar el clip.
- motion_bucket_id alto = mas movimiento (y mas artefactos). 127 es el centro
  recomendado por Stability.
- cfg se mantiene baja (~2.5) y se interpola con VideoLinearCFGGuidance (min_cfg en
  el primer frame -> cfg en el ultimo). Subir cfg degrada el video.
- SaveAnimatedWEBP declara `fps` como FLOAT en /object_info: el builder pasa
  `float(fps)` para no provocar HTTP 400. El nodo VHS_VideoCombine NO esta instalado
  en este servidor; por eso el guardado usa el SaveAnimatedWEBP nativo.
