Files
talkscore-asr/app/worker.py
T
Vladimir BryzgalovandClaude Opus 5 bea5d3f877 Липкий плеер, рабочий клик по времени, переключатель шума, режим двух моделей
Клик по времени не работал: в скрипте страницы черновика снова оказался
настоящий перевод строки вместо экранированного, и весь скрипт не выполнялся.
Тот же огрех, что был в странице разбора. Проверка парности кавычек теперь
охватывает обе страницы.

Плеер приклеен к верху на обеих страницах: спорное место слушают, не
прокручивая наверх.

Черновик подсвечивал слово при несогласии любой модели - жёлтым выходил
почти весь текст. Теперь по умолчанию нужно несогласие большинства: подсветки
втрое меньше (152 слова против 385), но в чистой части 95,4 % верных вместо
98 %. Строгий режим доступен ссылкой; порог применяется при показе, поэтому
переключение не требует пересборки.

Режим ensemble: вторая модель подставляется там, где основная промолчала.
Задаётся настройкой и параметром запроса - одну запись можно прогнать в обоих
режимах и сравнить на странице разбора.

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

50 lines
2.4 KiB
Python

"""Обработка задачи в отдельном процессе.
Замеры показали, что sherpa-onnx и onnxruntime держат GIL: две задачи в двух
потоках идут ровно столько же, сколько подряд (выигрыш 1.04x). Поэтому
процессор простаивает, а воркеры-потоки бесполезны. В процессах те же две
задачи дают 1.59x даже с загрузкой моделей в каждом.
Модуль намеренно не импортирует app.main: на Windows дочерний процесс
поднимается через spawn и заново импортирует модуль с функцией. Если бы
это был main, в каждом процессе поднимался бы ещё один веб-сервер.
"""
import logging
from pathlib import Path
from app.pipeline import Pipeline
log = logging.getLogger(__name__)
# Живёт внутри процесса-воркера: модели грузятся один раз на процесс.
_pipeline: Pipeline | None = None
def init_worker(models_dir: str, threads: int, replacements_path: str, base_dir: str) -> None:
"""Готовит процесс к работе: грузит модели один раз."""
global _pipeline
logging.basicConfig(level=logging.WARNING)
_pipeline = Pipeline(models_dir=Path(models_dir), threads=threads,
replacements_path=Path(replacements_path),
base_dir=Path(base_dir))
_pipeline.warmup()
def run_job(audio_path: str, num_speakers: int, ffmpeg: str,
normalize: bool = True, recover_gaps: bool = True,
diarize: bool = False, ensemble: bool = False) -> dict:
"""Переводит файл в WAV и распознаёт. Выполняется в процессе-воркере."""
import tempfile
from app.pipeline import to_wav16k
if _pipeline is None:
raise RuntimeError("процесс-воркер не инициализирован")
with tempfile.TemporaryDirectory() as tmp:
wav = Path(tmp) / "audio.wav"
to_wav16k(Path(audio_path), wav, ffmpeg, normalize)
return _pipeline.transcribe(wav, num_speakers=num_speakers,
recover_gaps=recover_gaps, diarize=diarize,
ensemble=ensemble)