Voice WebSocket

Двунаправленный аудио-стриминг для voice AI агентов. Свой протокол поверх WebSocket — не совместим с OpenAI Realtime API. 12 multilingual голосов с поддержкой узбекского.

WSS wss://zowy.ai/api/v1/audio/stream?token=YOUR_API_KEY
Не OpenAI Realtime.

Если вы мигрируете с OpenAI Realtime — переписывать клиент придётся, см. Migration guide.

Жизненный цикл сессии

  1. Клиент открывает WSS-соединение с ?token=...
  2. Клиент отправляет config сообщение (JSON)
  3. Сервер отвечает {"type":"ready"}
  4. Двусторонний обмен audio frames + serverside events
  5. Сессия закрывается клиентом, сервером (по hard-cap), или ошибкой

Hard cap: 15 минут на сессию. После этого сервер закрывает соединение. Для длинных сессий — переподключайтесь.

Config — первое сообщение клиента

JSON
{
  "type": "config",
  "voice": "Laylo",
  "system_prompt": "You are a helpful assistant. Speak in Uzbek by default."
}
ПолеТипОписание
typestringВсегда "config"
voicestringИмя голоса. По умолчанию Laylo. См. Voices
system_promptstringСистемный промпт для модели

Аудио форматы

НаправлениеФорматSample rateКаналы
Input (client → server)PCM 16-bit signed (LE)16 kHzmono
Output (server → client)PCM 16-bit signed (LE)24 kHzmono

Аудио передаётся как бинарные WebSocket-фреймы (не base64). Управляющие сообщения — JSON в текстовых фреймах.

События от сервера

ТипКогдаПоля
readyПосле config — сессия готова{type:"ready"}
transcriptSTT транскрипт пользовательской речи{type:"transcript", text, final}
turn_completeAI закончил говорить{type:"turn_complete"}
errorОшибка{type:"error", code, message}

Полный пример — Python и Browser JS

import asyncio, json, websockets

API_KEY = "zowy_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
URL = f"wss://zowy.ai/api/v1/audio/stream?token={API_KEY}"

async def main():
    async with websockets.connect(URL) as ws:
        await ws.send(json.dumps({
            "type": "config",
            "voice": "Laylo",
            "system_prompt": "You are a helpful assistant.",
        }))
        msg = json.loads(await ws.recv())
        assert msg["type"] == "ready"

        # Send 16kHz mono PCM frames
        # await ws.send(pcm_bytes)

        async for raw in ws:
            if isinstance(raw, bytes):
                handle_audio_out(raw)  # PCM 24kHz output
            else:
                event = json.loads(raw)
                if event["type"] == "transcript":
                    print("USER:", event["text"])
                elif event["type"] == "turn_complete":
                    print("AI finished speaking")

asyncio.run(main())

Биллинг и ограничения

Каждая секунда сессии (округление вверх до минуты на закрытии) списывается с активного Voice-пакета. Тариф Start: 1500 UZS / минута. Если активного пакета нет — error с кодом 402.

  • Hard-cap 15 минут на сессию — после переподключайтесь
  • Один аудиопоток на одну WS-сессию
  • VAD/turn-taking — серверная (через Gemini native audio)
  • Нет встроенного echo cancellation — обрабатывайте на клиенте