Files
talkscore-asr/app/models.py
T
Vladimir BryzgalovandClaude Opus 5 b1b09664b9 Сервис сам докачивает недостающие мелкие модели
Обновление возит только папку app, поэтому переход на детектор речи
потребовал файла, которого на машине нет - и сервис узнал об этом на
первой же задаче: отказ выглядел сбоем обработки, хотя это незаконченная
установка. Причём download_models.py лежит вне app и тоже был старым,
так что штатный способ докачки не помог бы.

Список моделей и адреса переехали в app/models.py. Мелкие докачиваются
при запуске, крупная модель распознавания - нет: сотни мегабайт без спроса
тянуть нельзя. Нехватка видна в /health полем missing_models.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 15:42:54 +05:00

84 lines
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Проверка и докачка моделей при запуске.
Обновление возит только папку app, поэтому смена режима работы может
потребовать файла, которого на машине нет: так вышло с детектором речи,
и сервис узнал об этом на первой же задаче. Список моделей и их адреса
живут здесь, внутри app, и приезжают вместе с кодом.
"""
import logging
from collections.abc import Callable
from pathlib import Path
__all__ = ["required_models", "missing_models", "ensure_models"]
log = logging.getLogger("talkscore-asr")
RELEASES = "https://github.com/k2-fsa/sherpa-onnx/releases/download"
# Модель распознавания качается отдельно: она весит сотни мегабайт и лежит
# на Hugging Face, докачивать её молча при запуске неправильно.
DOWNLOADABLE = {
"silero_vad.onnx": f"{RELEASES}/asr-models/silero_vad.onnx",
"nemo_en_titanet_small.onnx":
f"{RELEASES}/speaker-recongition-models/nemo_en_titanet_small.onnx",
}
def required_models(models_dir: Path, diarize: bool) -> list[Path]:
"""Что должно лежать на диске при текущих настройках."""
needed = [models_dir / "gigaam" / "config.json"]
if diarize:
needed.append(models_dir / "sherpa-onnx-pyannote-segmentation-3-0" / "model.onnx")
needed.append(models_dir / "nemo_en_titanet_small.onnx")
else:
needed.append(models_dir / "silero_vad.onnx")
return needed
def missing_models(models_dir: Path, diarize: bool) -> list[Path]:
return [path for path in required_models(models_dir, diarize) if not path.is_file()]
def ensure_models(models_dir: Path, diarize: bool,
fetch: Callable[[str, Path], None] | None = None) -> list[Path]:
"""Докачивает мелкие модели, которых не хватает. Возвращает оставшиеся.
Скачиваются только те, что весят единицы мегабайт: ждать их при запуске
не накладно. Всё крупное остаётся на download_models.bat - тянуть сотни
мегабайт без спроса нельзя.
"""
# Загрузчик передаётся параметром, а не берётся из глобальной области:
# подмену глобальной функции в тестах перебивает порядок импортов.
fetch = fetch or _fetch
still_missing: list[Path] = []
for path in missing_models(models_dir, diarize):
url = DOWNLOADABLE.get(path.name)
if url is None:
still_missing.append(path)
continue
try:
fetch(url, path)
print(f" Докачана модель: {path.name}")
except Exception as exc: # noqa: BLE001 - причина неважна, важен внятный отказ
log.warning("не удалось скачать %s: %s", path.name, exc)
still_missing.append(path)
return still_missing
def _fetch(url: str, dst: Path) -> None:
"""Скачивает файл во временное имя и переименовывает.
Встраиваемый Python идёт без хранилища сертификатов, поэтому берём
requests с certifi, а не urllib.
"""
import requests
dst.parent.mkdir(parents=True, exist_ok=True)
tmp = dst.with_suffix(dst.suffix + ".part")
with requests.get(url, stream=True, timeout=120) as response:
response.raise_for_status()
with open(tmp, "wb") as handle:
for chunk in response.iter_content(chunk_size=1 << 16):
handle.write(chunk)
# Переименование в конце: оборванная закачка не должна выглядеть моделью.
tmp.replace(dst)