fix(setup): tolerate reserved memory in the RAM preflight, add OMNIVOICE_RAM_PREFLIGHT=0 escape hatch (#1621)
* fix(setup): tolerate reserved memory in the RAM preflight, add OMNIVOICE_RAM_PREFLIGHT=0 escape hatch (#1618) An "8 GB" machine reports ~7.8 GB usable (firmware/iGPU/kernel reservations), so comparing OS-reported RAM against the marketing-size 8 GB threshold hard-blocked exactly the boundary hardware the minimum is meant to admit — with no way past the wizard. Both thresholds are now compared with a 7% reserved-memory allowance, and OMNIVOICE_RAM_PREFLIGHT=0 downgrades a genuine fail to a warning for users who accept the OOM risk (same opt-out shape as OMNIVOICE_ASR_VRAM_PREFLIGHT). Regression tests: backend/tests/test_ram_preflight_1618.py. Docs: troubleshooting §1c. * review: hermetic preflight stubs in tests; correct the escape-hatch doc Greptile P1: the Settings panel can't set OMNIVOICE_RAM_PREFLIGHT (and the blocker appears before setup completes anyway) — the doc now points at PowerShell / shell env only. CodeRabbit: stub _network_check and media_tools.summary so each RAM assertion stays fast and offline (26s -> 6s locally).
This commit is contained in:
@@ -33,6 +33,7 @@ the frozen-backend fallback mirror it for their toolchains.
|
||||
- The OmniVoice guide now covers combining style attributes with a reference clip (consistent instruct stabilizes cloning; the reference wins conflicts), inline pronunciation control (pinyin / CMU phonemes), and corrects the claim that the default engine can't do voice design — it can, from attributes (#1565)
|
||||
|
||||
### Fixed
|
||||
- The setup wizard's RAM check no longer blocks 8 GB machines whose OS reports ~7.8 GB usable — the thresholds now tolerate reserved memory, and `OMNIVOICE_RAM_PREFLIGHT=0` turns a genuine block into a warning for those who accept the OOM risk (#1618)
|
||||
- Invisible watermarking now runs eagerly instead of through `torch.compile` — AudioSeal's lazy compile sent the first embed of every session into Inductor's C++ codegen, which failed outright on macOS hosts whose toolchain couldn't serve it and shipped the audio unmarked after a 30-40s wait; first embed drops from 9.70s to 0.26s (#1615) — thanks @paoloantinori!
|
||||
- The macOS Accessibility blocker now rechecks while visible and closes as soon as the grant is enabled instead of keeping a stale permission prompt on screen (#1609)
|
||||
- The dubbing editor's video and transcript columns can now be resized by pointer or keyboard, and the chosen split persists across launches (#1571) — thanks @invio-a11y!
|
||||
|
||||
@@ -62,6 +62,11 @@ def setup_status():
|
||||
_MIN_NVIDIA_DRIVER = 555
|
||||
_RAM_FAIL_GB = 8
|
||||
_RAM_WARN_GB = 12
|
||||
# Installed DIMMs never fully reach the OS: firmware, integrated graphics and
|
||||
# kernel reservations shave off up to ~7% (an "8 GB" Windows laptop reports
|
||||
# ~7.8 GB usable). Thresholds are compared with this allowance applied so the
|
||||
# machines a threshold is meant to admit aren't blocked by that gap (#1618).
|
||||
_RAM_RESERVED_ALLOWANCE = 0.93
|
||||
|
||||
|
||||
def _run_cmd(args: list[str], timeout: float = 2.0) -> tuple[int, str]:
|
||||
@@ -352,17 +357,28 @@ def preflight():
|
||||
|
||||
# ── RAM
|
||||
ram = _ram_gb()
|
||||
# Escape hatch (#1618): a preflight should inform, not brick setup —
|
||||
# OMNIVOICE_RAM_PREFLIGHT=0 downgrades the hard block to a warning for
|
||||
# users who accept the OOM risk. Same opt-out shape as
|
||||
# OMNIVOICE_ASR_VRAM_PREFLIGHT.
|
||||
ram_gate = os.environ.get(
|
||||
"OMNIVOICE_RAM_PREFLIGHT", "1"
|
||||
).strip().lower() not in ("0", "false", "no")
|
||||
if ram == 0:
|
||||
ram_status, ram_detail, ram_fix = (
|
||||
"warn", "Could not detect system RAM.",
|
||||
"Install psutil in the backend environment or ignore this warning.",
|
||||
)
|
||||
elif ram < _RAM_FAIL_GB:
|
||||
elif ram < _RAM_FAIL_GB * _RAM_RESERVED_ALLOWANCE:
|
||||
ram_status, ram_detail, ram_fix = (
|
||||
"fail", f"{ram:.1f} GB total (need ≥ {_RAM_FAIL_GB} GB)",
|
||||
"The app will OOM on first dub. Close other apps or upgrade RAM.",
|
||||
"fail" if ram_gate else "warn",
|
||||
f"{ram:.1f} GB total (need ≥ {_RAM_FAIL_GB} GB)",
|
||||
"The app will OOM on first dub. Close other apps or upgrade RAM."
|
||||
if ram_gate else
|
||||
"RAM check disabled via OMNIVOICE_RAM_PREFLIGHT=0 — dubbing may "
|
||||
"OOM on this machine.",
|
||||
)
|
||||
elif ram < _RAM_WARN_GB:
|
||||
elif ram < _RAM_WARN_GB * _RAM_RESERVED_ALLOWANCE:
|
||||
ram_status, ram_detail, ram_fix = (
|
||||
"warn", f"{ram:.1f} GB total ({_RAM_WARN_GB}+ GB recommended)",
|
||||
"Long videos may hit swap. Keep other apps closed during dubbing.",
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
"""#1618 — RAM preflight must not hard-block the machines it means to admit.
|
||||
|
||||
An "8 GB" machine reports ~7.8 GB usable (firmware/iGPU/kernel reservations),
|
||||
so comparing reported RAM against the marketing-size threshold blocked exactly
|
||||
the boundary hardware the ≥8 GB rule intends to allow. The check now applies
|
||||
``_RAM_RESERVED_ALLOWANCE`` to both thresholds, and
|
||||
``OMNIVOICE_RAM_PREFLIGHT=0`` downgrades a genuine fail to a warning.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from api.routers.setup import wizard
|
||||
|
||||
|
||||
def _ram_check(monkeypatch, ram_gb: float, env: str | None = None) -> dict:
|
||||
# Keep the preflight hermetic: stub the probes that hit the network or
|
||||
# auto-acquire media tools, so each RAM assertion stays fast and offline.
|
||||
monkeypatch.setattr(wizard, "_network_check", lambda: {
|
||||
"id": "network", "label": "Network", "status": "pass",
|
||||
"detail": "stubbed", "fix": None, "mirror_reachable": True,
|
||||
})
|
||||
import services.media_tools as media_tools
|
||||
monkeypatch.setattr(media_tools, "summary", lambda auto_acquire=True: None)
|
||||
monkeypatch.setattr(wizard, "_ram_gb", lambda: ram_gb)
|
||||
if env is None:
|
||||
monkeypatch.delenv("OMNIVOICE_RAM_PREFLIGHT", raising=False)
|
||||
else:
|
||||
monkeypatch.setenv("OMNIVOICE_RAM_PREFLIGHT", env)
|
||||
resp = wizard.preflight()
|
||||
checks = resp["checks"] if isinstance(resp, dict) else resp.checks
|
||||
for c in checks:
|
||||
c = c if isinstance(c, dict) else c.model_dump()
|
||||
if c["id"] == "ram":
|
||||
return c
|
||||
raise AssertionError("no ram check in preflight response")
|
||||
|
||||
|
||||
def test_8gb_installed_reporting_7_84_usable_is_not_blocked(monkeypatch):
|
||||
"""The #1618 report: 7.84 GB usable on an 8 GB laptop was a hard fail."""
|
||||
check = _ram_check(monkeypatch, 7.84)
|
||||
assert check["status"] != "fail"
|
||||
|
||||
|
||||
def test_boundary_at_allowance_passes_the_fail_gate(monkeypatch):
|
||||
check = _ram_check(
|
||||
monkeypatch, wizard._RAM_FAIL_GB * wizard._RAM_RESERVED_ALLOWANCE
|
||||
)
|
||||
assert check["status"] != "fail"
|
||||
|
||||
|
||||
def test_genuinely_low_ram_still_fails(monkeypatch):
|
||||
check = _ram_check(monkeypatch, 6.0)
|
||||
assert check["status"] == "fail"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("env", ["0", "false", "no"])
|
||||
def test_escape_hatch_downgrades_fail_to_warn(monkeypatch, env):
|
||||
check = _ram_check(monkeypatch, 6.0, env=env)
|
||||
assert check["status"] == "warn"
|
||||
assert "OMNIVOICE_RAM_PREFLIGHT" in (check["fix"] or "")
|
||||
|
||||
|
||||
def test_escape_hatch_not_triggered_by_other_values(monkeypatch):
|
||||
check = _ram_check(monkeypatch, 6.0, env="1")
|
||||
assert check["status"] == "fail"
|
||||
|
||||
|
||||
def test_12gb_installed_reporting_11_8_usable_passes_clean(monkeypatch):
|
||||
"""Same reservation gap at the warn threshold: 12 GB installed ≈ 11.8."""
|
||||
check = _ram_check(monkeypatch, 11.8)
|
||||
assert check["status"] == "pass"
|
||||
|
||||
|
||||
def test_warn_band_between_thresholds(monkeypatch):
|
||||
check = _ram_check(monkeypatch, 9.0)
|
||||
assert check["status"] == "warn"
|
||||
@@ -138,6 +138,28 @@ failing outright.
|
||||
|
||||
**Linked issue:** [#1185](https://github.com/debpalash/VoiceStudio/issues/1185)
|
||||
|
||||
## 1c. Setup blocked: "System RAM … The app will OOM on first dub"
|
||||
|
||||
**Symptom:** the setup wizard's System Check shows **System RAM** in red and
|
||||
"Resolve blockers to continue" stays disabled — often on an 8 GB machine that
|
||||
reports ~7.8 GB usable (firmware and integrated graphics reserve a slice of
|
||||
installed RAM).
|
||||
|
||||
**Cause:** the preflight compares OS-reported RAM against the 8 GB minimum.
|
||||
Since [#1618](https://github.com/debpalash/VoiceStudio/issues/1618) the check
|
||||
tolerates that reserved-memory gap, so 8 GB-installed machines pass.
|
||||
|
||||
**Fix:** update to the latest release. If your machine is genuinely below the
|
||||
minimum and you accept the out-of-memory risk (long dubs may crash), set
|
||||
`OMNIVOICE_RAM_PREFLIGHT=0` before launching — the hard block becomes a
|
||||
warning. On Windows run PowerShell
|
||||
`[Environment]::SetEnvironmentVariable('OMNIVOICE_RAM_PREFLIGHT','0','User')`
|
||||
and relaunch; on macOS/Linux export it in the shell that starts the app. (The
|
||||
in-app Settings panel can't help here — this blocker appears before setup
|
||||
completes.)
|
||||
|
||||
**Linked issues:** [#1618](https://github.com/debpalash/VoiceStudio/issues/1618)
|
||||
|
||||
## 2. HF 401 / pyannote license not accepted
|
||||
|
||||
**Symptom:** dubbing fails with `HfHubHTTPError: 401 Client Error: Unauthorized
|
||||
|
||||
Reference in New Issue
Block a user