Voz em streaming
Texto entra, áudio sai em pedaços assim que cada um fica pronto. Várias falas por conexão, uma de cada vez. Escopo tts; R$ 20 por 1 milhão de caracteres.
wss://api.int8labs.com/v1/tts/streamAutenticação
No servidor, Authorization: Bearer int8_live_… no handshake. No navegador, ?token=int8_tmp_… na URL (um token de POST /v1/tokens). Credencial errada recebe a mensagem de erro e o fechamento 4401.
Mensagens
"speak"Uma fala: text (1 a 5.000 caracteres), voice e, opcionais, speed (0.8 a 1.3) e sample_rate (8000, 16000, 22050, 24000 ou 44100; padrão 44100).
{"type":"speak","text":"Sua consulta está confirmada.","voice":"helena","speed":1.0,"sample_rate":24000}"start"A fala começou: o id da fala, a taxa do áudio e em quantos pedaços ela vem.
{"type":"start","utterance_id":"utt_01JAA4K2W8M5R1T7Y3Q9C6V0BN","sample_rate":24000,"chunks":2}0x01 áudiobinárioUm pedaço de áudio. O primeiro byte é 0x01, os dois seguintes são o índice do pedaço, o resto é o som.
0x01 · índice do pedaço (uint16 LE) · PCM16 LE mono
"chunk"Depois de cada pedaço binário: o índice e a duração dele.
{"type":"chunk","index":0,"audio_ms":1840}"done"Fim da fala: caracteres cobrados, duração, tempo até o primeiro som, custo e saldo depois do débito.
{"type":"done","utterance_id":"utt_01JAA4K2W8M5R1T7Y3Q9C6V0BN","characters":29,"audio_ms":2310,"first_audio_ms":412,"cost_brl":0.00058,"balance_brl":87.3994}"error"Erro, com o mesmo code do REST. Os que fecham a conexão vêm seguidos do fechamento 4000 + o status HTTP.
{"type":"error","code":"insufficient_balance","message":"Saldo insuficiente. Recarregue em console › Faturamento.","request_id":"req_01JAA4M7Q2W8E5R3T6Y9U1I0PZ"}Regras
- Mande o próximo
speaksó depois dodoneda fala anterior. - Erros de uma fala (
invalid_request,payload_too_large,server_busy,concurrency_limited) chegam sem fechar a conexão; mande outra fala. - A credencial é conferida de novo antes de cada fala: chave revogada ou token vencido fecha com 4401.
- Sem mensagem por 5 minutos, ou depois de 4 horas de sessão, a conexão fecha com 1000.
Fechamentos
| Fechamento | Quando |
|---|---|
1000 | Fim normal: depois do done da transcrição ao vivo, por ociosidade ou ao passar de 4 h de sessão. |
4400 | invalid_request na transcrição ao vivo ou no modo áudio do avatar. Na voz e no modo texto do avatar, o erro de uma fala chega sem fechar a conexão. |
4401 | invalid_api_key, api_key_expired ou api_key_revoked, no começo ou no meio da sessão. Na transcrição ao vivo, os final do áudio já recebido chegam antes do erro. |
4402 | insufficient_balance: o saldo acabou. O que já foi falado ou ouvido é cobrado; na transcrição ao vivo, os final desse áudio chegam antes do erro. |
4403 | ip_not_allowed ou scope_denied. |
4413 | payload_too_large no modo áudio do avatar (fala acima de 5 min). |
4429 | rate_limited (áudio mais rápido que o tempo real) ou concurrency_limited (WebSockets demais na organização). |
4500 | internal_error. |
4503 | server_busy ou model_unavailable. |
Exemplo
import asyncioimport jsonimport osimport websockets # pip install "websockets>=14"async def falar(texto: str) -> bytes:audio = bytearray()async with websockets.connect("wss://api.int8labs.com/v1/tts/stream",additional_headers={"Authorization": f"Bearer {os.environ['INT8_API_KEY']}"},) as ws:await ws.send(json.dumps({"type": "speak", "text": texto, "voice": "helena", "sample_rate": 24000}))async for mensagem in ws:if isinstance(mensagem, bytes):if mensagem[0] == 0x01: # 0x01 + índice (uint16 LE) + PCM16 LE monoaudio += mensagem[3:]continueevento = json.loads(mensagem)if evento["type"] == "done":print("1º som em", evento["first_audio_ms"], "ms; custo R$", evento["cost_brl"])breakif evento["type"] == "error":raise RuntimeError(evento["message"])return bytes(audio)pcm = asyncio.run(falar("Sua consulta ficou para terça, às 14h30."))