diff --git a/tests/tools/test_transcription_tools.py b/tests/tools/test_transcription_tools.py index 2a69c07433..7db1f3c730 100644 --- a/tests/tools/test_transcription_tools.py +++ b/tests/tools/test_transcription_tools.py @@ -351,6 +351,10 @@ class TestTranscribeLocalCommand: # _transcribe_local — additional tests # ============================================================================ +@pytest.mark.skipif( + not __import__("importlib").util.find_spec("faster_whisper"), + reason="faster_whisper not installed", +) class TestLocalModelLoading: def test_cached_model_load_never_uses_online_resolution(self): cached_model = object() @@ -391,6 +395,19 @@ class TestLocalModelLoading: call("base", local_files_only=False, device="auto", compute_type="auto"), ] + def test_partial_cache_is_treated_as_a_cache_miss(self): + # An interrupted first download leaves refs/main + a snapshot without model.bin; + # snapshot_download(local_files_only=True) returns that folder and ctranslate2 + # raises RuntimeError, so the online path must still run. + from tools.transcription_local import _create_whisper_model + + downloaded_model = object() + side_effect = [RuntimeError("Unable to open file 'model.bin' in model '/cache/snap'"), downloaded_model] + with patch("faster_whisper.WhisperModel", side_effect=side_effect) as model_cls: + assert _create_whisper_model("base", device="cpu", compute_type="int8") is downloaded_model + + assert [c.kwargs["local_files_only"] for c in model_cls.call_args_list] == [True, False] + @pytest.mark.skipif( not __import__("importlib").util.find_spec("faster_whisper"), diff --git a/tools/transcription_local.py b/tools/transcription_local.py index db444f83bd..e2699469c9 100644 --- a/tools/transcription_local.py +++ b/tools/transcription_local.py @@ -140,7 +140,11 @@ def _create_whisper_model(model_name: str, *, device: str, compute_type: str): kwargs = {"device": device, "compute_type": compute_type} try: return WhisperModel(model_name, local_files_only=True, **kwargs) - except _hub_cache_miss_error(): + except (_hub_cache_miss_error(), RuntimeError) as exc: + # An interrupted first download leaves a snapshot folder without the weights; + # snapshot_download still returns it and ctranslate2 raises "Unable to open file". + if isinstance(exc, RuntimeError) and "Unable to open file" not in str(exc): + raise logger.info("faster-whisper model '%s' is not cached; downloading it from the Hugging Face Hub", model_name) # huggingface_hub surfaces every Hub/network failure as an OSError subclass