---
name: comfyui_build_audio_workflow
kind: function
lang: py
domain: ml
version: "1.0.0"
purity: pure
signature: "def comfyui_build_audio_workflow(ckpt_name: str, prompt: str, *, lyrics: str = \"\", seconds: float = 10.0, seed: int = 0, steps: int = 50, cfg: float = 5.0, sampler_name: str = \"euler\", scheduler: str = \"simple\", shift: float = 5.0, lyrics_strength: float = 1.0, filename_prefix: str = \"audio/comfy_audio\") -> dict"
description: "Construye el dict de un workflow ComfyUI texto->audio (ACE-Step) en API format. Cadena con nodos de audio NATIVOS de ComfyUI 0.26.0: CheckpointLoaderSimple(AUDIO_ace_step_v1_3.5b.safetensors -> MODEL, CLIP, VAE) -> TextEncodeAceStepAudio(tags=prompt, lyrics) como positive + ConditioningZeroOut como negative + EmptyAceStepLatentAudio(seconds) -> ModelSamplingSD3(shift) -> KSampler -> VAEDecodeAudio -> SaveAudio(.flac). ACE-Step es abierto (Apache 2.0). Genera musica y SFX por texto; lyrics opcional para voz cantada. Pura, sin red ni I/O. Hermana de audio de comfyui_build_txt2img_workflow."
tags: [comfyui, audio, ace-step, sfx, music, ml, workflow]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: []
params:
  - name: ckpt_name
    desc: "Nombre del checkpoint ACE-Step tal como lo ve el servidor ComfyUI (ej. 'AUDIO_ace_step_v1_3.5b.safetensors', todo-en-uno: DiT + text encoder + VAE de audio). Debe estar entre los que devuelve comfyui_object_info en CheckpointLoaderSimple."
  - name: prompt
    desc: "Descripcion del sonido o estilo musical. Va al campo 'tags' de TextEncodeAceStepAudio. Ej. '8-bit coin pickup sound, retro game' o 'lofi hip hop, mellow piano, 90 bpm'."
  - name: lyrics
    desc: "Letra cantada para musica con voz. Vacio '' para SFX o musica instrumental. keyword-only."
  - name: seconds
    desc: "Duracion del audio en segundos (min 1.0). Controla el tamano del latente via EmptyAceStepLatentAudio. keyword-only."
  - name: seed
    desc: "Semilla del KSampler. 0 es determinista; cambiar para variar el resultado. keyword-only."
  - name: steps
    desc: "Pasos de sampling del KSampler. 50 recomendado para ACE-Step. keyword-only."
  - name: cfg
    desc: "Classifier-free guidance scale. 5.0 recomendado para ACE-Step. keyword-only."
  - name: sampler_name
    desc: "Algoritmo del KSampler. Por defecto 'euler'. keyword-only."
  - name: scheduler
    desc: "Scheduler del KSampler. Por defecto 'simple'. keyword-only."
  - name: shift
    desc: "Shift del ModelSamplingSD3 aplicado al MODEL antes del sampling. 5.0 recomendado para ACE-Step; mejora la coherencia temporal. keyword-only."
  - name: lyrics_strength
    desc: "Fuerza del condicionamiento de la letra (1.0 por defecto; sin efecto practico cuando lyrics esta vacio). keyword-only."
  - name: filename_prefix
    desc: "Prefijo del .flac generado por SaveAudio en output/ del servidor. keyword-only."
output: "dict en API format listo para comfyui_submit_workflow. node_ids string; cada valor con class_type + inputs. Devuelve 8 nodos: CheckpointLoaderSimple, TextEncodeAceStepAudio, ConditioningZeroOut, EmptyAceStepLatentAudio, ModelSamplingSD3, KSampler, VAEDecodeAudio y SaveAudio. El denoise del KSampler se fija a 1.0 (genera desde el latente vacio, no es audio2audio)."
tested: true
tests: ["estructura: 8 nodos ACE-Step presentes + ckpt en CheckpointLoaderSimple + prompt en TextEncodeAceStepAudio.tags", "cableado: clip [4,1], positive [6,0], negative via ConditioningZeroOut [10,0], model post ModelSamplingSD3 [11,0], vae [4,2], denoise 1.0", "params reflejados (lyrics/seconds/seed/steps/cfg/sampler_name/scheduler/shift/lyrics_strength/filename_prefix)", "edge: seconds y seed variables se reflejan en EmptyAceStepLatentAudio y KSampler", "determinismo: misma entrada -> mismo dict (builder puro)"]
test_file_path: "python/functions/ml/tests/test_comfyui_build_audio_workflow.py"
file_path: "python/functions/ml/comfyui_build_audio_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_audio_workflow import comfyui_build_audio_workflow

wf = comfyui_build_audio_workflow(
    ckpt_name="AUDIO_ace_step_v1_3.5b.safetensors",
    prompt="8-bit coin pickup sound, retro game, short",
    seconds=4.0, seed=42,
)
# wf["6"]["class_type"] == "TextEncodeAceStepAudio"
# wf["9"]["class_type"] == "SaveAudio"
# -> comfyui_submit_workflow(wf, server="127.0.0.1:8188") para encolar (necesita GPU)
# -> comfyui_wait_result(prompt_id) -> comfyui_fetch_output_audio(prompt_id, dest=...)
```

O lanzable directo con: `./fn run comfyui_build_audio_workflow` (imprime el JSON del workflow ACE-Step de ejemplo).

## Cuando usarla

Antes de enviar una generacion de audio (musica o SFX por texto) a ComfyUI:
construye aqui el dict del workflow ACE-Step y pasalo a `comfyui_submit_workflow`.
Usala cuando quieres un sonido o pieza musical descrita en lenguaje natural
(`prompt`), opcionalmente con letra cantada (`lyrics`). Baja el resultado con
`comfyui_fetch_output_audio`. 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.
- El checkpoint ACE-Step 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.
- Stable Audio Open 1.0 (la otra via nativa, mas ligera) esta GATED en HuggingFace
  (resolve da HTTP 403 sin aceptar la licencia): por eso el modelo por defecto es
  ACE-Step, que es abierto (Apache 2.0) y no gated.
- VRAM 8GB: `ace_step_v1_3.5b.safetensors` pesa ~7.7GB. Arrancar ComfyUI con
  `--lowvram` para que streamee bloques a CPU; aun asi va justo. Antes de generar
  audio, liberar VRAM de SD/Flux con POST /free {"unload_models":true,
  "free_memory":true}. Si da OOM, bajar `seconds`. El builder es puro: no toca la
  GPU, solo arma el dict (un OOM ocurre en el submit posterior, no aqui).
- ACE-Step es modelo de MUSICA: para SFX cortos funciona pero el resultado tiende
  a sonar "musical". `seconds` minimo 1.0. Para SFX muy cortos usar 2-4 s.
- SaveAudio guarda `.flac` por defecto (clave "audio" en outputs[node]). Para bajar
  el archivo usa `comfyui_fetch_output_audio` (no `comfyui_fetch_output_video`, que
  solo busca extensiones de video).
- `lyrics` vacio = instrumental/SFX. Con letra, ACE-Step canta; `lyrics_strength`
  ajusta cuanto se ciñe a ella.
