API Reference¶
API empresarial async — drop-in replacement de la API de Suno AI, ejecutada localmente.
Lanzar¶
# 1. Redis broker
docker run -p 6379:6379 -d redis:7
# 2. Worker GPU (1 por GPU)
celery -A src.api.worker.app worker -P solo --concurrency=1 --loglevel=info
# 3. API
uvicorn src.api.main:app --host 0.0.0.0 --port 8000 --workers 1
Endpoints¶
GET /health¶
Comprobación de vida.
GET /api/v1/voices¶
Lista VAEs registrados, LoRAs disponibles y rutas del SmartRouter.
Respuesta:{
"vae_variants": ["oobleck", "scragvae", "xcodec", "rvq_codec"],
"lora_styles": ["lana_del_rey_inspired", "trap_2024", "vallenato_old_school"],
"routes": ["fast", "stereo_mix", "ace", "dual_stem", "auto"]
}
POST /api/v1/generate¶
Encola una generación. Respuesta inmediata con task_id.
Payload:
{
"prompt": "Una bachata romántica en La menor sobre un amor de verano",
"lyrics": "[VERSE]\nLa luna brilla sobre el mar\n[CHORUS]\nQuédate conmigo esta noche",
"duration_s": 60,
"mode": "ace",
"cfg_scale": 4.0,
"dcw_mode": "double",
"n_steps": 50,
"seed": 42,
"genre": "bachata",
"mood": "romantico",
"bpm": 125,
"key": "Amin"
}
Modos:
- "auto" — router heurístico decide
- "fast" — DDIM con menos pasos (~25), latencia mínima
- "stereo_mix" — pipeline clásico (SongGenerator)
- "ace" — ACE-Step híbrido (default para >60s)
- "dual_stem" — SongGen interleaving (vocals + instrumental separados)
Respuesta:
POST /api/v1/inpaint¶
Repintado de una ventana temporal de una canción ya generada.
{
"source_song_id": "f4a8-...",
"mask_start_s": 45.0,
"mask_end_s": 60.0,
"new_prompt": "guitarra solo más expresiva",
"new_lyrics": "",
"seed": 7,
"n_steps": 50
}
GET /api/v1/status/{task_id}¶
Polling del estado.
{
"task_id": "f4a8...",
"status": "processing",
"progress": 0.65,
"meta": {"step": "DDIM sampling (50 steps)"}
}
Cuando termina:
{
"task_id": "f4a8...",
"status": "completed",
"url": "out/api/f4a8.../master.wav",
"elapsed_s": 12.34,
"meta": {"plan": {...}, "mode": "ace", "duration_s": 60.0}
}
GET /api/v1/files/{task_id}/{filename}¶
Descarga del WAV producido (con protección anti path-traversal).
Filenames típicos:
- master.wav — mezcla maestra final
- vocals_stem.wav (solo en dual_stem)
- instrumental_stem.wav (solo en dual_stem)
- master_preview.wav (suma vocal+instr para preview rápido)
DELETE /api/v1/jobs/{task_id}¶
Cancela / revoca un trabajo en cola.
Cliente Python¶
import time
import requests
BASE = "http://localhost:8000"
def generate(prompt, **kw):
r = requests.post(f"{BASE}/api/v1/generate",
json={"prompt": prompt, **kw}, timeout=30)
r.raise_for_status()
return r.json()["task_id"]
def wait(task_id, timeout=600):
t0 = time.time()
while time.time() - t0 < timeout:
s = requests.get(f"{BASE}/api/v1/status/{task_id}").json()
if s["status"] in ("completed", "failed"):
return s
print(f" {s['status']} progress={s.get('progress', 0):.0%}")
time.sleep(2)
raise TimeoutError
task = generate("Bachata romántica en La menor, 125 BPM",
lyrics="[VERSE]\nLa luna brilla...",
duration_s=45, mode="ace")
res = wait(task)
print(res["url"])
Cliente curl¶
TASK=$(curl -s -X POST http://localhost:8000/api/v1/generate \
-H "Content-Type: application/json" \
-d '{"prompt":"Bachata 125 BPM en La menor","duration_s":30,"mode":"ace"}' \
| jq -r .task_id)
while :; do
STATE=$(curl -s "http://localhost:8000/api/v1/status/$TASK" | jq -r .status)
echo " $STATE"
[ "$STATE" = "completed" ] && break
[ "$STATE" = "failed" ] && exit 1
sleep 2
done
curl -s "http://localhost:8000/api/v1/status/$TASK" | jq
Seguridad y observabilidad¶
- En producción, montar detrás de un reverse proxy (nginx / Caddy) con TLS.
- Activar
require_api_key: trueenconfigs/api.yamlpara forzar headerX-API-Key. - Rate limit por IP via middleware (no incluido por defecto; añadir
slowapi). - Logs estructurados en stdout (recoger con Loki / Vector).
- Métricas Prometheus en
/metricssi se instalaprometheus-fastapi-instrumentator.