# SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 import http.server import io import json import os import sys import threading import time import wave from pathlib import Path import numpy as np import pytest import core.inference.stt_ggml_sidecar as ggml_module from core.inference.stt_ggml_sidecar import ( DEFAULT_GGML_STT_MODEL, GGML_STT_MODELS, GGML_STT_REPOS, GgmlSttSidecar, SttEngineUnavailableError, find_whisper_server_binary, resolve_ggml_model_id, ) from core.inference.stt_sidecar import ( SttLanguageError, SttLoadCancelledError, SttModelIdError, SttModelNotDownloadedError, SttUnavailableError, ) @pytest.fixture(autouse = True) def isolate_runtime_and_stub_audio_decoder(monkeypatch, tmp_path): """Unit tests exercise orchestration, not PyAV container parsing.""" monkeypatch.setenv("UNSLOTH_STUDIO_HOME", str(tmp_path / "studio")) monkeypatch.delenv("WHISPER_SERVER_PATH", raising = False) monkeypatch.delenv("UNSLOTH_WHISPER_CPP_PATH", raising = False) monkeypatch.setenv("PATH", "") monkeypatch.setattr( ggml_module, "_decode_audio_bounded", lambda audio: np.zeros(16000, dtype = np.float32), ) # --------------------------------------------------------------------------- # Model id resolution # --------------------------------------------------------------------------- def test_curated_ids_resolve(): for model_id in GGML_STT_MODELS: assert resolve_ggml_model_id(model_id) == model_id def test_default_model_resolves_from_none_and_blank(): assert resolve_ggml_model_id(None) == DEFAULT_GGML_STT_MODEL assert resolve_ggml_model_id(" ") == DEFAULT_GGML_STT_MODEL def test_custom_repo_ids_are_rejected(): with pytest.raises(SttModelIdError): resolve_ggml_model_id("owner/model") with pytest.raises(SttModelIdError): resolve_ggml_model_id("large-v2") def test_curated_ids_mirror_transformers_sidecar(): from core.inference.stt_sidecar import STT_MODELS assert list(GGML_STT_MODELS.keys()) == list(STT_MODELS.keys()) def test_curated_filenames_match_repo_naming(): # unslothai/whisper--GGUF hosts whisper-.bin; keep the download # filename in lockstep with the repo so it resolves instead of 404ing. for model_id, repo in GGML_STT_REPOS.items(): expected = repo.split("/", 1)[1].removesuffix("-GGUF") + ".bin" assert GGML_STT_MODELS[model_id] == expected # --------------------------------------------------------------------------- # Binary discovery # --------------------------------------------------------------------------- def test_env_binary_override_wins(monkeypatch, tmp_path): binary = tmp_path / "whisper-server" binary.write_text("#!/bin/sh\n") binary.chmod(0o755) # find_whisper_server_binary requires an executable monkeypatch.setenv("WHISPER_SERVER_PATH", str(binary)) assert find_whisper_server_binary() == str(binary) def test_env_dir_override_scans_layouts(monkeypatch, tmp_path): monkeypatch.delenv("WHISPER_SERVER_PATH", raising = False) build_bin = tmp_path / "build" / "bin" build_bin.mkdir(parents = True) binary = build_bin / "whisper-server" binary.write_text("#!/bin/sh\n") binary.chmod(0o755) # find_whisper_server_binary requires an executable monkeypatch.setenv("UNSLOTH_WHISPER_CPP_PATH", str(tmp_path)) assert find_whisper_server_binary() == str(binary) def test_missing_binary_reports_unavailable(monkeypatch, tmp_path): monkeypatch.delenv("WHISPER_SERVER_PATH", raising = False) monkeypatch.setenv("UNSLOTH_WHISPER_CPP_PATH", str(tmp_path / "nope")) monkeypatch.setattr(ggml_module, "_managed_whisper_cpp_dir", lambda: tmp_path / "gone") monkeypatch.setattr(ggml_module.shutil, "which", lambda name: None) assert find_whisper_server_binary() is None assert not ggml_module.is_available() with pytest.raises(SttEngineUnavailableError): ggml_module.ensure_engine_available() def test_non_executable_binary_is_not_runnable(monkeypatch, tmp_path): if sys.platform == "win32": pytest.skip("X_OK is an existence check on Windows") binary = tmp_path / "whisper-server" binary.write_text("#!/bin/sh\n") # written but not chmod +x monkeypatch.setenv("WHISPER_SERVER_PATH", str(binary)) monkeypatch.setattr(ggml_module.shutil, "which", lambda name: None) assert find_whisper_server_binary() is None # --------------------------------------------------------------------------- # Slim-install launch guard # --------------------------------------------------------------------------- def _slim_install( tmp_path, *, install_kind = "slim", with_ggml = True, linked_libraries = None, backend = "cpu", linked_runtime_directories = None, runtime_wiring_version = None, ) -> str: """A managed-looking install tree: marker at the root, server in build/bin.""" install_dir = tmp_path / "whisper.cpp" bin_dir = install_dir / "build" / "bin" bin_dir.mkdir(parents = True) binary = bin_dir / "whisper-server" binary.write_text("#!/bin/sh\n") binary.chmod(0o755) marker: dict = { "schema_version": 1, "component": "whisper.cpp", "release_tag": "v1.9.1-unsloth.1", "backend": backend, "paired_llama_tag": "b10069-mix-fb3d4ca", } if install_kind is not None: marker["install_kind"] = install_kind if linked_libraries is not None: marker["linked_libraries"] = linked_libraries if linked_runtime_directories is not None: marker["linked_runtime_directories"] = linked_runtime_directories for name in linked_runtime_directories: catalog = bin_dir / name catalog.mkdir() (catalog / "kernel.dat").write_bytes(b"kernel") if runtime_wiring_version is not None: marker["runtime_wiring_version"] = runtime_wiring_version (install_dir / "UNSLOTH_WHISPER_PREBUILT_INFO.json").write_text(json.dumps(marker)) if with_ggml: names = ( ("ggml.dll", "ggml-base.dll") if sys.platform == "win32" else ("libggml.so.0", "libggml-base.so.0") ) for name in names: (bin_dir / name).write_bytes(b"ggml") return str(binary) def test_slim_guard_flags_missing_ggml_links(monkeypatch, tmp_path): # A slim marker whose linked ggml runtime is gone must read as engine # unavailable (reinstall), never crash into a server launch. binary = _slim_install(tmp_path, with_ggml = False) assert ggml_module.slim_runtime_intact(binary) is False monkeypatch.setattr(ggml_module, "find_whisper_server_binary", lambda: binary) assert not ggml_module.is_available() with pytest.raises(SttEngineUnavailableError, match = "ggml"): ggml_module.ensure_engine_available() def test_slim_guard_passes_with_links_in_place(monkeypatch, tmp_path): names = ["libggml.so.0", "libggml-base.so.0"] binary = _slim_install(tmp_path, with_ggml = True, linked_libraries = names) assert ggml_module.slim_runtime_intact(binary) is True monkeypatch.setattr(ggml_module, "find_whisper_server_binary", lambda: binary) assert ggml_module.ensure_engine_available() == binary def test_slim_guard_verifies_the_marker_linked_libraries(monkeypatch, tmp_path): # New markers record the exact wired filenames; one missing name flips the # install to unavailable even when the legacy core ggml names are present. names = ["libggml.dylib", "libggml-base.dylib", "libggml-metal.dylib"] binary = _slim_install(tmp_path, with_ggml = True, linked_libraries = names) bin_dir = Path(binary).parent for name in names[:-1]: (bin_dir / name).write_bytes(b"ggml") assert ggml_module.slim_runtime_intact(binary) is False # metal dylib absent (bin_dir / names[-1]).write_bytes(b"ggml") assert ggml_module.slim_runtime_intact(binary) is True monkeypatch.setattr(ggml_module, "find_whisper_server_binary", lambda: binary) assert ggml_module.ensure_engine_available() == binary def test_slim_guard_malformed_authoritative_marker_fails_closed(tmp_path): for bad in ("not-a-list", [], [1, 2]): root = tmp_path / f"case_{type(bad).__name__}_{len(str(bad))}" root.mkdir() binary = _slim_install(root, with_ggml = True, linked_libraries = bad) assert ggml_module.slim_runtime_intact(binary) is False def test_slim_guard_prefers_authoritative_root_marker(tmp_path): names = ["libggml.so.0", "libggml-base.so.0"] binary = _slim_install(tmp_path, with_ggml = True, linked_libraries = names) packaging_marker = Path(binary).parent / "UNSLOTH_WHISPER_PREBUILT_INFO.json" packaging_marker.write_text(json.dumps({"backend": "slim", "release_tag": "packaging"})) assert ggml_module._whisper_install_marker(binary)["install_kind"] == "slim" assert ggml_module.slim_runtime_intact(binary) is True def test_slim_guard_rejects_invalid_root_even_with_inner_marker(tmp_path): binary = _slim_install(tmp_path, with_ggml = True, linked_libraries = ["libggml.so.0"]) root_marker = Path(binary).parents[2] / "UNSLOTH_WHISPER_PREBUILT_INFO.json" root_marker.write_text("not json") (Path(binary).parent / root_marker.name).write_text(json.dumps({"backend": "slim"})) assert ggml_module.slim_runtime_intact(binary) is False def test_slim_guard_rejects_missing_rocm_catalog(tmp_path): names = ["libggml.so.0", "libggml-base.so.0", "libggml-hip.so"] binary = _slim_install( tmp_path, linked_libraries = names, backend = "rocm", linked_runtime_directories = ["hipblaslt", "rocblas"], runtime_wiring_version = 2, ) bin_dir = Path(binary).parent (bin_dir / "libggml-hip.so").write_bytes(b"ggml") assert ggml_module.slim_runtime_intact(binary) is True (bin_dir / "rocblas" / "kernel.dat").unlink() assert ggml_module.slim_runtime_intact(binary) is False def test_slim_guard_accepts_windows_rocm_dll_overlay(monkeypatch, tmp_path): monkeypatch.setattr(ggml_module.sys, "platform", "win32") names = ["ggml.dll", "ggml-base.dll", "ggml-hip.dll", "amdhip64.dll"] binary = _slim_install( tmp_path, linked_libraries = names, backend = "rocm", linked_runtime_directories = [], runtime_wiring_version = 2, ) for name in names: (Path(binary).parent / name).write_bytes(b"dll") assert ggml_module.slim_runtime_intact(binary) is True def test_slim_guard_ignores_fat_and_markerless_installs(tmp_path): # Fat installs carry their own ggml; no marker means source/custom build. fat = _slim_install(tmp_path / "fat", install_kind = None, with_ggml = False) assert ggml_module.slim_runtime_intact(fat) is True bare = tmp_path / "bare" / "whisper-server" bare.parent.mkdir(parents = True) bare.write_text("#!/bin/sh\n") assert ggml_module.slim_runtime_intact(str(bare)) is True # --------------------------------------------------------------------------- # whisper-server child-process environment # --------------------------------------------------------------------------- def _loader_path_var() -> str: return {"win32": "PATH", "darwin": "DYLD_LIBRARY_PATH"}.get(sys.platform, "LD_LIBRARY_PATH") def test_child_env_scrubs_secrets_and_adds_lib_dir(monkeypatch, tmp_path): monkeypatch.setenv("HF_TOKEN", "secret-token") # exact name monkeypatch.setenv("MY_API_KEY", "nope") # marker substring monkeypatch.setenv("HTTPS_PROXY", "http://u:p@px:8080") # url-name monkeypatch.setenv("SOME_REMOTE", "https://u:pw@host/repo") # url-userinfo value monkeypatch.setenv("STT_KEEPME", "keep") # benign binary = tmp_path / "whisper-server" binary.write_text("#!/bin/sh\n") env = ggml_module._whisper_server_child_env(str(binary)) for scrubbed in ("HF_TOKEN", "MY_API_KEY", "HTTPS_PROXY", "SOME_REMOTE"): assert scrubbed not in env assert env.get("STT_KEEPME") == "keep" assert str(tmp_path.resolve()) in env[_loader_path_var()].split(os.pathsep) def test_child_env_isolates_home_and_cred_locations(monkeypatch, tmp_path): # The downloaded server must not see the real home (token caches live # there) nor explicit cred-store pointers like HF_HOME / NETRC. monkeypatch.setenv("HOME", "/real/home") monkeypatch.setenv("HF_HOME", "/real/hf") monkeypatch.setenv("NETRC", "/real/.netrc") monkeypatch.setattr(ggml_module, "_managed_whisper_cpp_dir", lambda: tmp_path / "managed") binary = tmp_path / "whisper-server" binary.write_text("#!/bin/sh\n") env = ggml_module._whisper_server_child_env(str(binary)) assert env["HOME"] == str(tmp_path / "managed" / ".child_home") assert "HF_HOME" not in env assert "NETRC" not in env assert (tmp_path / "managed" / ".child_home").is_dir() def test_child_env_wsl_rocm_prepends_system_hip(monkeypatch, tmp_path): if sys.platform != "linux": pytest.skip("WSL ROCm library precedence is Linux-only") rocm = tmp_path / "rocm-lib" rocm.mkdir() bindir = tmp_path / "bin" bindir.mkdir() binary = bindir / "whisper-server" binary.write_text("#!/bin/sh\n") monkeypatch.setattr(ggml_module, "_wsl_system_rocm_lib_dirs", lambda: [str(rocm)]) env = ggml_module._whisper_server_child_env(str(binary)) parts = env["LD_LIBRARY_PATH"].split(os.pathsep) assert parts[0] == str(rocm.resolve()) # system HIP wins assert str(bindir.resolve()) in parts # bundle libs still present assert env.get("HSA_ENABLE_DXG_DETECTION") == "1" def test_child_env_adds_cuda_runtime_dirs_for_cuda_bundle(monkeypatch, tmp_path): # Versioned CUDA backend modules are valid too. They still need the # CUDA-from-PyTorch wheel dirs for libcudart/libcublas at launch. if sys.platform == "darwin": pytest.skip("no CUDA on macOS") import utils.prebuilt.runtime_libs as rl bindir = tmp_path / "bin" bindir.mkdir() (bindir / "whisper-server").write_text("#!/bin/sh\n") module_name = "ggml-cuda.dll" if sys.platform == "win32" else "libggml-cuda.so.0" (bindir / module_name).write_text("") cuda_dir = tmp_path / "nvidia" / "cuda_runtime" / "lib" cuda_dir.mkdir(parents = True) monkeypatch.setattr(rl, "python_runtime_dirs", lambda: [str(cuda_dir)]) env = ggml_module._whisper_server_child_env(str(bindir / "whisper-server")) parts = env[_loader_path_var()].split(os.pathsep) assert str(bindir.resolve()) in parts assert str(cuda_dir.resolve()) in parts assert parts.index(str(bindir.resolve())) < parts.index(str(cuda_dir.resolve())) def test_child_env_omits_cuda_runtime_dirs_for_cpu_bundle(monkeypatch, tmp_path): # No libggml-cuda.so beside the binary -> a static CPU/Metal bundle -> the CUDA # wheel discovery must not run and must not touch the loader path. if sys.platform == "darwin": pytest.skip("no CUDA on macOS") import utils.prebuilt.runtime_libs as rl bindir = tmp_path / "bin" bindir.mkdir() (bindir / "whisper-server").write_text("#!/bin/sh\n") cuda_dir = tmp_path / "nvidia" / "cuda_runtime" / "lib" cuda_dir.mkdir(parents = True) called = {"n": 0} def _fake_dirs(): called["n"] += 1 return [str(cuda_dir)] monkeypatch.setattr(rl, "python_runtime_dirs", _fake_dirs) env = ggml_module._whisper_server_child_env(str(bindir / "whisper-server")) parts = env[_loader_path_var()].split(os.pathsep) assert str(cuda_dir.resolve()) not in parts assert called["n"] == 0 def test_engine_unavailable_is_stt_unavailable(): # Routes map SttUnavailableError to HTTP 501; the engine error must share it. assert issubclass(SttEngineUnavailableError, SttUnavailableError) # --------------------------------------------------------------------------- # WAV packaging # --------------------------------------------------------------------------- def test_pcm_to_wav_bytes_shape_and_rate(): pcm = np.zeros(3200, dtype = np.float32) data = ggml_module._pcm_to_wav_bytes(pcm) with wave.open(io.BytesIO(data)) as w: assert w.getnchannels() == 1 assert w.getsampwidth() == 2 assert w.getframerate() == 16000 assert w.getnframes() == 3200 def test_pcm_to_wav_bytes_clips_out_of_range(): pcm = np.array([2.0, -2.0], dtype = np.float32) data = ggml_module._pcm_to_wav_bytes(pcm) with wave.open(io.BytesIO(data)) as w: frames = np.frombuffer(w.readframes(2), dtype = "= {"downloading", "model", "error"}