Transmite diálogos en tiempo real

Esta guía te muestra cómo transmitir audio de diálogos de Eleven v3 a través de WebSocket de Texto a Diálogo.

El WebSocket de Texto a Diálogo (/v1/text-to-dialogue/stream-input) mantiene una única conexión abierta mientras envías líneas de diálogo y recibes fragmentos de audio codificados en base64. Está diseñado únicamente para los modelos de diálogo Eleven v3 y Eleven v4 (model_id debe empezar por eleven_v3 o eleven_v4).

Esta guía trata sobre el WebSocket de Texto a Diálogo. Para Flash, Multilingual v2 u otros modelos de TTS que no sean v3, usa el WebSocket de TTS en tiempo real. Para ver un resumen comparativo de ambos protocolos, consulta WebSockets de Texto a Voz y Texto a Diálogo .

Requisitos

  • Una cuenta de ElevenLabs con una clave de API (autenticación).
  • La clave de API debe tener permisos de Text to Speech.
  • Python o Node.js instalados en tu equipo.

Configuración

pip install python-dotenv websockets

Crea un archivo .env:

.env
ELEVENLABS_API_KEY=your_elevenlabs_api_key_here

Elige un ID de voz de la Biblioteca de voces. Los ejemplos siguientes usan eleven_v4_turbo, que permite una voz registrada por conexión.

Abre el WebSocket

Conéctate a wss://api.elevenlabs.io/v1/text-to-dialogue/stream-input con parámetros de consulta como model_id y output_format. Puedes enviar la clave de API en la cabecera xi-api-key o en el primer mensaje JSON (aquí se muestra en el cuerpo para seguir un mismo patrón en todos los lenguajes).

import asyncio
import base64
import json
import os
from dotenv import load_dotenv
import websockets
load_dotenv()
ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY")
VOICE_ID = "21m00Tcm4TlvDq8ikWAM"
MODEL_ID = "eleven_v4_turbo"
URI = (
"wss://api.elevenlabs.io/v1/text-to-dialogue/stream-input"
f"?model_id={MODEL_ID}&output_format=mp3_44100_128"
)

Registra voces y transmite texto

Envía un primer mensaje que incluya voices (obligatorio) y xi_api_key si no configuraste la cabecera xi-api-key. Después, envía uno o varios frames con inputs: cada elemento tiene text, voice_id y new_turn opcional.

El servidor almacena el texto en búfer hasta tener suficiente contexto (unos 40 caracteres y 8 palabras) y, a continuación, emite fragmentos de audio. Los campos de respuesta usan snake_case (por ejemplo, is_final).

async def stream_dialogue():
async with websockets.connect(URI) as websocket:
await websocket.send(
json.dumps(
{
"voices": [VOICE_ID],
"xi_api_key": ELEVENLABS_API_KEY,
}
)
)
line = (
"This is a longer line of dialogue used to exceed the minimum buffer so the model "
"starts generating streamed audio for the registered voice. "
)
await websocket.send(
json.dumps(
{
"inputs": [
{"text": line, "voice_id": VOICE_ID, "new_turn": False},
],
}
)
)
await websocket.send(json.dumps({"close_socket": True}))
os.makedirs("output", exist_ok=True)
out_path = "output/dialogue-ws.mp3"
with open(out_path, "wb") as audio_file:
while True:
raw = await websocket.recv()
msg = json.loads(raw)
if msg.get("error"):
raise RuntimeError(msg)
if msg.get("audio"):
audio_file.write(base64.b64decode(msg["audio"]))
if msg.get("is_final"):
break
print(f"Wrote {out_path}")
asyncio.run(stream_dialogue())

close_socket envía cualquier texto que quede en el búfer, el audio restante y, después, un frame final con is_final: true antes de que se cierre la conexión. Para mantener la conexión abierta entre líneas, omite close_socket hasta que termine la sesión; usa flush para forzar audio con búferes más cortos sin cerrar.

Ejecuta el script

python text-to-dialogue-websocket.py

Deberías obtener un archivo MP3 en output/ (con el nombre de archivo del ejemplo anterior).

Notas sobre el funcionamiento

Almacenamiento en búfer

A diferencia de chunk_length_schedule del WebSocket de TTS, la transmisión de diálogos usa un umbral fijo del servidor (número de caracteres y palabras) antes del primer audio parcial. Si envías líneas cortas y percibes retrasos, agrupa un poco más de texto por frame de inputs o envía flush: true para forzar la generación sin cerrar el socket.

Turnos y voces

Configura new_turn: true cuando un hablante termine su turno para que la prosodia se reinicie correctamente. Cambiar voice_id entre entradas de inputs también inicia un nuevo turno. Con eleven_v4_turbo, registra exactamente una voz en voices; eleven_v4 admite hasta 10 voces registradas.

Inactividad

Si el servidor no recibe ningún mensaje del cliente durante 20 segundos, la conexión se cierra. Envía {"keep_alive": true} para reiniciar el temporizador sin sintetizar audio.

Concurrencia

Cada conexión abierta mantiene una sesión de diálogo mientras permanezca abierta y procede de un grupo dedicado independiente del límite de concurrencia estándar de tu plan. El audio generado a través de la conexión no cuenta para la concurrencia estándar. Consulta la concurrencia de Texto a Diálogo.

Alineación

Añade sync_alignment=true a la cadena de consulta para recibir objetos alignment (matrices de temporización en snake_case) en los fragmentos cuando estén disponibles. Consulta la referencia de la API.

Próximos pasos