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.
Жизненный цикл сессии
- Клиент открывает WSS-соединение с
?token=... - Клиент отправляет
configсообщение (JSON) - Сервер отвечает
{"type":"ready"} - Двусторонний обмен audio frames + serverside events
- Сессия закрывается клиентом, сервером (по hard-cap), или ошибкой
Hard cap: 15 минут на сессию. После этого сервер закрывает соединение. Для длинных сессий — переподключайтесь.
Config — первое сообщение клиента
JSON
{
"type": "config",
"voice": "Laylo",
"system_prompt": "You are a helpful assistant. Speak in Uzbek by default."
}
| Поле | Тип | Описание |
|---|---|---|
| type | string | Всегда "config" |
| voice | string | Имя голоса. По умолчанию Laylo. См. Voices |
| system_prompt | string | Системный промпт для модели |
Аудио форматы
| Направление | Формат | Sample rate | Каналы |
|---|---|---|---|
| Input (client → server) | PCM 16-bit signed (LE) | 16 kHz | mono |
| Output (server → client) | PCM 16-bit signed (LE) | 24 kHz | mono |
Аудио передаётся как бинарные WebSocket-фреймы (не base64). Управляющие сообщения — JSON в текстовых фреймах.
События от сервера
| Тип | Когда | Поля |
|---|---|---|
| ready | После config — сессия готова | {type:"ready"} |
| transcript | STT транскрипт пользовательской речи | {type:"transcript", text, final} |
| turn_complete | AI закончил говорить | {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())const ws = new WebSocket(`wss://zowy.ai/api/v1/audio/stream?token=${API_KEY}`);
ws.binaryType = "arraybuffer";
ws.onopen = () => {
ws.send(JSON.stringify({
type: "config",
voice: "Laylo",
system_prompt: "You are a helpful assistant.",
}));
};
ws.onmessage = (e) => {
if (typeof e.data === "string") {
const event = JSON.parse(e.data);
if (event.type === "ready") startMicCapture(ws);
if (event.type === "transcript") console.log("USER:", event.text);
if (event.type === "turn_complete") console.log("AI done");
} else {
playAudio(e.data); // PCM 24kHz mono
}
};
function startMicCapture(ws) {
// capture mic, downsample to 16kHz mono PCM 16-bit, send via ws.send(buffer)
}Биллинг и ограничения
Каждая секунда сессии (округление вверх до минуты на закрытии) списывается с активного Voice-пакета. Тариф Start: 1500 UZS / минута. Если активного пакета нет — error с кодом 402.
- Hard-cap 15 минут на сессию — после переподключайтесь
- Один аудиопоток на одну WS-сессию
- VAD/turn-taking — серверная (через Gemini native audio)
- Нет встроенного echo cancellation — обрабатывайте на клиенте