# 04 · Referencia de modelos

Tabla de consulta rápida de todo lo que el editor le pide a una IA: qué modelo usa cada paso, su slug exacto, cuánto cuesta, qué aspect ratios acepta y dónde está definido en el código. Si algo acá no coincide con el código, manda el código (`src/lib/`).

El flujo completo, en orden:

```
imagen → animar → voz → transcribir → música → build → render → mezcla
```

| Paso | Herramienta | Qué consume | Definido en |
|---|---|---|---|
| imagen | Kie.ai (GPT Image / Nano Banana) | créditos Kie | `src/lib/kie.mjs`, `src/runners/imagen.mjs` |
| animar | Kie.ai (Grok / Wan) | créditos Kie | `src/lib/kie.mjs`, `src/runners/animar.mjs` |
| voz | ElevenLabs | cuota de caracteres ElevenLabs | `src/lib/elevenlabs.mjs`, `src/runners/voz.mjs` |
| transcribir | Whisper local (vía HyperFrames) | nada (corre en tu máquina) | `src/runners/transcribir.mjs` |
| música | Suno (sunoapi.org) | cuota de sunoapi.org | `src/lib/suno.mjs`, `src/runners/musica.mjs` |
| build | tu `build.mjs` de la composición | nada | `composicion/build.mjs` |
| render | HyperFrames (`npx hyperframes render`) | nada (local) | `src/runners/render.mjs` |
| mezcla | ffmpeg | nada (local) | `src/lib/ffmpeg.mjs`, `src/runners/mezclar.mjs` |

> Solo los pasos de imagen, animación, voz y música gastan saldo de un servicio externo. Transcribir, render y mezcla corren en tu computadora y no cuestan créditos.

---

## Modelos de imagen (texto → imagen)

Un solo proveedor: Kie.ai. El runner es `src/runners/imagen.mjs` y los modelos viven en `IMAGE_MODELS` (`src/lib/kie.mjs`).

| Alias (`--model`) | Slug exacto | Costo | Cuándo usarlo |
|---|---|---|---|
| `gpt-image` *(default)* | `gpt-image-2-text-to-image` | 6 cr | Texto legible dentro de la imagen y composiciones precisas. Es el que se usa para "crear la imagen como con ChatGPT". |
| `nano-banana` | `google/nano-banana` | 4 cr | Más barato. Sirve para escenas sin texto. No escribe bien texto en español (sale con errores de ortografía). |

```bash
# Imagen por defecto (gpt-image, vertical 9:16)
node src/runners/imagen.mjs --prompt "una mascota robot saludando, fondo blanco" --out ./assets/escena-1.png

# Versión más barata, formato horizontal
node src/runners/imagen.mjs --prompt "..." --out ./a.png --model nano-banana --aspect 16:9
```

El runner imprime el saldo antes y después, y devuelve dos cosas: la ruta local del PNG y una **URL remota** que el paso de animación puede consumir directo sin volver a subir el archivo.

---

## Modelos de video (imagen → video, I2V)

Mismo proveedor (Kie.ai). El runner es `src/runners/animar.mjs`; los modelos viven en `VIDEO_MODELS` (`src/lib/kie.mjs`). Toman una imagen (URL pública o archivo local) más un prompt de movimiento y devuelven un clip, que el runner normaliza a **1080×1920, 30 fps, sin audio**.

| Alias (`--model`) | Slug exacto | Costo | Salida | Cuándo usarlo |
|---|---|---|---|---|
| `grok` *(default)* | `grok-imagine/image-to-video` | 18 cr | 720p, 6 s | Animación sutil de una ilustración (respiración, parpadeo). Acepta `aspect_ratio`. |
| `wan` | `wan/2-6-flash-image-to-video` | 30 cr | 1080p, 5 s | Cuando querés más resolución de origen. Ignora `--aspect`: conserva la proporción de la imagen de entrada. |

```bash
# Animar una imagen que ya está en Kie (URL remota del paso anterior)
node src/runners/animar.mjs --image-url "https://tempfile..." --out ./assets/clip-1.mp4

# Animar una imagen local (el runner la sube a Kie automáticamente)
node src/runners/animar.mjs --image ./assets/escena-1.png --out ./assets/clip-1.mp4 \
  --movement "ligera respiración, un parpadeo, cámara fija, loop continuo"
```

Si no pasás `--movement`, se aplica un movimiento sobrio por defecto (`MOVIMIENTO_DEFAULT`): vida interna sutil, sin elementos nuevos, cámara fija, loop. Si das `--image` (archivo local), el runner lo sube con `uploadImage` y obtiene una URL pública temporal (ver más abajo).

---

## Aspect ratios

`--aspect` se pasa tal cual a la API: como `image_size` en la imagen y como `aspect_ratio` en Grok. El valor por defecto en todos los runners es `9:16`.

| Valor | Forma | Uso típico |
|---|---|---|
| `9:16` *(default)* | vertical | Reels, TikTok, Shorts |
| `4:5` | vertical recortado | feed de Instagram |
| `16:9` | horizontal | YouTube, miniaturas |

Notas que vienen del código y la práctica:

- **Grok no acepta `4:3`** — no lo fuerces; usá `9:16`, `4:5` o `16:9`.
- **Wan ignora `--aspect`**: respeta la proporción de la imagen de entrada. Si querés una forma concreta con Wan, generala desde la imagen.
- **Nano Banana** respeta `9:16`, `4:3` y `16:9`.
- El paso de animación normaliza el clip final a **1080×1920** sí o sí (recorte centrado). Para horizontal real, ajustá `normalizeClip` en `src/lib/ffmpeg.mjs`.

---

## Voz — ElevenLabs

Definida en `src/lib/elevenlabs.mjs`; el runner es `src/runners/voz.mjs`. Autenticación por header `xi-api-key`. Endpoint de voz: `POST /v1/text-to-speech/{voice_id}`.

- **model_id por defecto:** `eleven_multilingual_v2` (recomendado para español). Si tu cuenta tiene `eleven_v3`, podés pasarlo con `--model`.
- **Ajustes de voz** (en código): `stability 0.5`, `similarity_boost 0.75`, `style 0.0`, `use_speaker_boost true`.

### Elegir la voz (`--voice`)

`--voice` acepta un **alias conocido** o un **voice_id directo** de tu cuenta.

| Alias | voice_id | Nota |
|---|---|---|
| `rachel` *(default)* | `21m00Tcm4TlvDq8ikWAM` | inglés, cálida |
| `antoni` | `ErXwobaYiN019PkySvjV` | inglés, narrador |
| `bella` | `EXAVITQu4vr4xnSDxMaL` | inglés, suave |

> Los alias incluidos son voces en inglés. **Para narración en español, copiá un `voice_id` desde tu panel de ElevenLabs** y pasalo con `--voice <id>`.

Para listar las voces de tu cuenta (con sus IDs):

```bash
curl -sS https://api.elevenlabs.io/v1/voices \
  -H "xi-api-key: $ELEVENLABS_API_KEY" | python3 -m json.tool
```

```bash
# Voz desde texto inline
node src/runners/voz.mjs --text "Hola, esto es una prueba" --out ./assets/voz.mp3

# Voz desde un guion, con un voice_id en español
node src/runners/voz.mjs --file ./guion.txt --out ./assets/voz.mp3 --voice <tu-voice-id>
```

### Efectos de sonido (SFX) — opcional

Mismo proveedor y misma key. Endpoint `POST /v1/sound-generation`. El runner es `src/runners/sfx.mjs`.

- **Prompt en inglés** (la generación de SFX rinde más con descripciones en inglés).
- **Duración:** `--dur` en segundos, rango **0.5–22 s** (default 2). Menos de 0.5 s da error HTTP 400; el código lo limita al rango.

```bash
node src/runners/sfx.mjs --prompt "cinematic whoosh transition" --out ./assets/whoosh.mp3 --dur 1.5
```

---

## Música — Suno (sunoapi.org)

Definida en `src/lib/suno.mjs`; el runner es `src/runners/musica.mjs`. Flujo asíncrono: crear tarea → polling → descargar. Autenticación `Authorization: Bearer`.

- **Modelo por defecto:** `V4_5` (`--model` para cambiarlo).
- **Instrumental por defecto:** la música de fondo se genera sin voces para no competir con la narración. Si necesitás voces, pasá `--withVocals`.
- **Prompt:** se recorta a **500 caracteres** (modo no-custom: la API arma el resto desde tu prompt).
- **Salida:** uno o más MP3 en el directorio que indiques, nombrados `<base>-track0.mp3`, `<base>-track1.mp3`…

```bash
# Música instrumental de fondo
node src/runners/musica.mjs --prompt "tech minimal, calmo, cinematic, pads suaves, instrumental" --out ./assets

# Con un nombre base y con voces (no recomendado para fondo)
node src/runners/musica.mjs --prompt "..." --out ./assets --name fondo --withVocals
```

> La mezcla final (`mezclar.mjs`) **rechaza música con voces**: mide el exceso centro/lado en la banda de voz y, si supera 6.5 dB, lanza un error en vez de mezclar. Mantené el fondo instrumental.

---

## Subtítulos — Whisper (local, vía HyperFrames)

No usa ninguna API ni key: corre Whisper en tu máquina con `npx hyperframes transcribe`. El runner es `src/runners/transcribir.mjs`.

- **Modelos:** `small` (default, buen balance) o `medium` (más preciso, más lento). El modelo se baja la primera vez.
- **Idioma:** `--language es` por defecto en el runner.

> **Regla dura: nunca uses modelos `.en` con audio en español.** Los modelos terminados en `.en` (p. ej. `small.en`) **traducen** el audio al inglés y arruinan los subtítulos. El runner avisa si detecta esa combinación. Para español: `--model small --language es`.

```bash
node src/runners/transcribir.mjs --audio ./assets/voz.mp3 --language es --model small
```

Genera tres archivos junto al audio:

- `transcript.json` — palabras planas tal como salen de Whisper.
- `words.json` — palabras limpias, con duraciones corregidas.
- `groups.json` — grupos de 1–2 palabras, listos para subtítulos tipo karaoke.

---

## Render — HyperFrames

`src/runners/render.mjs` envuelve `npx hyperframes render` corriendo dentro de la carpeta de la composición (la que tiene `index.html`). Antes valida con `hyperframes validate` (los avisos no detienen el render).

- La **versión de HyperFrames se fija en el `package.json` de la composición** (p. ej. `0.6.20`), para que el render sea reproducible. El runner invoca `npx --yes hyperframes`, que respeta esa versión instalada.
- Con `--out` deja el MP4 donde digas; sin `--out`, busca el `.mp4` más reciente dentro de la carpeta.

```bash
node src/runners/render.mjs --dir ./ejemplo/proyecto-demo/composicion --out ./renders/video.mp4
```

---

## Endpoints de Kie.ai

Base: `https://api.kie.ai/api/v1`

| Método | Ruta | Para qué |
|---|---|---|
| `POST` | `/jobs/createTask` | crear la tarea (imagen o video). Body: `{ model, input }` → devuelve `taskId`. |
| `GET` | `/jobs/recordInfo?taskId=…` | consultar estado y resultado de la tarea. |
| `GET` | `/chat/credit` | saldo de créditos disponible. |

**Subir una imagen local** (para animar una imagen propia que no salió de Kie):

```
POST https://kieai.redpandaai.co/api/file-base64-upload
```

> Las URLs que devuelve el upload son **temporales: se borran a los ~3 días**. Subí justo antes de animar; no las guardes para más tarde. El runner `animar.mjs` ya hace este upload solo cuando le pasás `--image`.

---

## Costo estimado por video

Cada escena cuesta una imagen más una animación. Lo demás (voz, música) consume cuota de su propio servicio, no créditos Kie; transcribir, render y mezcla son gratis.

Con los modelos por defecto (`gpt-image` + `grok`):

| Paso | Modelo | Costo unitario | 1 escena | 3 escenas (demo) |
|---|---|---|---|---|
| imagen | gpt-image | 6 cr | 6 cr | 18 cr |
| animar | grok | 18 cr | 18 cr | 54 cr |
| voz | eleven_multilingual_v2 | cuota ElevenLabs (caracteres) | — | — |
| música | Suno V4_5 | cuota sunoapi.org (1 generación) | — | — |
| transcribir / render / mezcla | local | 0 | 0 | 0 |
| **Total créditos Kie** | | | **24 cr** | **72 cr** |

Con la combinación más barata (`nano-banana` + `wan`): 4 + 30 = **34 cr por escena**. Nano Banana baja el costo de la imagen, pero Wan sube el de la animación; conviene cuando necesitás 1080p de origen y la escena no lleva texto.

Regla de presupuesto: antes de un lote, calculá `escenas × (costo_imagen + costo_animar)` y confirmá que el saldo lo cubre con margen.

---

## Consultar el saldo de créditos de Kie

El endpoint es `GET /chat/credit`. Los runners de imagen y video ya imprimen el saldo antes y después de cada tarea. Para consultarlo a mano:

```bash
# Si la key está en una variable de entorno
curl -sS https://api.kie.ai/api/v1/chat/credit \
  -H "Authorization: Bearer $KIE_API_KEY"
```

```bash
# Si la key está en el Keychain de macOS (no la imprime: la pasa directo al header)
curl -sS https://api.kie.ai/api/v1/chat/credit \
  -H "Authorization: Bearer $(security find-generic-password -a editor -s 'editor:kie-ai-key' -w)"
```

El saldo viene en el campo `data` de la respuesta. `npm run check` confirma que las keys están configuradas, pero **no** consulta créditos ni imprime ningún valor de key.

---

## Dónde se define cada cosa (mapa rápido)

| Querés cambiar… | Archivo |
|---|---|
| slug o costo de un modelo de imagen/video | `src/lib/kie.mjs` (`IMAGE_MODELS`, `VIDEO_MODELS`) |
| endpoints o el upload de Kie | `src/lib/kie.mjs` |
| voces, ajustes de voz, SFX | `src/lib/elevenlabs.mjs` |
| modelo de música, instrumental, prompt máx. | `src/lib/suno.mjs` |
| resolución/fps del clip, mezcla y masterización | `src/lib/ffmpeg.mjs` |
| de dónde salen las keys (env → Keychain) | `src/lib/keys.mjs` |
