refactor(transcriber): введена pluggable-архитектура бэкендов транскрипции

- Зачем:
  - подготовка к добавлению OpenVINO бэкенда для ускорения на x86 CPU без CUDA.
  - архитектура должна позволять добавлять новые бэкенды (CoreML, AMD XDNA) без переписывания кода.
- Что:
  - создан types.py с общими типами (Segment, TranscribeResult, TranscribeFileResult).
  - создан backends/base.py с Backend Protocol (3 метода: ensure_model_available, create_model, transcribe).
  - создан backends/faster_whisper.py — текущий код вынесен из transcriber.py в FasterWhisperBackend.
  - transcriber.py переделан в оркестратор: load_model() владеет полным пайплайном (ensure + create), CLI больше не вызывает ensure_model_available() отдельно.
  - TranscribeFileResult расширен полями backend и model_path для корректного cross-backend fallback в батч-режиме.
  - device_used проставляется оркестратором, а не бэкендом.
  - cli.py: вынесен _format_device_info(), подготовлен к openvino.
  - тесты обновлены: mock-точки перенесены с WhisperModel на get_backend/бэкенд-объекты.
- Проверка:
  - uv run pytest -v — 98 passed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-21 23:18:39 +03:00
co-authored by Claude Opus 4.6
parent 8632960354
commit 4c5399c2fc
9 changed files with 747 additions and 610 deletions
+46
View File
@@ -0,0 +1,46 @@
"""Протокол бэкенда транскрипции."""
from __future__ import annotations
from collections.abc import Callable
from pathlib import Path
from typing import Any, Protocol
from local_transcriber.types import Segment, TranscribeResult
class Backend(Protocol):
"""Минимальный интерфейс бэкенда транскрипции.
Бэкенды реализуют этот протокол (structural typing) —
наследование не требуется.
"""
def ensure_model_available(
self,
model_name: str,
compute_type: str,
on_status: Callable[[str], None] | None = None,
) -> str:
"""Гарантирует наличие модели, возвращает путь к файлам."""
...
def create_model(
self,
model_path: str,
device: str,
compute_type: str,
) -> Any:
"""Создаёт модель. Возвращает backend-специфичный объект."""
...
def transcribe(
self,
model: Any,
file_path: Path,
language: str | None,
on_segment: Callable[[Segment], None] | None = None,
on_status: Callable[[str], None] | None = None,
) -> TranscribeResult:
"""Транскрибирует файл, возвращает результат."""
...