Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
7.6 KiB
ADR-003: Pluggable backends и OpenVINO
Статус: Принято Дата: 2026-03-21 Обновлено: 2026-09-21
Контекст
На CPU (faster-whisper/CTranslate2) транскрипция работает медленно (~1.5x реалтайм для medium). CUDA доступна на малом проценте машин (ноутбуки с NVIDIA GPU), на офисных ПК её нет.
OpenVINO ускоряет inference на x86 CPU (Intel и AMD) в 2-4 раза. Для его поддержки нужен второй движок транскрипции, а архитектура должна позволять добавлять новые бэкенды (CoreML для Mac, AMD XDNA NPU) без переписывания существующего кода.
Решение
Backend Protocol (structural typing)
Минимальный интерфейс в backends/base.py:
class Backend(Protocol):
def ensure_model_available(self, model_name, compute_type, on_status) -> str: ...
def create_model(self, model_path, device, compute_type) -> Any: ...
def transcribe(self, model, file_path, language, on_segment, on_status) -> TranscribeResult: ...
Protocol вместо ABC — бэкенды не наследуются, достаточно реализовать методы. Соответствует стилю проекта (наследование нигде не используется).
Ленивые импорты
Бэкенды импортируются только при выборе — get_backend(device) делает import внутри.
CUDA bootstrap вызывается только при создании CUDA-модели (см. ADR-001).
Импорт openvino-genai загружает ~50MB shared libraries и не должен происходить,
если бэкенд не выбран.
Device как селектор бэкенда
Вместо отдельного --backend флага устройство само определяет бэкенд:
cuda,cpu→ FasterWhisperBackendopenvino→ OpenVINOBackendonnx→ OnnxAsrBackendauto→ ONNX на CPU независимо отnvidia-smiи установки extra
Transcriber — единственный владелец загрузки и исполнения
Обновлено 2026-09-21. Transcriber(ExecutionRequest) в transcriber.py
разрешает auto и device-aware умолчания, выполняет ensure_model_available +
create_model в одном месте и держит модель, adapter и фактическое исполнение
на весь запуск. CLI не вызывает get_backend и не хранит handles.
Внутри module различаются три вещи: поддерживаемая модель, движок
распознавания (faster-whisper, openvino, onnx-asr) и способ исполнения.
Значение device из CLI/TOML сохраняется как вход: get_backend выбирает
по нему adapter, adapter называет свой движок атрибутом engine, а
аппаратную часть (CPU, CUDA, Intel GPU, список providers) трактует сам.
Новое исполнение — новое значение device в get_backend и его понимание
в adapter'е, не новая иерархия adapters.
Cross-backend fallback
Fallback живёт внутри Transcriber, не в бэкендах и не в CLI:
- CUDA ошибка → CPU (FasterWhisper)
- OpenVINO ошибка → CPU (FasterWhisper)
- strict запуск (явный device из CLI или TOML) → ошибка без fallback
После перехода module сам держит новое состояние; следующий файл батча
использует его без участия caller. WordTimestampsUnavailableError
и ошибки пользовательских данных переходом не считаются.
Диагностика исполнения
Каждый adapter отдаёт runtime_info() — версии runtime и фактическую
конфигурацию (для ONNX — доступные providers и те, что заданы сессиям ASR
и VAD, квантизация, бюджет потоков). Transcriber включает их в
ExecutionInfo; CLI печатает их в --verbose. Наличие provider в wheel не
считается доказательством выполнения графа на GPU/NPU.
Аудио для OpenVINO
OpenVINO GenAI WhisperPipeline принимает raw PCM float массив, не путь к файлу.
Используем faster_whisper.decode_audio() (PyAV) → .tolist() → pipe.generate().
Системный ffmpeg не требуется — PyAV бандлит FFmpeg внутри wheel.
compute_type для OpenVINO
OpenVINO модели предквантизированы (int8/fp16), compute_type определяет какую модель скачать. Контракт:
- Явный
--compute-typeили значение из конфига — уважается всегда - Из дефолтов: для large-v3 автоматически выбирается fp16 (стабильнее по качеству)
- Несуществующая пара (model + compute_type) при явном выборе → ошибка
Зависимости бэкендов по умолчанию
faster-whisper, onnx-asr/onnxruntime и openvino-genai ставятся вместе. Модели
скачиваются только для активного бэкенда. cuBLAS подключается через extra cuda
для Linux/WSL x86_64 и Windows x64 (см. ADR-001). OpenVINO — conditional для x86_64/AMD64, кроме
macOS; ONNX обеспечивает автоматический CPU-путь на остальных платформах.
Последствия
- Обратная совместимость:
transcribe()сохранён;load_model()и_transcribe_file()удалены (2026-09-21) - Новый бэкенд добавляется одним файлом в
backends/+ регистрацией в__init__.py - Модели скачиваются по запросу — CUDA пользователь не качает OpenVINO модели, и наоборот
- ARM и macOS: OpenVINO не ставится (platform markers), auto использует ONNX
Отклонённые альтернативы
| Альтернатива | Почему отклонена |
|---|---|
OpenVINO как optional extra (pip install .[openvino]) |
Теряется zero-config UX; пользователь должен знать про extras |
| whisper.cpp (pywhispercpp) | Другой движок, больший объём интеграции; OpenVINO GenAI проще |
| Единый бэкенд с OpenVINO для всего | CTranslate2 лучше оптимизирован для CUDA; OpenVINO — для CPU |
| ABC вместо Protocol | Наследование не используется в проекте; Protocol проще |
| librosa для загрузки аудио в OpenVINO | Лишняя зависимость; для видеоконтейнеров ненадёжна без системного ffmpeg |
--backend как отдельный флаг |
Усложняет CLI; device уже однозначно определяет бэкенд |