Перейти к содержанию

Веб-сервис

stt-ru serve, stt_ru.service

Описание

stt-ru serve запускает веб-интерфейс и REST API распознавания. Модель загружается один раз, файлы распознаются в очереди внутри процесса, результат скачивается в любом формате. Сервис рассчитан на одного пользователя или небольшую команду. Нужно дополнение service:

pip install "stt-ru[service]"
stt-ru serve                                         # http://127.0.0.1:8000
STT_RU_TOKEN=my-secret-token stt-ru serve --host 0.0.0.0 -j 2    # доступ по сети с токеном

Описание REST API с примерами запросов открывается на /docs.

Веб-интерфейс

  • Загрузка перетаскиванием или выбором, несколько файлов подряд, полоса загрузки.
  • Список задач этого браузера с состоянием: в очереди и место в ней, распознается, готово, ошибка.
  • Форма волны по каналам с фрагментами каждого говорящего, плеер и таблица фрагментов: щелчок по строке или по форме волны переходит к месту записи, текущий фрагмент подсвечивается.
  • Скачивание результата в TXT, JSON, JSONL, SRT, VTT, ASS и CSV, удаление задачи.

Задачи видны в том браузере, из которого загружены: их список хранится в браузере, а идентификаторы задач случайные.

Параметры

Параметр По умолчанию Описание
--host 127.0.0.1 Адрес: 127.0.0.1 - только этот компьютер, 0.0.0.0 - все адреса
--port 8000 Порт
-j, --workers 1 Сколько файлов распознавать одновременно
--max-size 500 Наибольший размер файла, МБ
--max-duration 180 Наибольшая длительность записи, минуты
--max-queue 100 Сколько файлов может ждать в очереди
--keep 24 Сколько часов хранить файл и результат после распознавания
--data-dir системный временный каталог Каталог для загруженных и временных файлов
-q, --quiet выключен Не выводить ход работы; ошибки выводятся всегда

Параметры модели и поиска речи - на странице Модели. Адрес и параметры проверяются до загрузки модели: занятый порт - сообщение и код 1.

Токен доступа

Токен задается переменной окружения STT_RU_TOKEN (параметр командной строки был бы виден всем в списке процессов). С токеном все действия с задачами требуют заголовок Authorization: Bearer <токен>; веб-интерфейс спрашивает токен один раз и хранит его в cookie на 30 дней. Без токена открыты страница, /healthz, /v1/info и описание API. Токен - строка из латинских букв, цифр и знаков - и _: многие клиенты HTTP не передают кириллицу в заголовках.

Без токена сервисом может пользоваться любой, кто достучится до порта, поэтому при --host 0.0.0.0 сервис выводит предупреждение. Токен один на всех: это не учетные записи. По обычному HTTP токен передается открытым текстом, поэтому за пределами доверенной сети ставьте сервис за прокси с HTTPS (nginx, Caddy). Случайный токен:

python -c "import secrets; print(secrets.token_urlsafe(24))"

Пределы и хранение

  • Файл больше --max-size отклоняется во время загрузки (ответ 413) и на диск не пишется.
  • Запись длиннее --max-duration дает задачу с ошибкой; длительность проверяется по заголовку файла или на декодировании, поэтому длинная запись не читается целиком в память.
  • Если в очереди --max-queue файлов, новые получают ответ 503 с Retry-After. Загрузка без токена (401) и в заполненную очередь отклоняется до передачи файла.
  • Файлы лежат во временном каталоге под случайными именами и удаляются по запросу, через --keep часов после окончания и при остановке сервиса. Задачи хранятся в памяти и пропадают при перезапуске; файлы запуска, завершившегося аварийно (kill -9, нехватка памяти), удаляются при следующем запуске с тем же каталогом (кроме Windows).

REST API

Метод и путь Что делает
POST /v1/transcriptions Ставит файл (multipart, поле file) в очередь: ответ 202 с задачей и заголовком Location
GET /v1/jobs/{id} Состояние задачи
GET /v1/jobs/{id}/result?format=srt Результат в формате (по умолчанию json) с именем файла в Content-Disposition
GET /v1/jobs/{id}/waveform Пики формы волны по каналам
GET /v1/jobs/{id}/audio Загруженный файл, с поддержкой перемотки (Range): звук и видео - для плеера, остальное - как вложение
DELETE /v1/jobs/{id} Удаляет задачу и ее файлы
GET /v1/info Версия, модель, пределы, форматы, нужен ли токен
GET /healthz Проверка работы: {"status": "ok"}

Задача - JSON с полями id, status (queued - в очереди, running - распознается, done - готово, failed - ошибка), filename, size, created, started, finished, expires (когда задача будет удалена), position (сколько задач перед ней), duration, channels, segments (число фрагментов) и error. Ошибки - {"detail": "..."}: 401 без токена, 404 для удаленной задачи, 409 для неготовой или упавшей, 413 для большого файла, 503 при полной очереди.

token="Authorization: Bearer my-secret-token"
job=$(curl -s -H "$token" -F file=@call.wav http://127.0.0.1:8000/v1/transcriptions | jq -r .id)
curl -s -H "$token" http://127.0.0.1:8000/v1/jobs/$job | jq .status
curl -s -H "$token" -OJ "http://127.0.0.1:8000/v1/jobs/$job/result?format=srt"
import time

import requests

api = "http://127.0.0.1:8000/v1"
headers = {"Authorization": "Bearer my-secret-token"}
with open("call.wav", "rb") as file:
    job = requests.post(f"{api}/transcriptions", files={"file": file}, headers=headers).json()
while job["status"] in ("queued", "running"):
    time.sleep(1)
    job = requests.get(f"{api}/jobs/{job['id']}", headers=headers).json()
result = requests.get(f"{api}/jobs/{job['id']}/result", headers=headers).json()
for segment in result["segments"]:
    print(segment["speaker"], segment["text"])

Сервис в своем приложении

create_app возвращает приложение ASGI (FastAPI) с очередью JobQueue. Запускайте его своим сервером ASGI с корня адреса: страница обращается к /static и /v1 от корня, а очистку задач по сроку и закрытие очереди выполняет запуск и остановка самого приложения, поэтому подключить его по пути внутри другого приложения нельзя.

import uvicorn

from stt_ru import Transcriber
from stt_ru.service import JobQueue, create_app

jobs = JobQueue(Transcriber(), "/data/stt", workers=2, max_duration=3600, keep=3600)
app = create_app(jobs, token="my-secret-token", max_size=200 << 20)
uvicorn.run(app, host="0.0.0.0", port=8000)