Потоковое распознавание — WebSocket¶
WebSocket STT принимает звук из микрофона или готовой записи. Клиент отправляет бинарные PCM-фреймы, а сервер возвращает JSON с промежуточным и финальным текстом по мере обработки.
Для длинных записей есть отдельный маршрут. Клиент передаёт исходный аудио- или видеоконтейнер частями и получает промежуточный и финальный текст по мере обработки:
В отличие от файловых методов, поток не сохраняет запись или транскрипт и не создаёт операцию. Коррекция LLM, NER и суммаризация здесь не выполняются. Для обоих маршрутов можно включить потоковую диаризацию начальной настройкой API; переключателя в браузерном интерфейсе пока нет.
Аутентификация¶
Используйте один из способов:
Authorization: Api-Key <ключ>илиAuthorization: Bearer <ключ>;- cookie активной portal-сессии;
- legacy query-параметр
?api_key=<ключ>.
Для новых клиентов предпочтителен заголовок: query string может попасть в
access-логи. Если анонимный доступ запрещён, соединение без аутентификации
закрывается с кодом 1008.
Для /api/stt/v1/file-stream/ws при кредитном биллинге всегда требуется
аутентификация, чтобы списание было связано с пользователем.
В начальном JSON обоих маршрутов можно передать "privacy_mode": true.
Для внешнего Scribe Realtime при этом требуется "external_ai_consent": true;
без согласия сервер возвращает external_ai_consent_required до передачи
аудио провайдеру. Также принимаются имена privacyMode и externalAiConsent.
Например:
{"language":"ru-RU","model":"ElevenLabs-Scribe-v2-Realtime","privacy_mode":true,"external_ai_consent":true}
В файловом маршруте добавьте эти поля в сообщение start.
Перед распознаванием сервер проверяет, что на балансе достаточно кредитов хотя
бы на одну секунду STT. Обычный PCM-маршрут при отказе отправляет
{"error":"...","code":"insufficient_credits"} и закрывает соединение с
кодом 1008; маршрут длинного файла отправляет событие error и закрывается с
кодом 4003. Дальнейший способ списания зависит от выбранного маршрута.
Длинный файл из браузера¶
Маршрут /api/stt/v1/file-stream/ws принимает исходный файл последовательными
бинарными сообщениями размером не более 4 МиБ. Это отдельный протокол от
PCM-потока: после начальной конфигурации отправляйте байты
аудио- или видеоконтейнера. Распознавание идёт по мере обработки поступивших
данных; клиент одновременно отправляет файл и читает ответы.
Первое сообщение обязательно задаёт сеанс:
{
"type": "start",
"filename": "recording.mp3",
"file_size": 1199868928,
"content_type": "audio/mpeg",
"language": "kk-KZ",
"channel_mode": "mono",
"diarization": false,
"llm_correction": false
}
Достаточно указать language: режим распознавания выбирается автоматически.
Если язык не передан, используется русский. Поле model необязательно; если
оно задано, значение должно соответствовать языку:
| Язык | Значение language |
Значение model, если указано явно |
|---|---|---|
| Русский | ru-RU |
SpeechExpert-STT-RU-stream |
| Английский | en-US |
Parakeet-EN |
| Казахский | kk-KZ |
SpeechExpert-STT-KK-stream |
| Кыргызский | ky-KG |
GigaAM-Multilingual-Large-CTC |
| Узбекский | uz-UZ |
SpeechExpert-STT-UZ-stream |
| Таджикский | tg-TJ |
SpeechExpert-STT-TG-stream |
Вместо локальной модели можно явно выбрать ElevenLabs-Scribe-v2-Realtime
(алиас scribe_v2_realtime). Она принимает 75 языков сервиса:
{"type":"start","filename":"recording.mp3","language":"ru-RU","model":"ElevenLabs-Scribe-v2-Realtime","diarization":false}
Для Realtime final_model должен быть пропущен или совпадать с model.
Окончательный текст выдаёт Realtime без файлового уточнения через Scribe v2
или Medical. Аудио передаётся ElevenLabs; подробности — в
обзоре Realtime.
Для кыргызской, узбекской, английской или таджикской записи замените language
в примере на ky-KG, uz-UZ, en-US или tg-TJ. Неподдерживаемый язык или несовместимое значение model
отклоняется до передачи файла.
Поле final_model также необязательно: по умолчанию оно совпадает с выбранным
model, и клиент получает итог каждой фразы без дополнительной настройки.
Для английского, казахского, кыргызского, узбекского и таджикского языков допускается только это
совпадающее значение. Для русского можно отдельно выбрать совместимый режим
уточнения, например:
Локальный кыргызский поток уточняет итог каждой фразы автоматически; отдельное значение
final_model не требуется. Например, достаточно {"type":"start","language":"ky-KG"}.
Partial следующей фразы может прийти до final предыдущей. Используйте
phrase_id из этих событий, чтобы final очищал только предварительный текст
своей фразы, в том числе при пустом итоговом тексте.
Для локального таджикского потока текст окончательных фраз возвращается без автоматической пунктуации и нормализации. Таймкоды обозначают границы аудиофрагментов.
Звук из файла распознаётся в mono. Если поле channel_mode передано, оно должно
равняться "mono"; другие значения отклоняются с unsupported_channel_mode.
Для диаризации передайте "diarization": true; при этом final_model должен
совпадать с model или быть пропущен. Отдельное уточнение текста отклоняется с
unsupported_diarization_refinement. LLM-коррекция должна быть выключена,
иначе start завершается ошибкой llm_correction_not_supported. Сервер также
ограничивает объявленный и фактически принятый файл 10 ГиБ.
Дождитесь ready перед отправкой бинарных чанков исходного файла.
После последнего чанка завершите ввод сообщением {"type":"eof"} и продолжайте
читать ответы до completed, чтобы получить итог оставшейся фразы. Для
остановки используется {"type":"cancel"}.
Серверные события:
type |
Назначение |
|---|---|
ready |
Сеанс создан; содержит session_id, language, выбранные streaming_model и recognition_model, параметры аудио и максимальный размер чанка |
upload_progress |
Сколько байт исходного контейнера принято |
progress |
processed_ms, оплаченная длительность, списанные кредиты и остаток |
partial |
Предварительный текст текущей фразы; refinement_status=pending, если запрошено уточнение |
final |
Итоговый текст фразы; при недоступности отдельно выбранного русского уточнения возвращается предварительный результат с refinement_status=fallback |
completed |
EOF обработан и финальный биллинг завершён |
error |
Ошибка протокола, декодирования, распознавания или баланса |
При временной недоступности распознавания событие error содержит один из кодов:
code |
Код закрытия WebSocket | Действие клиента |
|---|---|---|
capacity_exceeded |
1013 |
Все доступные сеансы заняты; повторите подключение после паузы |
recognition_unavailable |
1013 |
Распознавание временно недоступно; повторите подключение после паузы |
recognition_timeout |
1011 |
Истекло время ожидания распознавания; запрос можно повторить в новом соединении |
Повторное подключение создаёт новую сессию. Сохраните уже полученные итоговые
фразы: предыдущая обработка автоматически не возобновляется. Поля processed_ms,
billed_ms и charged_credits в событии ошибки описывают состояние сессии на
момент формирования этого события.
Каждый partial заменяет предварительный текст текущей фразы. Получив final,
добавьте его текст к итоговому транскрипту и очистите предварительный текст
этой фразы.
Финалы приходят в порядке записи, по мере завершения фраз. Итоговый текст может
отличаться от промежуточного; пустой final также завершает текущую фразу.
Поля start_ms и end_ms, когда они присутствуют, отсчитываются от начала
записи. Для локальных казахского, кыргызского, узбекского и таджикского потоков они обозначают границы обработанного
аудиофрагмента, включая паузы; timing_source имеет значение "audio-segment".
Это не пословное выравнивание: поле words для этих языков не передаётся.
Кредиты списываются по фактически обработанной длительности аудио с округлением
до начатой секунды. При
insufficient_credits обработка и загрузка останавливаются, а уже полученные
финальные фрагменты остаются у клиента.
Соединение должно оставаться открытым до события completed: при разрыве
обработка не продолжается в фоне. MP4/M4A-файлы, данные для декодирования которых
находятся в конце контейнера, поддерживаются: сервер сначала получает файл
целиком, затем начинает распознавание. Во время передачи такого файла клиент
получает upload_progress, а текст появляется после её завершения.
Выбор способа передачи и получения результата описан в разделе распознавание длинных записей.
Входной поток¶
Аудио имеет фиксированный формат:
| Параметр | Значение |
|---|---|
| Кодирование | signed PCM16 little-endian |
| Частота | 16 кГц |
| Каналы | mono |
Для длинной записи преобразуйте аудио в этот формат на стороне клиента и передавайте PCM частями. Одновременно читайте ответы: промежуточный текст появляется во время передачи, а итоговые фразы в пофразовых режимах можно сразу добавлять в транскрипт. Не нужно загружать весь исходный файл до появления первого текста. После последнего фрагмента отправьте EOF и дочитайте окончательный результат: русский режим по умолчанию возвращает его для всей сессии после EOF. Подробнее — распознавание длинных записей.
Первое сообщение может быть JSON-конфигурацией:
| Поле | По умолчанию | Описание |
|---|---|---|
language |
ru-RU |
Язык распознавания |
model |
SpeechExpert-STT-RU для русского |
T-One preview во время передачи и окончательный результат Riva после EOF |
enable_automatic_punctuation |
false |
Восстановление пунктуации в финальном тексте, если поддерживается выбранной моделью |
privacy_mode |
false |
Запрет внешнего STT без отдельного согласия |
external_ai_consent |
false |
Согласие на передачу звука внешнему провайдеру при включённом privacy_mode |
Автоматическое восстановление пунктуации сервисом выключено по умолчанию.
Чтобы включить его в PCM-маршруте, передайте логическое значение true в первом
сообщении; также принимается имя enableAutomaticPunctuation:
Восстановление применяется только к финальному тексту; промежуточные результаты
не проходят этот шаг. Знаки препинания, уже выданные выбранной моделью,
не удаляются. Эта настройка относится к /api/stt/v1/ws.
Конфигурация необязательна: первым сообщением можно сразу отправить бинарный PCM-фрейм. Если первое сообщение не поступило за 30 секунд, сеанс продолжается с настройками по умолчанию.
Русский режим по умолчанию SpeechExpert-STT-RU использует T-One для накопительного
предварительного текста и Riva для окончательного распознавания всей принятой
записи после EOF. Каждый is_final: false заменяет весь preview сессии;
окончательный текст после EOF заменяет его целиком. Если Riva не вернул текст,
итогом становится последний предварительный результат.
Чтобы получать прямые пофразовые результаты T-One во время записи, явно
выберите SpeechExpert-STT-RU-stream:
В этом режиме каждый partial заменяет только текущую фразу, а её final можно
сразу сохранить в транскрипте. Дополнительного Riva-распознавания после EOF нет.
Отдельный файловый маршрут /api/stt/v1/file-stream/ws сохраняет свои настройки по умолчанию.
Поддерживаемые языки и результаты¶
Локальные модели поддерживают следующие языки. Русский режим по умолчанию выдаёт общий
итог после EOF; явный SpeechExpert-STT-RU-stream и остальные пофразовые
потоки возвращают итог каждой завершённой фразы:
| Язык | Значение language |
Значение model |
|---|---|---|
| Русский, по умолчанию | ru-RU |
SpeechExpert-STT-RU |
| Русский, прямой пофразовый поток | ru-RU |
SpeechExpert-STT-RU-stream |
| Английский | en-US |
Parakeet-EN |
| Казахский | kk-KZ или kk |
SpeechExpert-STT-KK-stream |
| Кыргызский | ky-KG или ky |
GigaAM-Multilingual-Large-CTC |
| Узбекский | uz-UZ или uz |
SpeechExpert-STT-UZ-stream |
| Таджикский | tg-TJ или tg |
SpeechExpert-STT-TG-stream |
Для этих языков и остальных языков курируемого набора 75 языков
можно выбрать ElevenLabs-Scribe-v2-Realtime:
Короткий алиас scribe_v2_realtime также принимается. Формат входа сохраняется:
mono PCM16 16 кГц. VAD ElevenLabs завершает фразы по паузам; клиент получает
те же is_final: false|true. Окончательный текст ожидает пословных таймкодов
провайдера и выдаётся один раз с words и timing_source: "elevenlabs-scribe-realtime".
Если таймкоды не пришли вовремя, финал содержит оценённые границы фразы без words.
После EOF дочитывание результатов может занять до 15 секунд при стандартной
настройке. Дождитесь окончания потока, чтобы получить последнюю фразу.
Дополнительного файлового уточнения здесь нет. Сервер передаёт аудио ElevenLabs
и использует ELEVENLABS_API_KEY; webhook не нужен. Подробнее —
Scribe Realtime.
Идентификаторы в таблице нужны для настройки запроса. Несовместимые сочетания
языка и значения model отклоняются. Подробнее о выборе интерфейса —
в обзоре потокового распознавания.
Для казахского или узбекского потока отправьте соответственно:
Для таджикского потока используйте:
Локальный таджикский поток возвращает итог без автоматической пунктуации и нормализации текста.
Для кыргызского потока используйте:
В локальном кыргызском потоке текст каждой завершённой фразы уточняется отдельно.
Partial следующей фразы может прийти раньше final предыдущей. В событиях
передаётся phrase_id — целое неотрицательное число в пределах сессии.
Храните гипотезы по этому ключу; final очищает только соответствующий partial,
включая случай пустого final. Финалы идут в порядке фраз. Это правило действует
и для маршрута длинного файла; в остальных режимах поле может отсутствовать.
В пофразовых режимах из таблицы is_final: false содержит текущую предварительную
гипотезу фразы: заменяйте ею предыдущую гипотезу. is_final: true завершает
фразу: добавляйте её текст к финальному транскрипту и очищайте предварительный
текст этой фразы. Итог может отличаться от промежуточной гипотезы. Финалы сохраняют
порядок фраз и приходят во время сеанса, без ожидания EOF всей записи.
Успешный пустой final также завершает фразу и очищает её предварительный текст.
Границы фраз определяются автоматически. При EOF завершается оставшаяся фраза.
Доступны также режимы с уточнением предварительного текста:
| Язык | Значение model |
Поведение |
|---|---|---|
| Русский, по умолчанию | SpeechExpert-STT-RU |
Накопительный T-One preview всей записи и окончательный текст Riva после EOF |
| Русский | GigaAM-Multilingual-Large-CTC |
Накопительный промежуточный текст всей записи и один уточнённый итог после завершения ввода |
| Казахский | GigaAM-Multilingual-Large-CTC |
Предварительный текст текущей фразы и уточнённый итог каждой завершённой фразы |
В русских режимах с единственным итогом is_final: false заменяет весь
предварительный транскрипт. Если уточнение не дало текста, итогом становится
последний предварительный результат.
В узбекском потоке SpeechExpert-STT-UZ-stream возвращает предварительный
текст по мере поступления аудио и уточняет каждую завершённую фразу,
включая оставшийся фрагмент при завершении ввода.
Для казахского потока с уточнением текста отправьте конфигурацию:
Для этого режима действуют те же правила пофразовой обработки результатов.
После всех PCM-фреймов отправьте один из вариантов EOF и продолжайте читать ответы до закрытия потока:
Для Scribe Realtime сервер может дочитывать финалы до 15 секунд после EOF
при стандартной настройке. В PCM-маршруте завершение обозначается закрытием
WebSocket сервером с кодом 1000, отдельное событие completed не отправляется.
Оставьте соединение открытым до этого закрытия, чтобы получить итог последней
фразы. В файловом маршруте по-прежнему дождитесь события completed.
Ответы и таймстемпы¶
Каждое серверное сообщение — самостоятельный JSON-объект. В каждом событии
распознавания присутствуют text и is_final; тайминги добавляются, когда они
доступны. Сообщения об ошибках имеют отдельную форму без этих полей.
{
"text": "добрый день",
"is_final": false,
"start_ms": 340,
"end_ms": 1080,
"timing_source": "native-asr",
"words": [
{"text": "добрый", "start_ms": 340, "end_ms": 720},
{"text": "день", "start_ms": 760, "end_ms": 1080}
]
}
start_ms и end_ms отсчитываются от начала входного потока; нулевое начало
валидно. Для русского пофразового распознавания timing_source: "native-asr"
обозначает границы речи. Для локальных казахского, кыргызского, узбекского и таджикского потоков
timing_source: "audio-segment" обозначает границы обработанного аудиофрагмента,
включая паузы; это не точное выравнивание речи или слов. words для этого
режима не заполняется. При отсутствии доступного тайминга start_ms, end_ms,
timing_source и words опускаются.
В режимах с уточнением текста поле words не передаётся, если для итогового
текста неизвестно точное пословное выравнивание. Включённое восстановление
пунктуации может изменить text, поэтому words описывает исходные распознанные слова.
Потоковая диаризация¶
Включите диаризацию в первом JSON-сообщении PCM-маршрута:
В примере явно выбран прямой SpeechExpert-STT-RU-stream, чтобы финальные
фрагменты появлялись во время записи. В SpeechExpert-STT-RU окончательный
текст ASR появляется после EOF, поэтому диаризованные финалы также ждут EOF.
Эквивалентная настройка использует имена полей SpeechKit:
{
"language": "ru-RU",
"model": "SpeechExpert-STT-RU-stream",
"speaker_labeling": {"speaker_labeling":"SPEAKER_LABELING_ENABLED"}
}
Для длинного файла добавьте настройку в start; final_model можно пропустить,
чтобы он совпал с model:
{
"type": "start",
"filename": "conversation.wav",
"content_type": "audio/wav",
"language": "ru-RU",
"model": "SpeechExpert-STT-RU-stream",
"channel_mode": "mono",
"diarization": true
}
Файловый маршрут сводит вход в mono перед распознаванием и определением дикторов.
PCM-маршрут принимает mono PCM16 16 кГц. Состав сообщений и EOF остаются прежними.
partial не содержит метки диктора. Когда диаризация зафиксирует интервал речи,
final получает совпадающие строковые поля speaker_id и channel_tag:
{
"text": "добрый день",
"is_final": true,
"start_ms": 340,
"end_ms": 1080,
"speaker_id": "0",
"channel_tag": "0"
}
В файловом маршруте такой результат также содержит "type":"final".
ID принимает значения от "0" до "7" и сохраняется только внутри текущей
сессии. При недостаточной информации о дикторе оба поля опускаются; это возможно
и у непустого финального текста. Если слова имеют точные таймкоды и совпадают
с текстом, одна фраза может прийти несколькими финальными фрагментами разных
дикторов. Поле phrase_id, если оно доступно, сохраняется у всех фрагментов.
Если есть только таймкоды фразы, ей назначается один преобладающий диктор.
Разделение слов при одновременной речи нескольких людей не гарантируется.
Включённая диаризация учитывается при проверке баланса и списании. Задержка финала зависит от готовности ASR и диаризации; подробнее — в обзоре потоковой диаризации. Эти JSON-маршруты сохраняют собственный протокол; для protobuf-клиента SpeechKit используйте gRPC.
Ошибка распознавания возвращается как {"error":"..."}. Отдельного события
done нет: после EOF клиент дочитывает все final-сообщения до закрытия
соединения.
Пример Python¶
import asyncio
import json
import os
import websockets
async def send_audio(ws):
with open("audio.pcm", "rb") as source:
while chunk := source.read(3200): # около 100 мс
await ws.send(chunk)
await ws.send(json.dumps({"event": "eof"}))
async def receive_results(ws):
async for message in ws:
event = json.loads(message)
print(event)
async def main():
uri = "wss://api.speech.example.com/api/stt/v1/ws"
headers = {"Authorization": f"Api-Key {os.environ['API_KEY']}"}
async with websockets.connect(uri, additional_headers=headers) as ws:
await ws.send(json.dumps({"language": "ru-RU", "model": "SpeechExpert-STT-RU"}))
async with asyncio.TaskGroup() as tasks:
tasks.create_task(send_audio(ws))
tasks.create_task(receive_results(ws))
asyncio.run(main())
Выбор потокового API¶
| Интерфейс | Когда использовать |
|---|---|
| WebSocket | Браузер, простой JSON-протокол, mono PCM16 16 кГц |
| gRPC | Protobuf, несколько каналов, произвольная частота PCM, silence_chunk и нормализация |
| OpenAI SSE | Псевдострим готового загруженного файла; не для живого входящего аудио |