Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
| """Shared evaluation runtime: device selection, ZeroGPU bypass, CPU thread hints.""" | |
| from __future__ import annotations | |
| import importlib.util | |
| import os | |
| import sys | |
| import types | |
| _cpu_thread_settings_applied = False | |
| _torchcodec_patch_applied = False | |
| def _force_transformers_torchcodec_unavailable() -> None: | |
| """Tell transformers to use librosa/soundfile for audio, not torchcodec.""" | |
| try: | |
| import transformers.utils.import_utils as import_utils | |
| import_utils._torchcodec_available = False | |
| def _is_torchcodec_available() -> bool: | |
| return False | |
| import_utils.is_torchcodec_available = _is_torchcodec_available | |
| except Exception: | |
| pass | |
| def disable_broken_torchcodec() -> None: | |
| """ | |
| Transformers prefers torchcodec for ``load_audio`` when the package is installed, | |
| but Hub Job images often lack FFmpeg shared libraries (libavutil.so.*). | |
| If torchcodec cannot load, mark it unavailable so transformers falls back to | |
| librosa / soundfile. | |
| """ | |
| global _torchcodec_patch_applied | |
| if _torchcodec_patch_applied: | |
| return | |
| _torchcodec_patch_applied = True | |
| force_off = os.environ.get("FFASR_DISABLE_TORCHCODEC", "").strip().lower() in ( | |
| "1", | |
| "true", | |
| "yes", | |
| "on", | |
| ) | |
| if force_off or importlib.util.find_spec("torchcodec") is None: | |
| if force_off and importlib.util.find_spec("torchcodec") is not None: | |
| print( | |
| "[ffasr] FFASR_DISABLE_TORCHCODEC=1: using librosa/soundfile for audio " | |
| "(torchcodec disabled on Hub Jobs).", | |
| flush=True, | |
| ) | |
| _force_transformers_torchcodec_unavailable() | |
| return | |
| try: | |
| from torchcodec.decoders import AudioDecoder # noqa: F401 | |
| except (OSError, RuntimeError, ImportError, AttributeError): | |
| print( | |
| "[ffasr] torchcodec native libraries unavailable; " | |
| "transformers will use librosa/soundfile for audio.", | |
| flush=True, | |
| ) | |
| _force_transformers_torchcodec_unavailable() | |
| stub = types.ModuleType("torchcodec") | |
| stub.__dict__["decoders"] = types.ModuleType("torchcodec.decoders") | |
| sys.modules["torchcodec"] = stub | |
| sys.modules["torchcodec.decoders"] = stub.__dict__["decoders"] | |
| except Exception: | |
| _force_transformers_torchcodec_unavailable() | |
| def patch_transformers_load_audio_for_paths() -> None: | |
| """ | |
| Route ``transformers.audio_utils.load_audio`` for file paths through soundfile | |
| (no torchcodec / FFmpeg). Safe to call before loading user custom scripts. | |
| """ | |
| try: | |
| import numpy as np | |
| import transformers.audio_utils as audio_utils | |
| from backends._audio_utils import load_wav_mono | |
| _orig = audio_utils.load_audio | |
| def _load_audio(audio, sampling_rate=16000, timeout=None): | |
| if isinstance(audio, str): | |
| return load_wav_mono(audio, sampling_rate=int(sampling_rate)) | |
| if isinstance(audio, np.ndarray): | |
| return audio | |
| return _orig(audio, sampling_rate=sampling_rate, timeout=timeout) | |
| audio_utils.load_audio = _load_audio | |
| except Exception: | |
| pass | |
| def zerogpu_disabled() -> bool: | |
| """When True, never wrap evaluation in ``spaces.GPU`` even if ``spaces`` is installed.""" | |
| return os.environ.get("FFASR_DISABLE_ZEROGPU", "").strip().lower() in ( | |
| "1", | |
| "true", | |
| "yes", | |
| "on", | |
| ) | |
| def spaces_available() -> bool: | |
| return importlib.util.find_spec("spaces") is not None | |
| def use_spaces_gpu_decorator() -> bool: | |
| """Use ZeroGPU decorator only when ``spaces`` is present and not explicitly disabled.""" | |
| return spaces_available() and not zerogpu_disabled() | |
| def resolve_eval_devices() -> tuple[str, int]: | |
| """ | |
| Return ``(device_str, device_int)`` for backends / HF pipeline. | |
| ``FFASR_DEVICE`` (default ``auto``): ``cpu`` | ``cuda`` | ``auto``. | |
| ``auto`` uses CUDA when available, else CPU. | |
| """ | |
| choice = (os.environ.get("FFASR_DEVICE", "auto") or "auto").strip().lower() | |
| if choice not in ("auto", "cpu", "cuda"): | |
| choice = "auto" | |
| import torch | |
| has_cuda = torch.cuda.is_available() | |
| if choice == "cpu": | |
| return "cpu", -1 | |
| if choice == "cuda": | |
| if not has_cuda: | |
| raise RuntimeError("FFASR_DEVICE=cuda but torch.cuda.is_available() is False.") | |
| return "cuda", 0 | |
| # auto | |
| if has_cuda: | |
| return "cuda", 0 | |
| return "cpu", -1 | |
| def apply_cpu_thread_settings_once() -> None: | |
| """ | |
| Apply optional torch thread limits from env (once per process). | |
| - ``FFASR_TORCH_NUM_THREADS`` — passed to ``torch.set_num_threads`` if a positive int. | |
| - ``FFASR_TORCH_NUM_INTEROP_THREADS`` — ``torch.set_num_interop_threads`` if set. | |
| For BLAS/OpenMP, operators typically set ``OMP_NUM_THREADS`` / ``MKL_NUM_THREADS`` | |
| in the Space environment (see docs); those are read by the native libs at load time. | |
| """ | |
| global _cpu_thread_settings_applied | |
| if _cpu_thread_settings_applied: | |
| return | |
| _cpu_thread_settings_applied = True | |
| import torch | |
| raw = os.environ.get("FFASR_TORCH_NUM_THREADS", "").strip() | |
| if raw.isdigit(): | |
| n = int(raw) | |
| if n > 0: | |
| try: | |
| torch.set_num_threads(n) | |
| except Exception: | |
| pass | |
| raw_i = os.environ.get("FFASR_TORCH_NUM_INTEROP_THREADS", "").strip() | |
| if raw_i.isdigit(): | |
| ni = int(raw_i) | |
| if ni > 0: | |
| try: | |
| torch.set_num_interop_threads(ni) | |
| except Exception: | |
| pass | |