Веб-сервис¶
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)