From f09d30b6327443ece2857add1d71f9bd478ba7d1 Mon Sep 17 00:00:00 2001 From: RomirJ Date: Wed, 10 Jun 2026 17:01:00 -0700 Subject: [PATCH] fix(exporters): assert lerobot==0.5.1 + surface SmolVLA patch failure (#190) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audit §3.3 — the likely root cause behind community issue #190 ("facing issue while exporting smolvla_base model"). The monolithic export's dep check version-pinned transformers (==5.3.0) but only checked that lerobot was *importable*, not that it was 0.5.1. The SmolVLA export patches target lerobot 0.5.1's exact module layout (SmolVLMWithExpertModel.embed_image, the masking_utils surface), so a mismatched lerobot imports fine and then fails later with a cryptic vision/attention mask-broadcast error — or exports a subtly wrong graph. Two fixes: - _require_monolithic_deps now asserts lerobot.__version__ == "0.5.1" and adds a clear "found " message to the missing-deps error, so the mismatch is caught up front with an actionable fix instead of downstream. - The SmolVLA explicit-patch-mask monkeypatch failure was logged at debug and swallowed; raised to warning with an explicit note that the export proceeds unpatched and that lerobot==0.5.1 is the thing to check — so the failure is attributable rather than silent. Tests (tests/test_monolithic_version_guard.py): wrong lerobot version is reported in the dep-check error; correct version is not. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/tether/exporters/monolithic.py | 28 +++++++++++++++++- tests/test_monolithic_version_guard.py | 40 ++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 tests/test_monolithic_version_guard.py diff --git a/src/tether/exporters/monolithic.py b/src/tether/exporters/monolithic.py index 7725240..738c68c 100644 --- a/src/tether/exporters/monolithic.py +++ b/src/tether/exporters/monolithic.py @@ -144,6 +144,22 @@ def _require_monolithic_deps() -> None: except ImportError: missing.append(pip_name) + # lerobot version must be exactly 0.5.1 — the SmolVLA/pi0 export patches + # target its 0.5.1 module layout (SmolVLMWithExpertModel.embed_image, the + # masking_utils surface). A different lerobot imports fine but then fails + # the monkeypatch or exports a wrong graph — the common, confusing failure + # behind issue #190. Assert it explicitly rather than discovering it later. + try: + import lerobot + lerobot_ver = getattr(lerobot, "__version__", "unknown") + if lerobot_ver != "0.5.1": + missing.append( + f"lerobot==0.5.1 (found {lerobot_ver}; the monolithic export " + f"patches target lerobot 0.5.1's module layout exactly)" + ) + except ImportError: + pass # already reported as missing above + if missing: raise ImportError( "Missing dependencies for monolithic export:\n - " @@ -251,7 +267,17 @@ def _embed_image_with_explicit_patch_mask(self, image): _embed_image_with_explicit_patch_mask._tether_patched = True _smolvla.SmolVLMWithExpertModel.embed_image = _embed_image_with_explicit_patch_mask except Exception as exc: - logger.debug("SmolVLA explicit patch-mask export patch not installed: %s", exc) + # WARNING, not debug: if this patch fails to apply (e.g. a lerobot API + # change moved SmolVLMWithExpertModel.embed_image), the SmolVLA export + # proceeds with the UNPATCHED forward and can fail later with a cryptic + # mask-broadcast error — or export a subtly wrong graph. Surface it so + # the failure is attributable, not silent (relates to issue #190). + logger.warning( + "SmolVLA explicit patch-mask export patch did NOT apply (%s). " + "The export will continue unpatched; if it fails downstream with a " + "vision/attention-mask shape error, this is the likely cause. " + "Verify lerobot==0.5.1 is installed.", exc, + ) try: from transformers.models.smolvlm import modeling_smolvlm as _smolvlm diff --git a/tests/test_monolithic_version_guard.py b/tests/test_monolithic_version_guard.py new file mode 100644 index 0000000..6beefac --- /dev/null +++ b/tests/test_monolithic_version_guard.py @@ -0,0 +1,40 @@ +"""The monolithic export must reject a wrong lerobot version up front (#190). + +Previously only `transformers` was version-checked; `lerobot` was merely +imported, so a mismatched lerobot passed the dep check and failed later with a +confusing monkeypatch/mask error. +""" +from __future__ import annotations + +import sys +import types + +import pytest + +from tether.exporters import monolithic + + +def _fake_lerobot(version: str) -> types.ModuleType: + m = types.ModuleType("lerobot") + m.__version__ = version + return m + + +def test_wrong_lerobot_version_is_reported(monkeypatch): + monkeypatch.setitem(sys.modules, "lerobot", _fake_lerobot("0.4.0")) + with pytest.raises(ImportError) as ei: + monolithic._require_monolithic_deps() + assert "lerobot==0.5.1 (found 0.4.0" in str(ei.value) + + +def test_correct_lerobot_version_not_reported(monkeypatch): + monkeypatch.setitem(sys.modules, "lerobot", _fake_lerobot("0.5.1")) + # Other monolithic deps may be absent in this env (so it can still raise), + # but the lerobot-version complaint must NOT be among the reasons. + try: + monolithic._require_monolithic_deps() + msg = "" + except ImportError as e: + msg = str(e) + assert "found 0.5.1" not in msg + assert "lerobot==0.5.1 (found" not in msg