Потоковое распознавание и синтез (gRPC)¶
Помимо HTTP REST API, сервис предоставляет gRPC-интерфейс для высокопроизводительных интеграций и потоковой обработки в реальном времени. Это совместимый поднабор Yandex SpeechKit API v3, а не полная реализация всех сообщений STT v3.
Подключение¶
В примерах используется публичный порт 50052. В Docker Compose он задаётся
API_GRPC_PORT (по умолчанию 50052) и пробрасывается во внутренний
GRPC_PORT=50051. При нативном запуске порт сервера задаётся непосредственно
GRPC_PORT (в run.ps1 — 50052). Не путайте публичный API с вышестоящим
Riva-сервером, который обычно слушает 50051.
Аутентификация¶
gRPC требует аутентификации: передавайте действующий API-ключ в metadata. Запросы без валидного ключа отклоняются с кодом UNAUTHENTICATED. Принимаются обе схемы — Bearer (как в клиентах в стиле Yandex) и Api-Key:
Сервисы¶
Recognizer — потоковое распознавание (STT v3)¶
Сервис speechkit.stt.v3.Recognizer реализует основной потоковый контракт
Yandex SpeechKit STT v3.
| Метод | Описание |
|---|---|
RecognizeStreaming |
Двунаправленный поток: клиент отправляет аудио (StreamingRequest), сервер возвращает промежуточные и итоговые результаты (StreamingResponse) |
Первый кадр от клиента должен содержать session_options; только после него
отправляйте аудио. Сервер технически может временно буферизовать ранние чанки,
но это не является контрактом. Поток, завершившийся без конфигурации сессии,
сейчас отклоняется с INTERNAL. Реализованы следующие параметры:
| Поле | Поведение |
|---|---|
recognition_model.model |
Выбор STT-маршрута |
recognition_model.language_restriction.language_code[0] |
Языковая подсказка; остальные коды и restriction_type пока не применяются |
recognition_model.audio_format.raw_audio.sample_rate_hertz |
Частота входного PCM |
recognition_model.audio_format.raw_audio.audio_channel_count |
Число interleaved-каналов, от 1 до 32 |
recognition_model.text_normalization |
Нормализация, profanity filter, литературный текст/пунктуация и форматирование телефонов |
eou_classifier.default_classifier.max_pause_between_words_hint_ms |
Пауза завершения фразы: управляет splitter T-One, а для Riva применяется как endpointing и/или backend EOU buffer согласно STT_STREAM_EOU_MODE |
eou_classifier.external_classifier пока принимается protobuf-схемой, но
игнорируется. Параметры HTTP v3 recognitionClassifier, speakerLabeling,
speechAnalysis и summarization в потоковую gRPC-сессию не входят.
После конфигурации клиент отправляет little-endian PCM16 в chunk.data. Для
нескольких каналов сэмплы должны быть interleaved; сервер разделяет каналы и
распознаёт их параллельно. Поток, оборванный посередине многоканального PCM-фрейма,
отклоняется с INVALID_ARGUMENT.
Вместо байтов можно отправить silence_chunk.duration_ms. Сервер лениво добавляет
нулевой PCM в исходную временную шкалу; допустимая длительность одного события —
от 1 до 300000 мс. Для тишины частота должна быть задана в raw_audio либо иметь
серверное значение по умолчанию.
Минимальный клиент для mono PCM16 16 кГц:
import os
import grpc
from speechkit.stt.v3 import stt_pb2, stt_service_pb2_grpc
def requests():
options = stt_pb2.StreamingOptions()
model = options.recognition_model
model.model = "T-one"
model.audio_format.raw_audio.sample_rate_hertz = 16_000
model.audio_format.raw_audio.audio_channel_count = 1
model.language_restriction.language_code.append("ru-RU")
yield stt_pb2.StreamingRequest(session_options=options)
with open("audio.pcm", "rb") as source:
while chunk := source.read(3200): # около 100 мс
yield stt_pb2.StreamingRequest(
chunk=stt_pb2.AudioChunk(data=chunk)
)
channel = grpc.insecure_channel("api.speech.example.com:50052")
stub = stt_service_pb2_grpc.RecognizerStub(channel)
metadata = (("authorization", f"Bearer {os.environ['API_KEY']}"),)
for response in stub.RecognizeStreaming(requests(), metadata=metadata):
event = response.WhichOneof("Event")
if event in {"partial", "final"}:
update = getattr(response, event)
print(event, update.channel_tag, update.alternatives[0].text)
elif event == "status_code":
print("closed", response.status_code.code_type)
Импорт в клиенте зависит от package path, который использовался при генерации
кода из stt.proto; имена protobuf-сообщений остаются теми же.
Маршрутизация моделей и псевдостриминг¶
Выбор модели задаётся в recognition_model.model:
SpeechExpert-STT— гибрид: T-One строит накопительные preview-сообщения, а после завершения входа SpeechExpert-STT распознаёт весь накопленный PCM и возвращает единственный авторитетныйfinal;T-one— прямой поток T-One с промежуточными и пофразовымиfinal;GigaAM-Multilingual-Large-CTCи устаревшие алиасыGigaAM-v3-CTC/SpeechExpert-STT-Enhanced— T-One preview и единственный offline-финал multilingual GigaAM, только для русского; казахский, кыргызский и узбекский доступны через асинхронное распознавание готового файла;- пустое значение использует
RIVA_ONLINE_MODEL; штатное значение —SpeechExpert-STT.
В гибридном режиме пофразовые финалы T-One добавляются к накопленному preview, но
наружу всё равно отправляются как partial. Если offline-модель вернула пустой
текст, последний накопленный preview повышается до итогового результата.
T-One принимает 8 кГц mono; входной канал автоматически ресемплируется. По
умолчанию используется CTC greedy decoder (TONE_DECODER=greedy). Опциональный
TONE_DECODER=kenlm требует kenlm.bin и зависимостей KenLM; provisional partial
при любом режиме декодируется greedy, а закрытая фраза — выбранным декодером.
Статус KenLM
Штатный проект работает на Python 3.13, а зависимость kenlm объявлена только
для Python ниже 3.13, поэтому стандартные uv, Docker Compose и run.ps1
используют greedy; run.ps1 дополнительно переопределяет значение после
загрузки .env. Для эксперимента с KenLM нужна отдельная совместимая сборка
runtime, установленные pyctcdecode/kenlm и доступный процессу API файл
kenlm.bin. В контейнере путь к файлу должен быть контейнерным и файл нужно
явно смонтировать; Windows-путь из .env.example сам по себе не монтируется.
Vosk удалён
В псевдостриминге и прямом потоке Vosk полностью заменён на T-One; Vosk-клиент,
контейнеры и переменные окружения больше не используются. Для плавной миграции
старые идентификаторы Vosk 0.62, Vosk 0.56, Vosk 0.54,
Zipformer2-RU, Vosk-Streaming-RU, Sherpa-Streaming-RU и
SpeechExpert-STT-Streaming пока маршрутизируются в T-one. Новые клиенты
должны отправлять T-one. Для списания кредитов эти legacy-ID также считаются
T-one; отдельного тарифа Vosk в публичном списке больше нет, хотя исходное
имя сохраняется в истории ранее созданных записей и транзакций.
Ответы и таймстемпы¶
Сервер возвращает только partial, final и завершающий status_code.
События HTTP v3 finalRefinement, eouUpdate, classifierUpdate, аналитика
спикеров/разговора и суммаризация в этом gRPC-контракте не реализованы.
Для T-One поля Alternative.start_time_ms, Alternative.end_time_ms и
Alternative.words[] содержат границы речи на шкале исходного потока,
рассчитанные из acoustic frames модели с шагом 30 мс. Это время речи, а не момент
получения сетевого чанка. Нулевой start_time_ms валиден.
В гибридном режиме все preview получают накопленный диапазон T-One. Итоговый
offline-текст получает тот же диапазон; если его текст отличается от preview,
words у финала остаётся пустым, потому что слово-в-слово выравнивание неизвестно.
Для backend без нативного тайминга scalar-поля protobuf остаются со значением 0,
а words пуст.
AudioCursors — отдельная шкала прогресса транспорта:
| Поле | Семантика |
|---|---|
received_data_ms |
Объём принятых PCM-данных, включая silence_chunk |
partial_time_ms |
Монотонная граница обработанного аудио; не меньше final_time_ms |
final_time_ms |
Монотонный максимум границ отправленных финалов |
eou_time_ms |
Сейчас совпадает с final_time_ms |
final_index |
Число отправленных final в сессии |
Нативные границы в Alternative не подменяются курсорами: уточнённый финал может
заканчиваться раньше предыдущего partial, тогда как partial_time_ms всё равно
движется только вперёд. Для многоканального аудио AlternativeUpdate.channel_tag
равен строковому номеру канала, начиная с "1". После обработки всего ввода
сервер отправляет отдельный status_code с code_type=CLOSED.
response_wall_time_ms в gRPC содержит Unix-время формирования ответа в
миллисекундах. Это отличается от одноимённого JSON-поля HTTP v3, где хранится
прошедшее время обработки операции.
Нормализация применяется непосредственно к каждому partial и final,
отдельного finalRefinement нет. Если объект text_normalization передан с
PHONE_FORMATTING_MODE_UNSPECIFIED, форматирование телефонов считается
включённым. words[] сохраняют токены backend и поэтому после пунктуации,
фильтрации или нормализации могут уже не совпадать с итоговым text.
WebSocket STT¶
Для браузерных клиентов и более простого JSON-протокола используйте отдельный WebSocket STT. Он принимает mono PCM16 16 кГц и не требует генерации клиентского кода из protobuf.
Synthesizer — потоковый синтез (TTS v3)¶
Сервис speechkit.tts.v3.Synthesizer поддерживает синтез речи через методы UtteranceSynthesis и StreamSynthesis.
Server Reflection¶
Сервер поддерживает gRPC Server Reflection — API можно исследовать через grpcurl:
# Список доступных сервисов
grpcurl -plaintext -H "authorization: Bearer ${API_KEY}" api.speech.example.com:50052 list
# Описание доступных методов
grpcurl -plaintext -H "authorization: Bearer ${API_KEY}" api.speech.example.com:50052 describe
Proto-файлы
Proto-файлы находятся в app/grpc_services/proto/speechkit/. Для генерации клиентского кода используйте их либо Server Reflection.