feat(transcriber): добавлен module выполнения с владением моделью и исполнением

Transcriber принимает ExecutionRequest, разрешает auto и device-aware
умолчания, загружает модель один раз на запуск и держит fallback внутри.
Caller получает TranscribeResult и ExecutionInfo вместо model/backend/path.
Публичный transcribe() идёт через тот же module. Adapters отдают
runtime_info() для диагностики; legacy load_model/_transcribe_file пока
остаются для CLI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dmitry DementievandClaude Opus 5 committed 2026-09-21 23:46:54 +03:00
1 parent 343a31b35b
commit 4dea7f4bc6
10 files changed
+620 -29

No files matched your search

+4
View File
@@ -53,3 +53,7 @@ class Backend(Protocol):
) -> TranscribeResult:
"""Транскрибирует файл, возвращает результат."""
...
def runtime_info(self) -> dict[str, str]:
"""Версии runtime и фактическая конфигурация для удалённой диагностики."""
...
@@ -16,6 +16,7 @@ from local_transcriber.types import (
Word,
WordTimestampsUnavailableError,
)
from local_transcriber.utils import package_version
MODEL_REPOS = {
"tiny": "Systran/faster-whisper-tiny",
@@ -157,6 +158,22 @@ class FasterWhisperBackend:
words=words,
)
def runtime_info(self) -> dict[str, str]:
"""Версии CTranslate2/faster-whisper и число видимых CUDA-устройств."""
import ctranslate2
try:
cuda_devices = str(ctranslate2.get_cuda_device_count())
except Exception as exc: # noqa: BLE001 — диагностика не должна ронять запуск
cuda_devices = f"недоступно ({exc})"
return {
"engine": "faster-whisper",
"faster_whisper": package_version("faster-whisper"),
"ctranslate2": package_version("ctranslate2"),
"cuda_devices": cuda_devices,
"compute_type": self.actual_compute_type or "",
}
def _notify(on_status: Callable[[str], None] | None, message: str) -> None:
if on_status is not None:
+27 -2
View File
@@ -15,6 +15,7 @@ from local_transcriber.types import (
Word,
WordTimestampsUnavailableError,
)
from local_transcriber.utils import package_version
@dataclass(frozen=True)
@@ -138,6 +139,8 @@ class OnnxAsrBackend:
self._model_name: str | None = None
self._model_spec: OnnxModelSpec | None = None
self._vad: Any = None
self._providers: list[str] = []
self._quantization: str | None = None
@property
def word_timestamps_available(self) -> bool:
@@ -197,15 +200,37 @@ class OnnxAsrBackend:
actual_compute_type = self.actual_compute_type or compute_type
quantization = _normalize_quantization(actual_compute_type)
providers = ["CPUExecutionProvider"]
model = onnx_asr.load_model(
model=model_path,
quantization=quantization,
providers=["CPUExecutionProvider"],
providers=providers,
)
vad = onnx_asr.load_vad("silero", providers=["CPUExecutionProvider"])
vad = onnx_asr.load_vad("silero", providers=providers)
self._vad = vad
self._providers = providers
self._quantization = quantization
return model.with_vad(vad).with_timestamps()
def runtime_info(self) -> dict[str, str]:
"""Версии ONNX Runtime/onnx-asr и providers, заданные сессиям ASR и VAD.
Доступность provider в wheel не означает, что граф выполняется на нём:
сессии получают только явно заданный список.
"""
import onnxruntime
configured = ", ".join(self._providers)
return {
"engine": "onnx-asr",
"onnxruntime": package_version("onnxruntime"),
"onnx_asr": package_version("onnx-asr"),
"available_providers": ", ".join(onnxruntime.get_available_providers()),
"asr_providers": configured,
"vad_providers": configured,
"quantization": self._quantization or "float32",
}
def transcribe(
self,
model: Any,
@@ -19,6 +19,7 @@ from local_transcriber.types import (
Word,
WordTimestampsUnavailableError,
)
from local_transcriber.utils import package_version
# (model_alias, compute_type) → HF repo
MODEL_REPOS: dict[tuple[str, str], str] = {
@@ -203,6 +204,18 @@ class OpenVINOBackend:
words=words,
)
def runtime_info(self) -> dict[str, str]:
"""Версия OpenVINO, доступные устройства и выбранное фактически."""
from openvino import Core
return {
"engine": "openvino",
"openvino": package_version("openvino"),
"available_devices": ", ".join(Core().available_devices),
"device": self.actual_ov_device or "",
"compute_type": self.actual_compute_type or "",
}
def _resolve_repo(self, model_name: str, compute_type: str) -> tuple[str, str]:
"""Находит HF repo для пары (model, compute_type) с fallback.