feat(config): добавлены device-aware дефолты и результаты тестов
- Зачем: - тестирование на реальных записях показало, что int8 даёт галлюцинации на длинных файлах, auto-detect языка ошибается — нужны оптимальные дефолты по устройству. - Что: - дефолты: medium float16 (GPU), medium float32 (CPU), language=ru. - добавлены DEVICE_DEFAULTS и apply_device_defaults() в config.py. - убран preprocessor_config.json из обязательных файлов модели (отсутствует у medium). - README обновлён: таблицы скоростей, качества, результаты тестирования compute_type. - Проверка: - uv run pytest — 98 passed, 1 skipped. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+26
-26
@@ -43,7 +43,7 @@ def _single_patches(result=None, tmp_file=None, actual_device="cpu"):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", return_value=tmp_file),
|
||||
patch("local_transcriber.cli.detect_device", return_value=actual_device),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model, actual_device)),
|
||||
patch("local_transcriber.cli._transcribe_file", return_value=tfr),
|
||||
patch("local_transcriber.cli.write_transcript"),
|
||||
@@ -74,7 +74,7 @@ def test_cli_default_options_passed_to_transcribe(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", return_value=audio),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cpu"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model, "cpu")),
|
||||
patch("local_transcriber.cli._transcribe_file", mock_transcribe_file),
|
||||
patch("local_transcriber.cli.write_transcript"),
|
||||
@@ -82,9 +82,9 @@ def test_cli_default_options_passed_to_transcribe(tmp_path):
|
||||
runner.invoke(app, [str(audio)])
|
||||
|
||||
call_kwargs = mock_transcribe_file.call_args[1]
|
||||
assert call_kwargs["model_name"] == "/models/large-v3"
|
||||
assert call_kwargs["compute_type"] == "int8"
|
||||
assert call_kwargs["language"] is None # "auto" → None
|
||||
assert call_kwargs["model_name"] == "/models/medium"
|
||||
assert call_kwargs["compute_type"] == "float32"
|
||||
assert call_kwargs["language"] == "ru"
|
||||
assert call_kwargs["on_segment"] is None # verbose=False
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ def test_cli_verbose_passes_on_segment_callback(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", return_value=audio),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cpu"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model, "cpu")),
|
||||
patch("local_transcriber.cli._transcribe_file", mock_transcribe_file),
|
||||
patch("local_transcriber.cli.write_transcript"),
|
||||
@@ -173,7 +173,7 @@ def test_cli_default_output_path(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", return_value=audio),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cpu"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model, "cpu")),
|
||||
patch("local_transcriber.cli._transcribe_file", return_value=tfr),
|
||||
patch("local_transcriber.cli.write_transcript", mock_write),
|
||||
@@ -199,7 +199,7 @@ def test_cli_custom_output_path(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", return_value=audio),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cpu"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model, "cpu")),
|
||||
patch("local_transcriber.cli._transcribe_file", return_value=tfr),
|
||||
patch("local_transcriber.cli.write_transcript", mock_write),
|
||||
@@ -236,7 +236,7 @@ def test_cli_passes_status_callback_to_transcribe(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", return_value=audio),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cpu"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model, "cpu")),
|
||||
patch("local_transcriber.cli._transcribe_file", mock_transcribe_file),
|
||||
patch("local_transcriber.cli.write_transcript"),
|
||||
@@ -284,7 +284,7 @@ def test_cli_windows_cuda_diagnostic(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", return_value=audio),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cuda"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model, "cuda")),
|
||||
patch("local_transcriber.cli._transcribe_file", side_effect=RuntimeError("CUDA error: no device")),
|
||||
patch("local_transcriber.cli.sys") as mock_sys,
|
||||
@@ -308,7 +308,7 @@ def test_cli_linux_cuda_error_no_windows_hint(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", return_value=audio),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cuda"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model, "cuda")),
|
||||
patch("local_transcriber.cli._transcribe_file", side_effect=RuntimeError("CUDA error: no device")),
|
||||
patch("local_transcriber.cli.sys") as mock_sys,
|
||||
@@ -333,7 +333,7 @@ def test_cli_device_fallback_warning(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", return_value=audio),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cuda"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model, "cuda")),
|
||||
patch("local_transcriber.cli._transcribe_file", return_value=tfr),
|
||||
patch("local_transcriber.cli.write_transcript"),
|
||||
@@ -357,7 +357,7 @@ def test_cli_strict_device_passed_to_transcribe(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", return_value=audio),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cuda"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model, "cuda")),
|
||||
patch("local_transcriber.cli._transcribe_file", mock_transcribe_file),
|
||||
patch("local_transcriber.cli.write_transcript"),
|
||||
@@ -377,7 +377,7 @@ def test_cli_strict_device_passed_to_transcribe(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", return_value=audio),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cpu"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model, "cpu")),
|
||||
patch("local_transcriber.cli._transcribe_file", mock_transcribe_file),
|
||||
patch("local_transcriber.cli.write_transcript"),
|
||||
@@ -398,7 +398,7 @@ def test_cli_keyboard_interrupt(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", return_value=audio),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cpu"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model, "cpu")),
|
||||
patch("local_transcriber.cli._transcribe_file", side_effect=KeyboardInterrupt),
|
||||
patch("local_transcriber.cli.write_transcript"),
|
||||
@@ -432,7 +432,7 @@ def test_cli_unexpected_error_verbose_traceback(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", return_value=audio),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cpu"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model, "cpu")),
|
||||
patch("local_transcriber.cli._transcribe_file", side_effect=RuntimeError("unexpected boom")),
|
||||
patch("local_transcriber.cli.write_transcript"),
|
||||
@@ -454,7 +454,7 @@ def test_cli_unexpected_error_no_verbose_hint(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", return_value=audio),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cpu"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model, "cpu")),
|
||||
patch("local_transcriber.cli._transcribe_file", side_effect=RuntimeError("unexpected boom")),
|
||||
patch("local_transcriber.cli.write_transcript"),
|
||||
@@ -484,7 +484,7 @@ def test_cli_batch_two_files(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", side_effect=lambda p: p),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cpu"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model, "cpu")),
|
||||
patch("local_transcriber.cli._transcribe_file", return_value=tfr),
|
||||
patch("local_transcriber.cli.write_transcript"),
|
||||
@@ -512,7 +512,7 @@ def test_cli_batch_skips_existing(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", side_effect=lambda p: p),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cpu"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model, "cpu")),
|
||||
patch("local_transcriber.cli._transcribe_file", return_value=tfr),
|
||||
patch("local_transcriber.cli.write_transcript"),
|
||||
@@ -563,7 +563,7 @@ def test_cli_batch_force_overwrites(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", side_effect=lambda p: p),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cpu"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model, "cpu")),
|
||||
patch("local_transcriber.cli._transcribe_file", return_value=tfr),
|
||||
patch("local_transcriber.cli.write_transcript"),
|
||||
@@ -598,7 +598,7 @@ def test_cli_batch_per_file_error(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", side_effect=lambda p: p),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cpu"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model, "cpu")),
|
||||
patch("local_transcriber.cli._transcribe_file", side_effect=transcribe_side_effect),
|
||||
patch("local_transcriber.cli.write_transcript"),
|
||||
@@ -630,7 +630,7 @@ def test_cli_batch_invalid_in_prescan(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", side_effect=validate_side_effect),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cpu"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model, "cpu")),
|
||||
patch("local_transcriber.cli._transcribe_file", return_value=tfr),
|
||||
patch("local_transcriber.cli.write_transcript"),
|
||||
@@ -725,7 +725,7 @@ def test_cli_batch_fallback_warning(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", side_effect=lambda p: p),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cuda"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model, "cpu")),
|
||||
patch("local_transcriber.cli._transcribe_file", return_value=tfr),
|
||||
patch("local_transcriber.cli.write_transcript"),
|
||||
@@ -753,7 +753,7 @@ def test_cli_batch_empty_speech_warning(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", side_effect=lambda p: p),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cpu"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model, "cpu")),
|
||||
patch("local_transcriber.cli._transcribe_file", side_effect=[tfr_empty, tfr_ok]),
|
||||
patch("local_transcriber.cli.write_transcript"),
|
||||
@@ -784,7 +784,7 @@ def test_cli_batch_midstream_fallback_warning(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", side_effect=lambda p: p),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cuda"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", return_value=(model_gpu, "cuda")),
|
||||
patch("local_transcriber.cli._transcribe_file", side_effect=[tfr_fallback, tfr_ok]),
|
||||
patch("local_transcriber.cli.write_transcript"),
|
||||
@@ -811,7 +811,7 @@ def test_cli_batch_model_loaded_once(tmp_path):
|
||||
patch("local_transcriber.cli.check_ffmpeg"),
|
||||
patch("local_transcriber.cli.validate_input_file", side_effect=lambda p: p),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cpu"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/large-v3"),
|
||||
patch("local_transcriber.cli.ensure_model_available", return_value="/models/medium"),
|
||||
patch("local_transcriber.cli.load_model", mock_load_model),
|
||||
patch("local_transcriber.cli._transcribe_file", return_value=tfr),
|
||||
patch("local_transcriber.cli.write_transcript"),
|
||||
|
||||
+37
-3
@@ -4,6 +4,7 @@ from unittest.mock import patch
|
||||
import pytest
|
||||
|
||||
from local_transcriber.config import (
|
||||
apply_device_defaults,
|
||||
find_config_file,
|
||||
load_config,
|
||||
resolve_defaults,
|
||||
@@ -89,8 +90,41 @@ def test_resolve_defaults_hardcoded_fallback():
|
||||
{"model": None, "language": None, "device": None, "compute_type": None}, {}
|
||||
)
|
||||
assert result == {
|
||||
"model": "large-v3",
|
||||
"language": "auto",
|
||||
"model": "medium",
|
||||
"language": "ru",
|
||||
"device": "auto",
|
||||
"compute_type": "int8",
|
||||
"compute_type": "float32",
|
||||
}
|
||||
|
||||
|
||||
def test_apply_device_defaults_cuda():
|
||||
defaults = {"model": "medium", "language": "ru", "device": "auto", "compute_type": "float32"}
|
||||
cli = {"model": None, "language": None, "device": None, "compute_type": None}
|
||||
result = apply_device_defaults(defaults, "cuda", cli, {})
|
||||
assert result["model"] == "medium"
|
||||
assert result["compute_type"] == "float16"
|
||||
|
||||
|
||||
def test_apply_device_defaults_cpu():
|
||||
defaults = {"model": "medium", "language": "ru", "device": "auto", "compute_type": "float32"}
|
||||
cli = {"model": None, "language": None, "device": None, "compute_type": None}
|
||||
result = apply_device_defaults(defaults, "cpu", cli, {})
|
||||
assert result["model"] == "medium"
|
||||
assert result["compute_type"] == "float32"
|
||||
|
||||
|
||||
def test_apply_device_defaults_cli_overrides():
|
||||
defaults = {"model": "large-v3", "language": "ru", "device": "auto", "compute_type": "int8"}
|
||||
cli = {"model": "large-v3", "language": None, "device": None, "compute_type": "int8"}
|
||||
result = apply_device_defaults(defaults, "cuda", cli, {})
|
||||
assert result["model"] == "large-v3"
|
||||
assert result["compute_type"] == "int8"
|
||||
|
||||
|
||||
def test_apply_device_defaults_config_overrides():
|
||||
defaults = {"model": "small", "language": "ru", "device": "auto", "compute_type": "int8"}
|
||||
cli = {"model": None, "language": None, "device": None, "compute_type": None}
|
||||
config = {"model": "small", "compute_type": "int8"}
|
||||
result = apply_device_defaults(defaults, "cuda", cli, config)
|
||||
assert result["model"] == "small"
|
||||
assert result["compute_type"] == "int8"
|
||||
|
||||
Reference in New Issue
Block a user