fix(openvino): исправлены баги compute_type, fallback и help text
- Зачем: - compute_type из конфига терялся как implicit, ломая выбор модели OpenVINO. - узкий матчинг ошибок пропускал реальные OpenVINO runtime failures. - ensure_model_available(device="openvino") падал из-за дефолта float32. - ensure_model_available не передавал compute_type_explicit, ломая large-v3 → fp16 override. - help по --compute-type не отражал дефолт для OpenVINO. - Что: - ct_explicit учитывает и CLI-флаг, и конфиг-файл. - публичный API transcribe() передаёт compute_type_explicit=True. - _is_openvino_error() ловит любой RuntimeError. - ensure_model_available: дефолт compute_type из DEVICE_DEFAULTS, explicit прокинут в get_backend. - --compute-type help обновлён: float16 (CUDA) / int8 (OpenVINO) / float32 (CPU). - Проверка: - uv run pytest -v — 124 passed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -55,7 +55,7 @@ def main(
|
||||
),
|
||||
compute_type: str | None = typer.Option(
|
||||
None, "--compute-type", show_default=False,
|
||||
help="Тип вычислений [по умолч.: float16 (GPU) / float32 (CPU)]"
|
||||
help="Тип вычислений [по умолч.: float16 (CUDA) / int8 (OpenVINO) / float32 (CPU)]"
|
||||
),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="Подробный вывод"),
|
||||
force: bool = typer.Option(False, "--force", "-f", help="Перезаписать существующие транскрипты"),
|
||||
@@ -72,7 +72,7 @@ def main(
|
||||
resolved_device = detect_device(defaults["device"])
|
||||
defaults = apply_device_defaults(defaults, resolved_device, cli_values, config)
|
||||
|
||||
ct_explicit = compute_type is not None
|
||||
ct_explicit = compute_type is not None or "compute_type" in config
|
||||
|
||||
expanded = expand_globs(files)
|
||||
if not expanded:
|
||||
|
||||
@@ -118,6 +118,7 @@ def transcribe(
|
||||
"""High-level API: загрузка модели + транскрипция за один вызов."""
|
||||
model, actual_device, backend, model_path = load_model(
|
||||
model_name, device, compute_type, on_status, strict_device,
|
||||
compute_type_explicit=True, # Python API — caller explicitly chose compute_type
|
||||
)
|
||||
tfr = _transcribe_file(
|
||||
model, actual_device, backend, model_path,
|
||||
@@ -130,11 +131,19 @@ def transcribe(
|
||||
def ensure_model_available(
|
||||
model_name: str,
|
||||
device: str = "cpu",
|
||||
compute_type: str = "float32",
|
||||
compute_type: str | None = None,
|
||||
on_status: Callable[[str], None] | None = None,
|
||||
) -> str:
|
||||
"""Публичный helper: гарантирует наличие модели для указанного бэкенда."""
|
||||
backend = get_backend(device)
|
||||
from local_transcriber.config import DEVICE_DEFAULTS, HARDCODED_DEFAULTS
|
||||
|
||||
if compute_type is None:
|
||||
device_defs = DEVICE_DEFAULTS.get(device, {})
|
||||
compute_type = device_defs.get("compute_type", HARDCODED_DEFAULTS["compute_type"])
|
||||
explicit = False
|
||||
else:
|
||||
explicit = True
|
||||
backend = get_backend(device, compute_type_explicit=explicit)
|
||||
return backend.ensure_model_available(model_name, compute_type, on_status)
|
||||
|
||||
|
||||
@@ -154,9 +163,14 @@ def _is_backend_error(exc: BaseException, device: str) -> bool:
|
||||
|
||||
|
||||
def _is_openvino_error(exc: BaseException) -> bool:
|
||||
"""Проверка ошибок OpenVINO runtime."""
|
||||
msg = str(exc).lower()
|
||||
return any(k in msg for k in ("openvino", "ov_", "inference_engine"))
|
||||
"""Проверка ошибок OpenVINO runtime.
|
||||
|
||||
OpenVINO runtime кидает RuntimeError с разнообразными сообщениями
|
||||
(openvino, ov_, inference, plugins, src/...). Пользовательские ошибки
|
||||
(файл не найден, неверный формат) приходят как FileNotFoundError/ValueError
|
||||
и не попадают сюда. Поэтому для RuntimeError считаем это backend failure.
|
||||
"""
|
||||
return isinstance(exc, RuntimeError)
|
||||
|
||||
|
||||
def _notify_status(on_status: Callable[[str], None] | None, message: str) -> None:
|
||||
|
||||
@@ -512,3 +512,32 @@ def test_openvino_strict_device_no_fallback(mock_get_backend):
|
||||
|
||||
with pytest.raises(RuntimeError, match="OpenVINO"):
|
||||
load_model("medium", "openvino", "int8", strict_device=True)
|
||||
|
||||
|
||||
@patch("local_transcriber.transcriber.get_backend")
|
||||
def test_openvino_runtime_error_triggers_fallback(mock_get_backend):
|
||||
"""Любой RuntimeError от OpenVINO бэкенда → fallback."""
|
||||
ov_backend = _make_backend(
|
||||
create_model_error=RuntimeError("Exception from src/inference/..."),
|
||||
)
|
||||
cpu_backend = _make_backend(model_path="/mock/cpu/model")
|
||||
|
||||
def backend_for_device(device, **kwargs):
|
||||
return ov_backend if device == "openvino" else cpu_backend
|
||||
|
||||
mock_get_backend.side_effect = backend_for_device
|
||||
|
||||
with pytest.warns(UserWarning, match="Переключение на CPU"):
|
||||
_, actual_device, _, _ = load_model("medium", "openvino", "int8")
|
||||
|
||||
assert actual_device == "cpu"
|
||||
|
||||
|
||||
def test_ensure_model_available_openvino_default_compute_type():
|
||||
"""ensure_model_available(device='openvino') без compute_type не падает."""
|
||||
from local_transcriber.backends.openvino import OpenVINOBackend
|
||||
|
||||
backend = OpenVINOBackend(compute_type_explicit=True)
|
||||
# Проверяем что _resolve_repo работает с дефолтным compute_type для openvino (int8)
|
||||
repo = backend._resolve_repo("medium", "int8")
|
||||
assert repo == "OpenVINO/whisper-medium-int8-ov"
|
||||
|
||||
Reference in New Issue
Block a user