Files
talkscore-asr/app/worker.py
T
Vladimir BryzgalovandClaude Opus 5 56cf7e5b0e Второй проход по промежуткам, которые детектор счёл тишиной
Диаризация задавала не только «кто говорит», но и границы того, что вообще
попадает в распознавание: фразы вне её сегментов пропадали молча. Второй
проход их возвращает с пометкой recovered=true и speaker=0.

Замер на восьми записях: 131 слово из 17 004 (0,8 %), 3-19 с на запись.
Возвращается и настоящий диалог (вопрос о цене, даты, документы), и радио
на фоне, поэтому проход отключается настройкой recover_gaps.

Скрипты render_transcript.py и check_gaps.py - для сверки расшифровки
с записью и поиска потерянных фраз.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 22:07:02 +05:00

48 lines
2.3 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) -> 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)