Files
aiturk-hermes-ide/tests/tools/test_windows_native_runtime.py
T
yilsemandClaude Opus 5 233885d3a7 fix(desktop): ship the VC++ runtime the Windows voice wheels need
onnxruntime (wake word) and ctranslate2 (local speech-to-text) link against
msvcp140.dll and msvcp140_1.dll, which the managed CPython does not ship.
On Windows without the system VC++ Redistributable both imports failed with
"DLL load failed: The specified module could not be found", so "Hey Hermes"
and voice chat transcription never worked.

- Bundle the Microsoft-signed VC143 CRT set (14.44.35211, matching the
  managed CPython's vcruntime140) as a Windows extra resource with a SHA-256
  manifest.
- Before every local backend start, copy missing or mismatching CRT files
  beside the managed python.exe (the process image directory, searched first),
  verifying digests; never overwrite the interpreter's own vcruntime and never
  block boot. Existing installs heal on their next launch.
- Report an unloadable native voice wheel as a VC++ runtime problem in the
  wake-word requirements, local transcription and `hermes doctor`, instead of
  treating installed package metadata as proof the wheel loads.
- Bump AITURK IDE to 1.0.0-beta.12.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 19:46:39 +03:00

130 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Windows VC++ runtime detection for the native voice wheels."""
import subprocess
import pytest
import tools.transcription_tools as tt
import tools.wake_word as ww
from tools import windows_native_runtime as wnr
_NATIVE_LOAD_ERROR = wnr.native_load_error
@pytest.fixture(autouse=True)
def _clear_probe_cache():
_NATIVE_LOAD_ERROR.cache_clear()
yield
_NATIVE_LOAD_ERROR.cache_clear()
def _fake_run(returncode, stderr, calls=None):
def run(cmd, **kwargs):
if calls is not None:
calls.append((cmd, kwargs))
return subprocess.CompletedProcess(cmd, returncode, stdout="", stderr=stderr)
return run
def test_non_windows_never_probes(monkeypatch):
monkeypatch.setattr(wnr.sys, "platform", "linux")
monkeypatch.setattr(wnr.subprocess, "run", pytest.fail)
assert _NATIVE_LOAD_ERROR("onnxruntime") is None
@pytest.mark.parametrize(
"stderr",
[
"ImportError: DLL load failed while importing onnxruntime_pybind11_state: The specified module could not be found.",
"ImportError: DLL load failed while importing onnxruntime_pybind11_state: Belirtilen modül bulunamadı.",
"FileNotFoundError: Could not find module 'C:\\venv\\ctranslate2\\ctranslate2.dll' (or one of its dependencies).",
],
)
def test_dll_load_failure_is_reported(monkeypatch, stderr):
calls = []
monkeypatch.setattr(wnr.sys, "platform", "win32")
monkeypatch.setattr(wnr.subprocess, "run", _fake_run(1, "Traceback (most recent call last):\n" + stderr, calls))
assert _NATIVE_LOAD_ERROR("onnxruntime") == stderr
assert calls[0][0][1:] == ["-c", "import onnxruntime"]
assert calls[0][1]["timeout"] > 0
def test_missing_package_is_not_a_runtime_failure(monkeypatch):
monkeypatch.setattr(wnr.sys, "platform", "win32")
monkeypatch.setattr(
wnr.subprocess, "run", _fake_run(1, "ModuleNotFoundError: No module named 'onnxruntime'")
)
assert _NATIVE_LOAD_ERROR("onnxruntime") is None
def test_probe_result_is_cached_per_process(monkeypatch):
calls = []
monkeypatch.setattr(wnr.sys, "platform", "win32")
monkeypatch.setattr(wnr.subprocess, "run", _fake_run(0, "", calls))
assert _NATIVE_LOAD_ERROR("ctranslate2") is None
assert _NATIVE_LOAD_ERROR("ctranslate2") is None
assert len(calls) == 1
def test_inconclusive_probe_does_not_block(monkeypatch):
def timeout(cmd, **kwargs):
raise subprocess.TimeoutExpired(cmd, kwargs["timeout"])
monkeypatch.setattr(wnr.sys, "platform", "win32")
monkeypatch.setattr(wnr.subprocess, "run", timeout)
assert _NATIVE_LOAD_ERROR("onnxruntime") is None
def test_module_name_is_validated(monkeypatch):
monkeypatch.setattr(wnr.sys, "platform", "win32")
with pytest.raises(ValueError):
_NATIVE_LOAD_ERROR("os; import shutil")
def test_wake_requirements_report_missing_vc_runtime(monkeypatch):
monkeypatch.setattr(ww, "_stt_ready", lambda: True)
monkeypatch.setattr(ww, "_tts_ready", lambda: True)
monkeypatch.setattr(ww, "_audio_available", lambda: True)
monkeypatch.setattr(ww, "_local_input_device_ready", lambda: True)
monkeypatch.setattr("tools.lazy_deps.is_available", lambda f: True)
monkeypatch.setattr("tools.lazy_deps._allow_lazy_installs", lambda: True)
monkeypatch.setattr(ww, "_native_runtime_error", lambda module: "ImportError: DLL load failed")
reqs = ww.check_wake_word_requirements({"provider": "openwakeword", "capture": "local"})
assert reqs["available"] is False
assert reqs["native_runtime_error"] == "ImportError: DLL load failed"
assert "msvcp140.dll" in reqs["hint"]
def test_wake_requirements_unaffected_when_runtime_loads(monkeypatch):
monkeypatch.setattr(ww, "_stt_ready", lambda: True)
monkeypatch.setattr(ww, "_tts_ready", lambda: True)
monkeypatch.setattr(ww, "_audio_available", lambda: True)
monkeypatch.setattr(ww, "_local_input_device_ready", lambda: True)
monkeypatch.setattr("tools.lazy_deps.is_available", lambda f: True)
monkeypatch.setattr(ww, "_native_runtime_error", lambda module: None)
reqs = ww.check_wake_word_requirements({"provider": "openwakeword", "capture": "local"})
assert reqs["available"] is True
assert reqs["native_runtime_error"] is None
def test_local_transcription_reports_missing_vc_runtime(monkeypatch, tmp_path):
monkeypatch.setattr(tt, "_HAS_FASTER_WHISPER", True)
monkeypatch.setattr(wnr, "native_load_error", lambda module: "ImportError: DLL load failed")
monkeypatch.setattr(tt, "_load_stt_config", pytest.fail)
result = tt._transcribe_local(str(tmp_path / "a.wav"), "base")
assert result["success"] is False
assert "msvcp140.dll" in result["error"]