Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions src/inspect_robots_so101/policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,17 @@ def _import_hw_to_dataset_features() -> Callable[..., dict[str, Any]]:
"""Import lerobot's ``hw_to_dataset_features`` across supported versions.

lerobot moved it from ``lerobot.datasets.utils`` (v0.5.0) to
``lerobot.datasets.feature_utils`` (v0.5.1); the ``lerobot-seam`` CI job
``lerobot.datasets.feature_utils`` (v0.5.1) to
``lerobot.utils.feature_utils`` (v0.6.0); the ``lerobot-seam`` CI job
guards this against further drift.
"""
try:
from lerobot.datasets.feature_utils import hw_to_dataset_features
except ImportError: # lerobot == 0.5.0
from lerobot.datasets.utils import hw_to_dataset_features
from lerobot.utils.feature_utils import hw_to_dataset_features
except ImportError:
try: # lerobot 0.5.1 - 0.5.x
from lerobot.datasets.feature_utils import hw_to_dataset_features
except ImportError: # lerobot == 0.5.0
from lerobot.datasets.utils import hw_to_dataset_features
return hw_to_dataset_features # type: ignore[no-any-return]


Expand Down
43 changes: 38 additions & 5 deletions tests/test_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,9 @@ def _install_lerobot_fakes(
*,
chunk: np.ndarray,
image_features: dict[str, Any] | None = None,
feature_utils: bool = True,
feature_layout: str = "v060",
) -> dict[str, Any]:
"""Install fake torch/lerobot modules; return a dict recording every call."""
"""Install fakes for the v0.6.0, v0.5.1, or v0.5.0 feature layout."""
calls: dict[str, Any] = {}
if image_features is None:
image_features = {"observation.images.front": SimpleNamespace(shape=(3, 4, 4))}
Expand Down Expand Up @@ -267,19 +267,37 @@ def _stack(tensors: list[FakeTensor], dim: int = 0) -> FakeTensor:
"lerobot.async_inference.helpers": helpers,
"lerobot.datasets": datasets,
}
if feature_utils:
if feature_layout == "v060":
# lerobot >= 0.6.0: hw_to_dataset_features lives in lerobot.utils.feature_utils.
monkeypatch.delitem(sys.modules, "lerobot.datasets.feature_utils", raising=False)
monkeypatch.delitem(sys.modules, "lerobot.datasets.utils", raising=False)
utils = ModuleType("lerobot.utils")
feature_utils_mod = ModuleType("lerobot.utils.feature_utils")
feature_utils_mod.hw_to_dataset_features = hw_to_dataset_features # type: ignore[attr-defined]
utils.feature_utils = feature_utils_mod # type: ignore[attr-defined]
lerobot.utils = utils # type: ignore[attr-defined]
modules["lerobot.utils"] = utils
modules["lerobot.utils.feature_utils"] = feature_utils_mod
elif feature_layout == "v051":
# lerobot 0.5.1 - 0.5.x: the helper moved to datasets.feature_utils.
monkeypatch.delitem(sys.modules, "lerobot.utils.feature_utils", raising=False)
monkeypatch.delitem(sys.modules, "lerobot.utils", raising=False)
feature_utils_mod = ModuleType("lerobot.datasets.feature_utils")
feature_utils_mod.hw_to_dataset_features = hw_to_dataset_features # type: ignore[attr-defined]
datasets.feature_utils = feature_utils_mod # type: ignore[attr-defined]
modules["lerobot.datasets.feature_utils"] = feature_utils_mod
else:
elif feature_layout == "v050":
# lerobot == 0.5.0 layout: hw_to_dataset_features in lerobot.datasets.utils
# and NO feature_utils module (the import must raise ImportError).
monkeypatch.delitem(sys.modules, "lerobot.utils.feature_utils", raising=False)
monkeypatch.delitem(sys.modules, "lerobot.utils", raising=False)
monkeypatch.delitem(sys.modules, "lerobot.datasets.feature_utils", raising=False)
utils_mod = ModuleType("lerobot.datasets.utils")
utils_mod.hw_to_dataset_features = hw_to_dataset_features # type: ignore[attr-defined]
datasets.utils = utils_mod # type: ignore[attr-defined]
modules["lerobot.datasets.utils"] = utils_mod
else:
raise ValueError(f"unknown feature layout: {feature_layout}")

for name, mod in modules.items():
monkeypatch.setitem(sys.modules, name, mod)
Expand Down Expand Up @@ -370,7 +388,22 @@ def test_default_predict_rejects_camera_feature_mismatch(
def test_default_predict_hw_features_fallback_import(monkeypatch: pytest.MonkeyPatch) -> None:
# lerobot 0.5.0 layout: no lerobot.datasets.feature_utils -> fall back to
# lerobot.datasets.utils.
calls = _install_lerobot_fakes(monkeypatch, chunk=np.ones((1, 2, 6)), feature_utils=False)
calls = _install_lerobot_fakes(monkeypatch, chunk=np.ones((1, 2, 6)), feature_layout="v050")
pol = LeRobotPolicy(_seam_cfg())
pol.reset(Scene(id="s", instruction="x"))
out = pol.act(_obs())
assert len(out) == 2
assert calls["hw_prefix"] == "observation"


def test_default_predict_hw_features_v051_fallback_import(
monkeypatch: pytest.MonkeyPatch,
) -> None:
calls = _install_lerobot_fakes(
monkeypatch,
chunk=np.ones((1, 2, 6)),
feature_layout="v051",
)
pol = LeRobotPolicy(_seam_cfg())
pol.reset(Scene(id="s", instruction="x"))
out = pol.act(_obs())
Expand Down
Loading