feat(dependecies): minimal default tag install

This commit is contained in:
Steven Palma
2026-04-10 14:22:13 +02:00
parent 6799da35eb
commit e2381633cd
44 changed files with 575 additions and 363 deletions
+1 -1
View File
@@ -168,7 +168,7 @@ Use the `image_transforms` parameter when loading a dataset for training:
```python ```python
from lerobot.datasets.lerobot_dataset import LeRobotDataset from lerobot.datasets.lerobot_dataset import LeRobotDataset
from lerobot.datasets.transforms import ImageTransforms, ImageTransformsConfig, ImageTransformConfig from lerobot.transforms import ImageTransforms, ImageTransformsConfig, ImageTransformConfig
# Option 1: Use default transform configuration (disabled by default) # Option 1: Use default transform configuration (disabled by default)
transforms_config = ImageTransformsConfig( transforms_config = ImageTransformsConfig(
@@ -27,7 +27,7 @@ from torchvision.transforms import v2
from torchvision.transforms.functional import to_pil_image from torchvision.transforms.functional import to_pil_image
from lerobot.datasets.lerobot_dataset import LeRobotDataset from lerobot.datasets.lerobot_dataset import LeRobotDataset
from lerobot.datasets.transforms import ImageTransformConfig, ImageTransforms, ImageTransformsConfig from lerobot.transforms import ImageTransformConfig, ImageTransforms, ImageTransformsConfig
def save_image(tensor, filename): def save_image(tensor, filename):
+60 -33
View File
@@ -58,44 +58,64 @@ classifiers = [
keywords = ["lerobot", "huggingface", "robotics", "machine learning", "artificial intelligence"] keywords = ["lerobot", "huggingface", "robotics", "machine learning", "artificial intelligence"]
dependencies = [ dependencies = [
# Core ML
# Hugging Face dependencies
"datasets>=4.0.0,<5.0.0",
"diffusers>=0.27.2,<0.36.0",
"huggingface-hub>=1.0.0,<2.0.0",
"accelerate>=1.10.0,<2.0.0",
# Core dependencies
"numpy>=2.0.0,<2.3.0", # NOTE: Explicitly listing numpy helps the resolver converge faster. Upper bound imposed by opencv-python-headless.
"setuptools>=71.0.0,<81.0.0",
"cmake>=3.29.0.1,<4.2.0",
"packaging>=24.2,<26.0",
"torch>=2.7,<2.11.0", "torch>=2.7,<2.11.0",
"torchcodec>=0.3.0,<0.11.0; sys_platform != 'win32' and (sys_platform != 'linux' or (platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l')) and (sys_platform != 'darwin' or platform_machine != 'x86_64')", # NOTE: Windows support starts at version 0.7 (needs torch==2.8), ffmpeg>=8 support starts at version 0.8.1 (needs torch==2.9), system-wide ffmpeg support starts at version 0.10 (needs torch==2.10).
"torchvision>=0.22.0,<0.26.0", "torchvision>=0.22.0,<0.26.0",
"numpy>=2.0.0,<2.3.0", # NOTE: Explicitly listing numpy helps the resolver converge faster. Upper bound imposed by opencv-python-headless.
"einops>=0.8.0,<0.9.0",
"opencv-python-headless>=4.9.0,<4.14.0", "opencv-python-headless>=4.9.0,<4.14.0",
"av>=15.0.0,<16.0.0", "einops>=0.8.0,<0.9.0",
"jsonlines>=4.0.0,<5.0.0",
"pynput>=1.7.8,<1.9.0",
"pyserial>=3.5,<4.0",
"wandb>=0.24.0,<0.25.0", # Config & Hub
"draccus==0.10.0", # TODO: Relax version constraint "draccus==0.10.0", # TODO: Relax version constraint
"gymnasium>=1.1.1,<2.0.0", "huggingface-hub>=1.0.0,<2.0.0",
"rerun-sdk>=0.24.0,<0.27.0",
# Support dependencies # Environments
"deepdiff>=7.0.1,<9.0.0", "gymnasium>=1.1.1,<2.0.0",
"imageio[ffmpeg]>=2.34.0,<3.0.0",
# Lightweight utilities
"packaging>=24.2,<26.0",
"termcolor>=2.4.0,<4.0.0", "termcolor>=2.4.0,<4.0.0",
] ]
# Optional dependencies # Optional dependencies
[project.optional-dependencies] [project.optional-dependencies]
# ── Feature-scoped extras ──────────────────────────────────
dataset = [
"datasets>=4.0.0,<5.0.0",
"av>=15.0.0,<16.0.0",
"torchcodec>=0.3.0,<0.11.0; sys_platform != 'win32' and (sys_platform != 'linux' or (platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l')) and (sys_platform != 'darwin' or platform_machine != 'x86_64')", # NOTE: Windows support starts at version 0.7 (needs torch==2.8), ffmpeg>=8 support starts at version 0.8.1 (needs torch==2.9), system-wide ffmpeg support starts at version 0.10 (needs torch==2.10).
"jsonlines>=4.0.0,<5.0.0",
]
train = [
"lerobot[dataset]",
"accelerate>=1.10.0,<2.0.0",
"wandb>=0.24.0,<0.25.0",
"diffusers>=0.27.2,<0.36.0",
]
hardware = [
"pynput>=1.7.8,<1.9.0",
"pyserial>=3.5,<4.0",
"deepdiff>=7.0.1,<9.0.0",
]
viz = [
"rerun-sdk>=0.24.0,<0.27.0",
]
build = [
"cmake>=3.29.0.1,<4.2.0",
"setuptools>=71.0.0,<81.0.0",
]
# ── User-facing composite extras (map to CLI scripts) ─────
# lerobot-record, lerobot-replay, lerobot-calibrate, lerobot-teleoperate, etc.
robot = ["lerobot[dataset]", "lerobot[hardware]", "lerobot[viz]"]
# lerobot-eval
evaluation = ["av>=15.0.0,<16.0.0"]
# lerobot-train
training = ["lerobot[train]"]
# lerobot-dataset-viz, lerobot-imgtransform-viz
dataset_viz = ["lerobot[dataset]", "lerobot[viz]"]
# Common # Common
pygame-dep = ["pygame>=2.5.1,<2.7.0"] pygame-dep = ["pygame>=2.5.1,<2.7.0"]
placo-dep = ["placo>=0.9.6,<0.9.17"] placo-dep = ["placo>=0.9.6,<0.9.17"]
@@ -166,19 +186,25 @@ async = ["lerobot[grpcio-dep]", "lerobot[matplotlib-dep]"]
peft = ["lerobot[transformers-dep]", "lerobot[peft-dep]"] peft = ["lerobot[transformers-dep]", "lerobot[peft-dep]"]
# Development # Development
dev = ["pre-commit>=3.7.0,<5.0.0", "debugpy>=1.8.1,<1.9.0", "lerobot[grpcio-dep]", "grpcio-tools==1.73.1", "mypy>=1.19.1"] dev = ["lerobot[dataset]", "lerobot[train]", "lerobot[hardware]", "lerobot[viz]", "pre-commit>=3.7.0,<5.0.0", "debugpy>=1.8.1,<1.9.0", "lerobot[grpcio-dep]", "grpcio-tools==1.73.1", "mypy>=1.19.1"]
test = ["pytest>=8.1.0,<9.0.0", "pytest-timeout>=2.4.0,<3.0.0", "pytest-cov>=5.0.0,<8.0.0", "mock-serial>=0.0.1,<0.1.0 ; sys_platform != 'win32'"] test = ["lerobot[dataset]", "lerobot[train]", "lerobot[hardware]", "lerobot[viz]", "pytest>=8.1.0,<9.0.0", "pytest-timeout>=2.4.0,<3.0.0", "pytest-cov>=5.0.0,<8.0.0", "mock-serial>=0.0.1,<0.1.0 ; sys_platform != 'win32'"]
video_benchmark = ["scikit-image>=0.23.2,<0.26.0", "pandas>=2.2.2,<2.4.0"] video_benchmark = ["scikit-image>=0.23.2,<0.26.0", "pandas>=2.2.2,<2.4.0"]
# Simulation # Simulation
# NOTE: Explicitly listing scipy helps flatten the dependecy tree. # NOTE: Explicitly listing scipy helps flatten the dependecy tree.
aloha = ["gym-aloha>=0.1.2,<0.2.0", "lerobot[scipy-dep]"] aloha = ["lerobot[dataset]", "gym-aloha>=0.1.2,<0.2.0", "lerobot[scipy-dep]"]
pusht = ["gym-pusht>=0.1.5,<0.2.0", "pymunk>=6.6.0,<7.0.0"] # TODO: Fix pymunk version in gym-pusht instead pusht = ["lerobot[dataset]", "gym-pusht>=0.1.5,<0.2.0", "pymunk>=6.6.0,<7.0.0"] # TODO: Fix pymunk version in gym-pusht instead
libero = ["lerobot[transformers-dep]", "hf-libero>=0.1.3,<0.2.0; sys_platform == 'linux'", "lerobot[scipy-dep]"] libero = ["lerobot[dataset]", "lerobot[transformers-dep]", "hf-libero>=0.1.3,<0.2.0; sys_platform == 'linux'", "lerobot[scipy-dep]"]
metaworld = ["metaworld==3.0.0", "lerobot[scipy-dep]"] metaworld = ["lerobot[dataset]", "metaworld==3.0.0", "lerobot[scipy-dep]"]
# All # All
all = [ all = [
# Feature-scoped extras
"lerobot[dataset]",
"lerobot[train]",
"lerobot[hardware]",
"lerobot[viz]",
"lerobot[build]",
# NOTE(resolver hint): scipy is pulled in transitively via lerobot[scipy-dep] through # NOTE(resolver hint): scipy is pulled in transitively via lerobot[scipy-dep] through
# multiple extras (aloha, metaworld, pi, wallx, phone). Listing it explicitly # multiple extras (aloha, metaworld, pi, wallx, phone). Listing it explicitly
# helps pip's resolver converge by constraining scipy early, before it encounters # helps pip's resolver converge by constraining scipy early, before it encounters
@@ -267,7 +293,8 @@ ignore = [
] ]
[tool.ruff.lint.per-file-ignores] [tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401", "F403"] "__init__.py" = ["F401", "F403", "E402"]
"src/lerobot/scripts/*" = ["E402"] # require_package gates before imports
"src/lerobot/policies/wall_x/**" = ["N801", "N812", "SIM102", "SIM108", "SIM210", "SIM211", "B006", "B007", "SIM118"] # Supprese these as they are coming from original Qwen2_5_vl code TODO(pepijn): refactor original "src/lerobot/policies/wall_x/**" = ["N801", "N812", "SIM102", "SIM108", "SIM210", "SIM211", "B006", "B007", "SIM118"] # Supprese these as they are coming from original Qwen2_5_vl code TODO(pepijn): refactor original
[tool.ruff.lint.isort] [tool.ruff.lint.isort]
+2 -2
View File
@@ -16,8 +16,8 @@
from dataclasses import dataclass, field from dataclasses import dataclass, field
from lerobot.datasets.transforms import ImageTransformsConfig from lerobot.transforms import ImageTransformsConfig
from lerobot.datasets.video_utils import get_safe_default_codec from lerobot.utils.import_utils import get_safe_default_codec
@dataclass @dataclass
+4 -3
View File
@@ -15,17 +15,18 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from lerobot.utils.import_utils import require_package
require_package("datasets", extra="dataset")
from lerobot.datasets.dataset_metadata import LeRobotDatasetMetadata from lerobot.datasets.dataset_metadata import LeRobotDatasetMetadata
from lerobot.datasets.lerobot_dataset import LeRobotDataset from lerobot.datasets.lerobot_dataset import LeRobotDataset
from lerobot.datasets.multi_dataset import MultiLeRobotDataset from lerobot.datasets.multi_dataset import MultiLeRobotDataset
from lerobot.datasets.sampler import EpisodeAwareSampler from lerobot.datasets.sampler import EpisodeAwareSampler
from lerobot.datasets.streaming_dataset import StreamingLeRobotDataset from lerobot.datasets.streaming_dataset import StreamingLeRobotDataset
from lerobot.datasets.transforms import ImageTransforms, ImageTransformsConfig
__all__ = [ __all__ = [
"EpisodeAwareSampler", "EpisodeAwareSampler",
"ImageTransforms",
"ImageTransformsConfig",
"LeRobotDataset", "LeRobotDataset",
"LeRobotDatasetMetadata", "LeRobotDatasetMetadata",
"MultiLeRobotDataset", "MultiLeRobotDataset",
+1 -1
View File
@@ -829,7 +829,7 @@ def _copy_and_reindex_episodes_metadata(
data_metadata: Dict mapping new episode index to its data file metadata data_metadata: Dict mapping new episode index to its data file metadata
video_metadata: Optional dict mapping new episode index to its video metadata video_metadata: Optional dict mapping new episode index to its video metadata
""" """
from lerobot.datasets.utils import flatten_dict from lerobot.utils.utils import flatten_dict
if src_dataset.meta.episodes is None: if src_dataset.meta.episodes is None:
src_dataset.meta.episodes = load_episodes(src_dataset.meta.root) src_dataset.meta.episodes = load_episodes(src_dataset.meta.root)
+1 -1
View File
@@ -24,7 +24,7 @@ from lerobot.datasets.dataset_metadata import LeRobotDatasetMetadata
from lerobot.datasets.lerobot_dataset import LeRobotDataset from lerobot.datasets.lerobot_dataset import LeRobotDataset
from lerobot.datasets.multi_dataset import MultiLeRobotDataset from lerobot.datasets.multi_dataset import MultiLeRobotDataset
from lerobot.datasets.streaming_dataset import StreamingLeRobotDataset from lerobot.datasets.streaming_dataset import StreamingLeRobotDataset
from lerobot.datasets.transforms import ImageTransforms from lerobot.transforms import ImageTransforms
from lerobot.utils.constants import ACTION, OBS_PREFIX, REWARD from lerobot.utils.constants import ACTION, OBS_PREFIX, REWARD
IMAGENET_STATS = { IMAGENET_STATS = {
+1 -28
View File
@@ -13,7 +13,6 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import json
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
@@ -41,6 +40,7 @@ from lerobot.datasets.utils import (
serialize_dict, serialize_dict,
unflatten_dict, unflatten_dict,
) )
from lerobot.utils.io_utils import load_json, write_json
from lerobot.utils.utils import SuppressProgressBars from lerobot.utils.utils import SuppressProgressBars
@@ -116,33 +116,6 @@ def embed_images(dataset: datasets.Dataset) -> datasets.Dataset:
return dataset return dataset
def load_json(fpath: Path) -> Any:
"""Load data from a JSON file.
Args:
fpath (Path): Path to the JSON file.
Returns:
Any: The data loaded from the JSON file.
"""
with open(fpath) as f:
return json.load(f)
def write_json(data: dict, fpath: Path) -> None:
"""Write data to a JSON file.
Creates parent directories if they don't exist.
Args:
data (dict): The dictionary to write.
fpath (Path): The path to the output JSON file.
"""
fpath.parent.mkdir(exist_ok=True, parents=True)
with open(fpath, "w") as f:
json.dump(data, f, indent=4, ensure_ascii=False)
def write_info(info: dict, local_dir: Path) -> None: def write_info(info: dict, local_dir: Path) -> None:
write_json(info, local_dir / INFO_PATH) write_json(info, local_dir / INFO_PATH)
+2 -53
View File
@@ -28,6 +28,8 @@ import torch
from huggingface_hub import DatasetCard, DatasetCardData, HfApi from huggingface_hub import DatasetCard, DatasetCardData, HfApi
from huggingface_hub.errors import RevisionNotFoundError from huggingface_hub.errors import RevisionNotFoundError
from lerobot.utils.utils import flatten_dict, unflatten_dict
V30_MESSAGE = """ V30_MESSAGE = """
The dataset you requested ({repo_id}) is in {version} format. The dataset you requested ({repo_id}) is in {version} format.
@@ -123,59 +125,6 @@ def update_chunk_file_indices(chunk_idx: int, file_idx: int, chunks_size: int) -
return chunk_idx, file_idx return chunk_idx, file_idx
def flatten_dict(d: dict, parent_key: str = "", sep: str = "/") -> dict:
"""Flatten a nested dictionary by joining keys with a separator.
Example:
>>> dct = {"a": {"b": 1, "c": {"d": 2}}, "e": 3}
>>> print(flatten_dict(dct))
{'a/b': 1, 'a/c/d': 2, 'e': 3}
Args:
d (dict): The dictionary to flatten.
parent_key (str): The base key to prepend to the keys in this level.
sep (str): The separator to use between keys.
Returns:
dict: A flattened dictionary.
"""
items = []
for k, v in d.items():
new_key = f"{parent_key}{sep}{k}" if parent_key else k
if isinstance(v, dict):
items.extend(flatten_dict(v, new_key, sep=sep).items())
else:
items.append((new_key, v))
return dict(items)
def unflatten_dict(d: dict, sep: str = "/") -> dict:
"""Unflatten a dictionary with delimited keys into a nested dictionary.
Example:
>>> flat_dct = {"a/b": 1, "a/c/d": 2, "e": 3}
>>> print(unflatten_dict(flat_dct))
{'a': {'b': 1, 'c': {'d': 2}}, 'e': 3}
Args:
d (dict): A dictionary with flattened keys.
sep (str): The separator used in the keys.
Returns:
dict: A nested dictionary.
"""
outdict = {}
for key, value in d.items():
parts = key.split(sep)
d = outdict
for part in parts[:-1]:
if part not in d:
d[part] = {}
d = d[part]
d[parts[-1]] = value
return outdict
def serialize_dict(stats: dict[str, torch.Tensor | np.ndarray | dict]) -> dict: def serialize_dict(stats: dict[str, torch.Tensor | np.ndarray | dict]) -> dict:
"""Serialize a dictionary containing tensors or numpy arrays to be JSON-compatible. """Serialize a dictionary containing tensors or numpy arrays to be JSON-compatible.
+2 -10
View File
@@ -37,6 +37,8 @@ import torchvision
from datasets.features.features import register_feature from datasets.features.features import register_feature
from PIL import Image from PIL import Image
from lerobot.utils.import_utils import get_safe_default_codec
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# List of hardware encoders to probe for auto-selection. Availability depends on the platform and FFmpeg build. # List of hardware encoders to probe for auto-selection. Availability depends on the platform and FFmpeg build.
@@ -116,16 +118,6 @@ def resolve_vcodec(vcodec: str) -> str:
return "libsvtav1" return "libsvtav1"
def get_safe_default_codec():
if importlib.util.find_spec("torchcodec"):
return "torchcodec"
else:
logger.warning(
"'torchcodec' is not available in your platform, falling back to 'pyav' as a default decoder"
)
return "pyav"
def decode_video_frames( def decode_video_frames(
video_path: Path | str, video_path: Path | str,
timestamps: list[float], timestamps: list[float],
+2 -3
View File
@@ -23,13 +23,12 @@ import draccus
import torch import torch
from safetensors.torch import load_file, save_file from safetensors.torch import load_file, save_file
from lerobot.datasets.io_utils import write_json
from lerobot.datasets.utils import flatten_dict, unflatten_dict
from lerobot.utils.constants import ( from lerobot.utils.constants import (
OPTIMIZER_PARAM_GROUPS, OPTIMIZER_PARAM_GROUPS,
OPTIMIZER_STATE, OPTIMIZER_STATE,
) )
from lerobot.utils.io_utils import deserialize_json_into_object from lerobot.utils.io_utils import deserialize_json_into_object, write_json
from lerobot.utils.utils import flatten_dict, unflatten_dict
# Type alias for parameters accepted by optimizer build() methods. # Type alias for parameters accepted by optimizer build() methods.
# This matches PyTorch's optimizer signature while also supporting: # This matches PyTorch's optimizer signature while also supporting:
+1 -2
View File
@@ -23,9 +23,8 @@ import draccus
from torch.optim import Optimizer from torch.optim import Optimizer
from torch.optim.lr_scheduler import LambdaLR, LRScheduler from torch.optim.lr_scheduler import LambdaLR, LRScheduler
from lerobot.datasets.io_utils import write_json
from lerobot.utils.constants import SCHEDULER_STATE from lerobot.utils.constants import SCHEDULER_STATE
from lerobot.utils.io_utils import deserialize_json_into_object from lerobot.utils.io_utils import deserialize_json_into_object, write_json
@dataclass @dataclass
+6 -3
View File
@@ -18,14 +18,15 @@ from __future__ import annotations
import importlib import importlib
import logging import logging
from typing import Any, TypedDict, Unpack from typing import TYPE_CHECKING, Any, TypedDict, Unpack
import torch import torch
if TYPE_CHECKING:
from lerobot.datasets.dataset_metadata import LeRobotDatasetMetadata
from lerobot.configs.policies import PreTrainedConfig from lerobot.configs.policies import PreTrainedConfig
from lerobot.configs.types import FeatureType from lerobot.configs.types import FeatureType
from lerobot.datasets.dataset_metadata import LeRobotDatasetMetadata
from lerobot.datasets.feature_utils import dataset_to_policy_features
from lerobot.envs.configs import EnvConfig from lerobot.envs.configs import EnvConfig
from lerobot.envs.utils import env_to_policy_features from lerobot.envs.utils import env_to_policy_features
from lerobot.policies.act.configuration_act import ACTConfig from lerobot.policies.act.configuration_act import ACTConfig
@@ -494,6 +495,8 @@ def make_policy(
kwargs = {} kwargs = {}
if ds_meta is not None: if ds_meta is not None:
from lerobot.datasets.feature_utils import dataset_to_policy_features
features = dataset_to_policy_features(ds_meta.features) features = dataset_to_policy_features(ds_meta.features)
else: else:
if not cfg.pretrained_path: if not cfg.pretrained_path:
+2 -1
View File
@@ -23,7 +23,6 @@ from torch import nn
from lerobot.configs.policies import PreTrainedConfig from lerobot.configs.policies import PreTrainedConfig
from lerobot.configs.types import FeatureType, PolicyFeature from lerobot.configs.types import FeatureType, PolicyFeature
from lerobot.datasets.feature_utils import build_dataset_frame
from lerobot.types import PolicyAction, RobotAction, RobotObservation from lerobot.types import PolicyAction, RobotAction, RobotObservation
from lerobot.utils.constants import ACTION, OBS_STR from lerobot.utils.constants import ACTION, OBS_STR
@@ -163,6 +162,8 @@ def build_inference_frame(
Returns: Returns:
A dictionary of preprocessed tensors ready for model inference. A dictionary of preprocessed tensors ready for model inference.
""" """
from lerobot.datasets.feature_utils import build_dataset_frame
# Extracts the correct keys from the incoming raw observation # Extracts the correct keys from the incoming raw observation
observation = build_dataset_frame(ds_features, observation, prefix=OBS_STR) observation = build_dataset_frame(ds_features, observation, prefix=OBS_STR)
+5 -2
View File
@@ -19,14 +19,17 @@ from __future__ import annotations
from copy import deepcopy from copy import deepcopy
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any from typing import TYPE_CHECKING, Any
import torch import torch
from torch import Tensor from torch import Tensor
from lerobot.configs.types import FeatureType, NormalizationMode, PipelineFeatureType, PolicyFeature from lerobot.configs.types import FeatureType, NormalizationMode, PipelineFeatureType, PolicyFeature
from lerobot.datasets.lerobot_dataset import LeRobotDataset
from lerobot.types import EnvTransition, PolicyAction, TransitionKey from lerobot.types import EnvTransition, PolicyAction, TransitionKey
if TYPE_CHECKING:
from lerobot.datasets.lerobot_dataset import LeRobotDataset
from lerobot.utils.constants import ACTION from lerobot.utils.constants import ACTION
from .converters import from_tensor_to_numpy, to_tensor from .converters import from_tensor_to_numpy, to_tensor
+5 -1
View File
@@ -66,7 +66,6 @@ import time
from pathlib import Path from pathlib import Path
import numpy as np import numpy as np
import rerun as rr
import torch import torch
import torch.utils.data import torch.utils.data
import tqdm import tqdm
@@ -117,6 +116,11 @@ def visualize_dataset(
if mode not in ["local", "distant"]: if mode not in ["local", "distant"]:
raise ValueError(mode) raise ValueError(mode)
from lerobot.utils.import_utils import require_package
require_package("rerun-sdk", extra="viz", import_name="rerun")
import rerun as rr
spawn_local_viewer = mode == "local" and not save spawn_local_viewer = mode == "local" and not save
rr.init(f"{repo_id}/episode_{episode_index}", spawn=spawn_local_viewer) rr.init(f"{repo_id}/episode_{episode_index}", spawn=spawn_local_viewer)
+4 -1
View File
@@ -28,7 +28,10 @@ from pathlib import Path
def find_available_ports(): def find_available_ports():
from serial.tools import list_ports # Part of pyserial library from lerobot.utils.import_utils import require_package
require_package("pyserial", extra="hardware", import_name="serial")
from serial.tools import list_ports
if platform.system() == "Windows": if platform.system() == "Windows":
# List COM ports using pyserial # List COM ports using pyserial
@@ -37,7 +37,7 @@ from torchvision.transforms import ToPILImage
from lerobot.configs.default import DatasetConfig from lerobot.configs.default import DatasetConfig
from lerobot.datasets.lerobot_dataset import LeRobotDataset from lerobot.datasets.lerobot_dataset import LeRobotDataset
from lerobot.datasets.transforms import ( from lerobot.transforms import (
ImageTransforms, ImageTransforms,
ImageTransformsConfig, ImageTransformsConfig,
make_transform_from_config, make_transform_from_config,
+2 -4
View File
@@ -56,8 +56,6 @@ import time
from dataclasses import asdict, dataclass from dataclasses import asdict, dataclass
from pprint import pformat from pprint import pformat
import rerun as rr
from lerobot.cameras.opencv.configuration_opencv import OpenCVCameraConfig # noqa: F401 from lerobot.cameras.opencv.configuration_opencv import OpenCVCameraConfig # noqa: F401
from lerobot.cameras.realsense.configuration_realsense import RealSenseCameraConfig # noqa: F401 from lerobot.cameras.realsense.configuration_realsense import RealSenseCameraConfig # noqa: F401
from lerobot.cameras.zmq.configuration_zmq import ZMQCameraConfig # noqa: F401 from lerobot.cameras.zmq.configuration_zmq import ZMQCameraConfig # noqa: F401
@@ -103,7 +101,7 @@ from lerobot.teleoperators import ( # noqa: F401
from lerobot.utils.import_utils import register_third_party_plugins from lerobot.utils.import_utils import register_third_party_plugins
from lerobot.utils.robot_utils import precise_sleep from lerobot.utils.robot_utils import precise_sleep
from lerobot.utils.utils import init_logging, move_cursor_up from lerobot.utils.utils import init_logging, move_cursor_up
from lerobot.utils.visualization_utils import init_rerun, log_rerun_data from lerobot.utils.visualization_utils import init_rerun, log_rerun_data, shutdown_rerun
@dataclass @dataclass
@@ -240,7 +238,7 @@ def teleoperate(cfg: TeleoperateConfig):
pass pass
finally: finally:
if cfg.display_data: if cfg.display_data:
rr.rerun_shutdown() shutdown_rerun()
teleop.disconnect() teleop.disconnect()
robot.disconnect() robot.disconnect()
+4
View File
@@ -20,6 +20,10 @@ from contextlib import nullcontext
from pprint import pformat from pprint import pformat
from typing import Any from typing import Any
from lerobot.utils.import_utils import require_package
require_package("accelerate", extra="training")
import torch import torch
from accelerate import Accelerator from accelerate import Accelerator
from termcolor import colored from termcolor import colored
+13 -6
View File
@@ -12,25 +12,25 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from __future__ import annotations
######################################################################################## ########################################################################################
# Utilities # Utilities
######################################################################################## ########################################################################################
import logging import logging
import traceback import traceback
from contextlib import nullcontext from contextlib import nullcontext
from copy import copy from copy import copy
from functools import cache from functools import cache
from typing import Any from typing import TYPE_CHECKING, Any
import numpy as np import numpy as np
import torch import torch
from deepdiff import DeepDiff
from lerobot.datasets.lerobot_dataset import LeRobotDataset
from lerobot.datasets.utils import DEFAULT_FEATURES
from lerobot.policies.pretrained import PreTrainedPolicy from lerobot.policies.pretrained import PreTrainedPolicy
if TYPE_CHECKING:
from lerobot.datasets.lerobot_dataset import LeRobotDataset
from lerobot.policies.utils import prepare_observation_for_inference from lerobot.policies.utils import prepare_observation_for_inference
from lerobot.processor import PolicyProcessorPipeline from lerobot.processor import PolicyProcessorPipeline
from lerobot.robots import Robot from lerobot.robots import Robot
@@ -218,6 +218,13 @@ def sanity_check_dataset_robot_compatibility(
Raises: Raises:
ValueError: If any of the checked metadata fields do not match. ValueError: If any of the checked metadata fields do not match.
""" """
from lerobot.utils.import_utils import require_package
require_package("deepdiff", extra="hardware")
from deepdiff import DeepDiff
from lerobot.datasets.utils import DEFAULT_FEATURES
fields = [ fields = [
("robot_type", dataset.meta.robot_type, robot.robot_type), ("robot_type", dataset.meta.robot_type, robot.robot_type),
("fps", dataset.fps, fps), ("fps", dataset.fps, fps),
+18
View File
@@ -69,6 +69,24 @@ def is_package_available(
return package_exists return package_exists
def get_safe_default_codec():
if importlib.util.find_spec("torchcodec"):
return "torchcodec"
else:
logging.warning(
"'torchcodec' is not available in your platform, falling back to 'pyav' as a default decoder"
)
return "pyav"
def require_package(pkg_name: str, extra: str, import_name: str | None = None) -> None:
"""Raise an informative ImportError if a package required by an optional feature is missing."""
if not is_package_available(pkg_name, import_name):
raise ImportError(
f"'{pkg_name}' is required but not installed. Install it with: pip install 'lerobot[{extra}]'"
)
_transformers_available = is_package_available("transformers") _transformers_available = is_package_available("transformers")
_peft_available = is_package_available("peft") _peft_available = is_package_available("peft")
_scipy_available = is_package_available("scipy") _scipy_available = is_package_available("scipy")
+53 -10
View File
@@ -14,21 +14,64 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import json import json
import warnings
from pathlib import Path from pathlib import Path
from typing import Any
import imageio
JsonLike = str | int | float | bool | None | list["JsonLike"] | dict[str, "JsonLike"] | tuple["JsonLike", ...] JsonLike = str | int | float | bool | None | list["JsonLike"] | dict[str, "JsonLike"] | tuple["JsonLike", ...]
def write_video(video_path, stacked_frames, fps): def load_json(fpath: Path) -> Any:
# Filter out DeprecationWarnings raised from pkg_resources """Load data from a JSON file.
with warnings.catch_warnings():
warnings.filterwarnings( Args:
"ignore", "pkg_resources is deprecated as an API", category=DeprecationWarning fpath (Path): Path to the JSON file.
)
imageio.mimsave(video_path, stacked_frames, fps=fps) Returns:
Any: The data loaded from the JSON file.
"""
with open(fpath) as f:
return json.load(f)
def write_json(data: dict, fpath: Path) -> None:
"""Write data to a JSON file.
Creates parent directories if they don't exist.
Args:
data (dict): The dictionary to write.
fpath (Path): The path to the output JSON file.
"""
fpath.parent.mkdir(exist_ok=True, parents=True)
with open(fpath, "w") as f:
json.dump(data, f, indent=4, ensure_ascii=False)
def write_video(video_path: str | Path, stacked_frames: list, fps: int) -> None:
"""Write a sequence of RGB frames to an MP4 video file using libx264.
Args:
video_path: Output file path.
stacked_frames: List of HWC uint8 numpy arrays (RGB).
fps: Frames per second for the output video.
"""
import av
with av.open(str(video_path), mode="w") as container:
height, width = stacked_frames[0].shape[:2]
# Ensure dimensions are even for yuv420p compatibility
height = height if height % 2 == 0 else height - 1
width = width if width % 2 == 0 else width - 1
stream = container.add_stream("libx264", rate=fps)
stream.width = width
stream.height = height
stream.pix_fmt = "yuv420p"
for frame_array in stacked_frames:
frame = av.VideoFrame.from_ndarray(frame_array, format="rgb24")
for packet in stream.encode(frame):
container.mux(packet)
for packet in stream.encode():
container.mux(packet)
def deserialize_json_into_object[T: JsonLike](fpath: Path, obj: T) -> T: def deserialize_json_into_object[T: JsonLike](fpath: Path, obj: T) -> T:
+1 -1
View File
@@ -23,8 +23,8 @@ import numpy as np
import torch import torch
from safetensors.torch import load_file, save_file from safetensors.torch import load_file, save_file
from lerobot.datasets.utils import flatten_dict, unflatten_dict
from lerobot.utils.constants import RNG_STATE from lerobot.utils.constants import RNG_STATE
from lerobot.utils.utils import flatten_dict, unflatten_dict
def serialize_python_rng_state() -> dict[str, torch.Tensor]: def serialize_python_rng_state() -> dict[str, torch.Tensor]:
+1 -1
View File
@@ -19,7 +19,6 @@ from torch.optim import Optimizer
from torch.optim.lr_scheduler import LRScheduler from torch.optim.lr_scheduler import LRScheduler
from lerobot.configs.train import TrainPipelineConfig from lerobot.configs.train import TrainPipelineConfig
from lerobot.datasets.io_utils import load_json, write_json
from lerobot.optim.optimizers import load_optimizer_state, save_optimizer_state from lerobot.optim.optimizers import load_optimizer_state, save_optimizer_state
from lerobot.optim.schedulers import load_scheduler_state, save_scheduler_state from lerobot.optim.schedulers import load_scheduler_state, save_scheduler_state
from lerobot.policies.pretrained import PreTrainedPolicy from lerobot.policies.pretrained import PreTrainedPolicy
@@ -31,6 +30,7 @@ from lerobot.utils.constants import (
TRAINING_STATE_DIR, TRAINING_STATE_DIR,
TRAINING_STEP, TRAINING_STEP,
) )
from lerobot.utils.io_utils import load_json, write_json
from lerobot.utils.random_utils import load_rng_state, save_rng_state from lerobot.utils.random_utils import load_rng_state, save_rng_state
+53
View File
@@ -199,6 +199,59 @@ def get_elapsed_time_in_days_hours_minutes_seconds(elapsed_time_s: float):
return days, hours, minutes, seconds return days, hours, minutes, seconds
def flatten_dict(d: dict, parent_key: str = "", sep: str = "/") -> dict:
"""Flatten a nested dictionary by joining keys with a separator.
Example:
>>> dct = {"a": {"b": 1, "c": {"d": 2}}, "e": 3}
>>> print(flatten_dict(dct))
{'a/b': 1, 'a/c/d': 2, 'e': 3}
Args:
d (dict): The dictionary to flatten.
parent_key (str): The base key to prepend to the keys in this level.
sep (str): The separator to use between keys.
Returns:
dict: A flattened dictionary.
"""
items = []
for k, v in d.items():
new_key = f"{parent_key}{sep}{k}" if parent_key else k
if isinstance(v, dict):
items.extend(flatten_dict(v, new_key, sep=sep).items())
else:
items.append((new_key, v))
return dict(items)
def unflatten_dict(d: dict, sep: str = "/") -> dict:
"""Unflatten a dictionary with delimited keys into a nested dictionary.
Example:
>>> flat_dct = {"a/b": 1, "a/c/d": 2, "e": 3}
>>> print(unflatten_dict(flat_dct))
{'a': {'b': 1, 'c': {'d': 2}}, 'e': 3}
Args:
d (dict): A dictionary with flattened keys.
sep (str): The separator used in the keys.
Returns:
dict: A nested dictionary.
"""
outdict = {}
for key, value in d.items():
parts = key.split(sep)
d_inner = outdict
for part in parts[:-1]:
if part not in d_inner:
d_inner[part] = {}
d_inner = d_inner[part]
d_inner[parts[-1]] = value
return outdict
class SuppressProgressBars: class SuppressProgressBars:
""" """
Context manager to suppress progress bars. Context manager to suppress progress bars.
+13 -3
View File
@@ -16,11 +16,16 @@ import numbers
import os import os
import numpy as np import numpy as np
import rerun as rr
from lerobot.types import RobotAction, RobotObservation from lerobot.utils.import_utils import require_package
from .constants import ACTION, ACTION_PREFIX, OBS_PREFIX, OBS_STR require_package("rerun-sdk", extra="viz", import_name="rerun")
import rerun as rr # noqa: E402
from lerobot.types import RobotAction, RobotObservation # noqa: E402
from .constants import ACTION, ACTION_PREFIX, OBS_PREFIX, OBS_STR # noqa: E402
def init_rerun( def init_rerun(
@@ -44,6 +49,11 @@ def init_rerun(
rr.spawn(memory_limit=memory_limit) rr.spawn(memory_limit=memory_limit)
def shutdown_rerun() -> None:
"""Shuts down the Rerun SDK gracefully."""
rr.rerun_shutdown()
def _is_scalar(x): def _is_scalar(x):
return isinstance(x, (float | numbers.Real | np.integer | np.floating)) or ( return isinstance(x, (float | numbers.Real | np.integer | np.floating)) or (
isinstance(x, np.ndarray) and x.ndim == 0 isinstance(x, np.ndarray) and x.ndim == 0
@@ -19,7 +19,7 @@ import torch
from safetensors.torch import save_file from safetensors.torch import save_file
from lerobot.datasets.lerobot_dataset import LeRobotDataset from lerobot.datasets.lerobot_dataset import LeRobotDataset
from lerobot.datasets.transforms import ( from lerobot.transforms import (
ImageTransformConfig, ImageTransformConfig,
ImageTransforms, ImageTransforms,
ImageTransformsConfig, ImageTransformsConfig,
+2 -2
View File
@@ -24,7 +24,7 @@ import torch
from lerobot.configs.types import PolicyFeature from lerobot.configs.types import PolicyFeature
from lerobot.utils.constants import OBS_STATE from lerobot.utils.constants import OBS_STATE
from tests.utils import require_package from tests.utils import skip_if_package_missing
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# Test fixtures # Test fixtures
@@ -62,7 +62,7 @@ class MockPolicy:
@pytest.fixture @pytest.fixture
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def policy_server(): def policy_server():
"""Fresh `PolicyServer` instance with a stubbed-out policy model.""" """Fresh `PolicyServer` instance with a stubbed-out policy model."""
# Import only when the test actually runs (after decorator check) # Import only when the test actually runs (after decorator check)
+1 -1
View File
@@ -16,7 +16,7 @@
"""Contract tests for DatasetReader.""" """Contract tests for DatasetReader."""
from lerobot.datasets.dataset_reader import DatasetReader from lerobot.datasets.dataset_reader import DatasetReader
from lerobot.datasets.video_utils import get_safe_default_codec from lerobot.utils.import_utils import get_safe_default_codec
# ── Loading ────────────────────────────────────────────────────────── # ── Loading ──────────────────────────────────────────────────────────
+1 -1
View File
@@ -35,7 +35,6 @@ from lerobot.datasets.image_writer import image_array_to_pil_image
from lerobot.datasets.io_utils import hf_transform_to_torch from lerobot.datasets.io_utils import hf_transform_to_torch
from lerobot.datasets.lerobot_dataset import LeRobotDataset from lerobot.datasets.lerobot_dataset import LeRobotDataset
from lerobot.datasets.multi_dataset import MultiLeRobotDataset from lerobot.datasets.multi_dataset import MultiLeRobotDataset
from lerobot.datasets.transforms import ImageTransforms, ImageTransformsConfig
from lerobot.datasets.utils import ( from lerobot.datasets.utils import (
DEFAULT_CHUNK_SIZE, DEFAULT_CHUNK_SIZE,
DEFAULT_DATA_FILE_SIZE_IN_MB, DEFAULT_DATA_FILE_SIZE_IN_MB,
@@ -46,6 +45,7 @@ from lerobot.datasets.video_utils import VALID_VIDEO_CODECS
from lerobot.envs.factory import make_env_config from lerobot.envs.factory import make_env_config
from lerobot.policies.factory import make_policy_config from lerobot.policies.factory import make_policy_config
from lerobot.robots import make_robot_from_config from lerobot.robots import make_robot_from_config
from lerobot.transforms import ImageTransforms, ImageTransformsConfig
from lerobot.utils.constants import ACTION, DONE, OBS_IMAGES, OBS_STATE, OBS_STR, REWARD from lerobot.utils.constants import ACTION, DONE, OBS_IMAGES, OBS_STATE, OBS_STR, REWARD
from tests.fixtures.constants import DUMMY_CHW, DUMMY_HWC, DUMMY_REPO_ID from tests.fixtures.constants import DUMMY_CHW, DUMMY_HWC, DUMMY_REPO_ID
from tests.mocks.mock_robot import MockRobotConfig from tests.mocks.mock_robot import MockRobotConfig
+5 -5
View File
@@ -21,7 +21,11 @@ from safetensors.torch import load_file
from torchvision.transforms import v2 from torchvision.transforms import v2
from torchvision.transforms.v2 import functional as F # noqa: N812 from torchvision.transforms.v2 import functional as F # noqa: N812
from lerobot.datasets.transforms import ( from lerobot.scripts.lerobot_imgtransform_viz import (
save_all_transforms,
save_each_transform,
)
from lerobot.transforms import (
ImageTransformConfig, ImageTransformConfig,
ImageTransforms, ImageTransforms,
ImageTransformsConfig, ImageTransformsConfig,
@@ -29,10 +33,6 @@ from lerobot.datasets.transforms import (
SharpnessJitter, SharpnessJitter,
make_transform_from_config, make_transform_from_config,
) )
from lerobot.scripts.lerobot_imgtransform_viz import (
save_all_transforms,
save_each_transform,
)
from lerobot.utils.random_utils import seeded_context from lerobot.utils.random_utils import seeded_context
from tests.artifacts.image_transforms.save_image_transforms_to_safetensors import ARTIFACT_DIR from tests.artifacts.image_transforms.save_image_transforms_to_safetensors import ARTIFACT_DIR
from tests.utils import require_x86_64_kernel from tests.utils import require_x86_64_kernel
@@ -21,7 +21,7 @@ from lerobot.configs.types import FeatureType, NormalizationMode, PolicyFeature
from lerobot.policies.sac.reward_model.configuration_classifier import RewardClassifierConfig from lerobot.policies.sac.reward_model.configuration_classifier import RewardClassifierConfig
from lerobot.policies.sac.reward_model.modeling_classifier import ClassifierOutput from lerobot.policies.sac.reward_model.modeling_classifier import ClassifierOutput
from lerobot.utils.constants import OBS_IMAGE, REWARD from lerobot.utils.constants import OBS_IMAGE, REWARD
from tests.utils import require_package from tests.utils import skip_if_package_missing
def test_classifier_output(): def test_classifier_output():
@@ -37,7 +37,7 @@ def test_classifier_output():
) )
@require_package("transformers") @skip_if_package_missing("transformers")
@pytest.mark.skip( @pytest.mark.skip(
reason="helper2424/resnet10 needs to be updated to work with the latest version of transformers" reason="helper2424/resnet10 needs to be updated to work with the latest version of transformers"
) )
@@ -81,7 +81,7 @@ def test_binary_classifier_with_default_params():
assert not torch.isnan(output.hidden_states).any(), "Tensor contains NaN values" assert not torch.isnan(output.hidden_states).any(), "Tensor contains NaN values"
@require_package("transformers") @skip_if_package_missing("transformers")
@pytest.mark.skip( @pytest.mark.skip(
reason="helper2424/resnet10 needs to be updated to work with the latest version of transformers" reason="helper2424/resnet10 needs to be updated to work with the latest version of transformers"
) )
@@ -123,7 +123,7 @@ def test_multiclass_classifier():
assert not torch.isnan(output.hidden_states).any(), "Tensor contains NaN values" assert not torch.isnan(output.hidden_states).any(), "Tensor contains NaN values"
@require_package("transformers") @skip_if_package_missing("transformers")
@pytest.mark.skip( @pytest.mark.skip(
reason="helper2424/resnet10 needs to be updated to work with the latest version of transformers" reason="helper2424/resnet10 needs to be updated to work with the latest version of transformers"
) )
@@ -138,7 +138,7 @@ def test_default_device():
assert p.device == torch.device("cpu") assert p.device == torch.device("cpu")
@require_package("transformers") @skip_if_package_missing("transformers")
@pytest.mark.skip( @pytest.mark.skip(
reason="helper2424/resnet10 needs to be updated to work with the latest version of transformers" reason="helper2424/resnet10 needs to be updated to work with the latest version of transformers"
) )
+6 -6
View File
@@ -24,10 +24,10 @@ from lerobot.policies.factory import make_pre_post_processors # noqa: E402
from lerobot.policies.rtc.configuration_rtc import RTCConfig # noqa: E402 from lerobot.policies.rtc.configuration_rtc import RTCConfig # noqa: E402
from lerobot.policies.smolvla.configuration_smolvla import SmolVLAConfig # noqa: F401 from lerobot.policies.smolvla.configuration_smolvla import SmolVLAConfig # noqa: F401
from lerobot.utils.random_utils import set_seed # noqa: E402 from lerobot.utils.random_utils import set_seed # noqa: E402
from tests.utils import require_cuda, require_package # noqa: E402 from tests.utils import require_cuda, skip_if_package_missing # noqa: E402
@require_package("transformers") @skip_if_package_missing("transformers")
@require_cuda @require_cuda
def test_smolvla_rtc_initialization(): def test_smolvla_rtc_initialization():
from lerobot.policies.smolvla.modeling_smolvla import SmolVLAPolicy # noqa: F401 from lerobot.policies.smolvla.modeling_smolvla import SmolVLAPolicy # noqa: F401
@@ -65,7 +65,7 @@ def test_smolvla_rtc_initialization():
print("✓ SmolVLA RTC initialization: Test passed") print("✓ SmolVLA RTC initialization: Test passed")
@require_package("transformers") @skip_if_package_missing("transformers")
@require_cuda @require_cuda
def test_smolvla_rtc_initialization_without_rtc_config(): def test_smolvla_rtc_initialization_without_rtc_config():
from lerobot.policies.smolvla.modeling_smolvla import SmolVLAPolicy # noqa: F401 from lerobot.policies.smolvla.modeling_smolvla import SmolVLAPolicy # noqa: F401
@@ -87,7 +87,7 @@ def test_smolvla_rtc_initialization_without_rtc_config():
print("✓ SmolVLA RTC initialization without RTC config: Test passed") print("✓ SmolVLA RTC initialization without RTC config: Test passed")
@require_package("transformers") @skip_if_package_missing("transformers")
@require_cuda @require_cuda
@pytest.mark.skipif(True, reason="Requires pretrained SmolVLA model weights") @pytest.mark.skipif(True, reason="Requires pretrained SmolVLA model weights")
def test_smolvla_rtc_inference_with_prev_chunk(): def test_smolvla_rtc_inference_with_prev_chunk():
@@ -170,7 +170,7 @@ def test_smolvla_rtc_inference_with_prev_chunk():
print("✓ SmolVLA RTC inference with prev_chunk: Test passed") print("✓ SmolVLA RTC inference with prev_chunk: Test passed")
@require_package("transformers") @skip_if_package_missing("transformers")
@require_cuda @require_cuda
@pytest.mark.skipif(True, reason="Requires pretrained SmolVLA model weights") @pytest.mark.skipif(True, reason="Requires pretrained SmolVLA model weights")
def test_smolvla_rtc_inference_without_prev_chunk(): def test_smolvla_rtc_inference_without_prev_chunk():
@@ -244,7 +244,7 @@ def test_smolvla_rtc_inference_without_prev_chunk():
print("✓ SmolVLA RTC inference without prev_chunk: Test passed") print("✓ SmolVLA RTC inference without prev_chunk: Test passed")
@require_package("transformers") @skip_if_package_missing("transformers")
@require_cuda @require_cuda
@pytest.mark.skipif(True, reason="Requires pretrained SmolVLA model weights") @pytest.mark.skipif(True, reason="Requires pretrained SmolVLA model weights")
def test_smolvla_rtc_validation_rules(): def test_smolvla_rtc_validation_rules():
+59 -59
View File
@@ -36,7 +36,7 @@ from lerobot.utils.constants import (
OBS_LANGUAGE_SUBTASK_TOKENS, OBS_LANGUAGE_SUBTASK_TOKENS,
OBS_STATE, OBS_STATE,
) )
from tests.utils import require_package from tests.utils import skip_if_package_missing
class MockTokenizer: class MockTokenizer:
@@ -94,7 +94,7 @@ def mock_tokenizer():
return MockTokenizer(vocab_size=100) return MockTokenizer(vocab_size=100)
@require_package("transformers") @skip_if_package_missing("transformers")
@patch("lerobot.processor.tokenizer_processor.AutoTokenizer") @patch("lerobot.processor.tokenizer_processor.AutoTokenizer")
def test_basic_tokenization(mock_auto_tokenizer): def test_basic_tokenization(mock_auto_tokenizer):
"""Test basic string tokenization functionality.""" """Test basic string tokenization functionality."""
@@ -129,7 +129,7 @@ def test_basic_tokenization(mock_auto_tokenizer):
assert attention_mask.shape == (10,) assert attention_mask.shape == (10,)
@require_package("transformers") @skip_if_package_missing("transformers")
def test_basic_tokenization_with_tokenizer_object(): def test_basic_tokenization_with_tokenizer_object():
"""Test basic string tokenization functionality using tokenizer object directly.""" """Test basic string tokenization functionality using tokenizer object directly."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -161,7 +161,7 @@ def test_basic_tokenization_with_tokenizer_object():
assert attention_mask.shape == (10,) assert attention_mask.shape == (10,)
@require_package("transformers") @skip_if_package_missing("transformers")
@patch("lerobot.processor.tokenizer_processor.AutoTokenizer") @patch("lerobot.processor.tokenizer_processor.AutoTokenizer")
def test_list_of_strings_tokenization(mock_auto_tokenizer): def test_list_of_strings_tokenization(mock_auto_tokenizer):
"""Test tokenization of a list of strings.""" """Test tokenization of a list of strings."""
@@ -189,7 +189,7 @@ def test_list_of_strings_tokenization(mock_auto_tokenizer):
assert attention_mask.shape == (2, 8) assert attention_mask.shape == (2, 8)
@require_package("transformers") @skip_if_package_missing("transformers")
@patch("lerobot.processor.tokenizer_processor.AutoTokenizer") @patch("lerobot.processor.tokenizer_processor.AutoTokenizer")
def test_tuple_of_strings_tokenization(mock_auto_tokenizer): def test_tuple_of_strings_tokenization(mock_auto_tokenizer):
"""Test tokenization of a tuple of strings (returned by VectorEnv.call()).""" """Test tokenization of a tuple of strings (returned by VectorEnv.call())."""
@@ -213,7 +213,7 @@ def test_tuple_of_strings_tokenization(mock_auto_tokenizer):
assert attention_mask.shape == (2, 8) assert attention_mask.shape == (2, 8)
@require_package("transformers") @skip_if_package_missing("transformers")
@patch("lerobot.processor.tokenizer_processor.AutoTokenizer") @patch("lerobot.processor.tokenizer_processor.AutoTokenizer")
def test_custom_keys(mock_auto_tokenizer): def test_custom_keys(mock_auto_tokenizer):
"""Test using custom task_key.""" """Test using custom task_key."""
@@ -239,7 +239,7 @@ def test_custom_keys(mock_auto_tokenizer):
assert tokens.shape == (5,) assert tokens.shape == (5,)
@require_package("transformers") @skip_if_package_missing("transformers")
@patch("lerobot.processor.tokenizer_processor.AutoTokenizer") @patch("lerobot.processor.tokenizer_processor.AutoTokenizer")
def test_none_complementary_data(mock_auto_tokenizer): def test_none_complementary_data(mock_auto_tokenizer):
"""Test handling of None complementary_data.""" """Test handling of None complementary_data."""
@@ -255,7 +255,7 @@ def test_none_complementary_data(mock_auto_tokenizer):
processor(transition) processor(transition)
@require_package("transformers") @skip_if_package_missing("transformers")
@patch("lerobot.processor.tokenizer_processor.AutoTokenizer") @patch("lerobot.processor.tokenizer_processor.AutoTokenizer")
def test_missing_task_key(mock_auto_tokenizer): def test_missing_task_key(mock_auto_tokenizer):
"""Test handling when task key is missing.""" """Test handling when task key is missing."""
@@ -270,7 +270,7 @@ def test_missing_task_key(mock_auto_tokenizer):
processor(transition) processor(transition)
@require_package("transformers") @skip_if_package_missing("transformers")
@patch("lerobot.processor.tokenizer_processor.AutoTokenizer") @patch("lerobot.processor.tokenizer_processor.AutoTokenizer")
def test_none_task_value(mock_auto_tokenizer): def test_none_task_value(mock_auto_tokenizer):
"""Test handling when task value is None.""" """Test handling when task value is None."""
@@ -285,7 +285,7 @@ def test_none_task_value(mock_auto_tokenizer):
processor(transition) processor(transition)
@require_package("transformers") @skip_if_package_missing("transformers")
@patch("lerobot.processor.tokenizer_processor.AutoTokenizer") @patch("lerobot.processor.tokenizer_processor.AutoTokenizer")
def test_unsupported_task_type(mock_auto_tokenizer): def test_unsupported_task_type(mock_auto_tokenizer):
"""Test handling of unsupported task types.""" """Test handling of unsupported task types."""
@@ -307,14 +307,14 @@ def test_unsupported_task_type(mock_auto_tokenizer):
processor(transition) processor(transition)
@require_package("transformers") @skip_if_package_missing("transformers")
def test_no_tokenizer_error(): def test_no_tokenizer_error():
"""Test that ValueError is raised when neither tokenizer nor tokenizer_name is provided.""" """Test that ValueError is raised when neither tokenizer nor tokenizer_name is provided."""
with pytest.raises(ValueError, match="Either 'tokenizer' or 'tokenizer_name' must be provided"): with pytest.raises(ValueError, match="Either 'tokenizer' or 'tokenizer_name' must be provided"):
TokenizerProcessorStep() TokenizerProcessorStep()
@require_package("transformers") @skip_if_package_missing("transformers")
def test_invalid_tokenizer_name_error(): def test_invalid_tokenizer_name_error():
"""Test that error is raised when invalid tokenizer_name is provided.""" """Test that error is raised when invalid tokenizer_name is provided."""
with patch("lerobot.processor.tokenizer_processor.AutoTokenizer") as mock_auto_tokenizer: with patch("lerobot.processor.tokenizer_processor.AutoTokenizer") as mock_auto_tokenizer:
@@ -325,7 +325,7 @@ def test_invalid_tokenizer_name_error():
TokenizerProcessorStep(tokenizer_name="invalid-tokenizer") TokenizerProcessorStep(tokenizer_name="invalid-tokenizer")
@require_package("transformers") @skip_if_package_missing("transformers")
@patch("lerobot.processor.tokenizer_processor.AutoTokenizer") @patch("lerobot.processor.tokenizer_processor.AutoTokenizer")
def test_get_config_with_tokenizer_name(mock_auto_tokenizer): def test_get_config_with_tokenizer_name(mock_auto_tokenizer):
"""Test configuration serialization when using tokenizer_name.""" """Test configuration serialization when using tokenizer_name."""
@@ -354,7 +354,7 @@ def test_get_config_with_tokenizer_name(mock_auto_tokenizer):
assert config == expected assert config == expected
@require_package("transformers") @skip_if_package_missing("transformers")
def test_get_config_with_tokenizer_object(): def test_get_config_with_tokenizer_object():
"""Test configuration serialization when using tokenizer object.""" """Test configuration serialization when using tokenizer object."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -382,7 +382,7 @@ def test_get_config_with_tokenizer_object():
assert "tokenizer_name" not in config assert "tokenizer_name" not in config
@require_package("transformers") @skip_if_package_missing("transformers")
@patch("lerobot.processor.tokenizer_processor.AutoTokenizer") @patch("lerobot.processor.tokenizer_processor.AutoTokenizer")
def test_state_dict_methods(mock_auto_tokenizer): def test_state_dict_methods(mock_auto_tokenizer):
"""Test state_dict and load_state_dict methods.""" """Test state_dict and load_state_dict methods."""
@@ -399,7 +399,7 @@ def test_state_dict_methods(mock_auto_tokenizer):
processor.load_state_dict({}) processor.load_state_dict({})
@require_package("transformers") @skip_if_package_missing("transformers")
@patch("lerobot.processor.tokenizer_processor.AutoTokenizer") @patch("lerobot.processor.tokenizer_processor.AutoTokenizer")
def test_reset_method(mock_auto_tokenizer): def test_reset_method(mock_auto_tokenizer):
"""Test reset method.""" """Test reset method."""
@@ -412,7 +412,7 @@ def test_reset_method(mock_auto_tokenizer):
processor.reset() processor.reset()
@require_package("transformers") @skip_if_package_missing("transformers")
@patch("lerobot.processor.tokenizer_processor.AutoTokenizer") @patch("lerobot.processor.tokenizer_processor.AutoTokenizer")
def test_integration_with_robot_processor(mock_auto_tokenizer): def test_integration_with_robot_processor(mock_auto_tokenizer):
"""Test integration with RobotProcessor.""" """Test integration with RobotProcessor."""
@@ -449,7 +449,7 @@ def test_integration_with_robot_processor(mock_auto_tokenizer):
assert torch.equal(result[TransitionKey.ACTION], transition[TransitionKey.ACTION]) assert torch.equal(result[TransitionKey.ACTION], transition[TransitionKey.ACTION])
@require_package("transformers") @skip_if_package_missing("transformers")
@patch("lerobot.processor.tokenizer_processor.AutoTokenizer") @patch("lerobot.processor.tokenizer_processor.AutoTokenizer")
def test_save_and_load_pretrained_with_tokenizer_name(mock_auto_tokenizer): def test_save_and_load_pretrained_with_tokenizer_name(mock_auto_tokenizer):
"""Test saving and loading processor with tokenizer_name.""" """Test saving and loading processor with tokenizer_name."""
@@ -489,7 +489,7 @@ def test_save_and_load_pretrained_with_tokenizer_name(mock_auto_tokenizer):
assert f"{OBS_LANGUAGE}.attention_mask" in result[TransitionKey.OBSERVATION] assert f"{OBS_LANGUAGE}.attention_mask" in result[TransitionKey.OBSERVATION]
@require_package("transformers") @skip_if_package_missing("transformers")
def test_save_and_load_pretrained_with_tokenizer_object(): def test_save_and_load_pretrained_with_tokenizer_object():
"""Test saving and loading processor with tokenizer object using overrides.""" """Test saving and loading processor with tokenizer object using overrides."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -528,7 +528,7 @@ def test_save_and_load_pretrained_with_tokenizer_object():
assert f"{OBS_LANGUAGE}.attention_mask" in result[TransitionKey.OBSERVATION] assert f"{OBS_LANGUAGE}.attention_mask" in result[TransitionKey.OBSERVATION]
@require_package("transformers") @skip_if_package_missing("transformers")
def test_registry_functionality(): def test_registry_functionality():
"""Test that the processor is properly registered.""" """Test that the processor is properly registered."""
from lerobot.processor import ProcessorStepRegistry from lerobot.processor import ProcessorStepRegistry
@@ -541,7 +541,7 @@ def test_registry_functionality():
assert retrieved_class is TokenizerProcessorStep assert retrieved_class is TokenizerProcessorStep
@require_package("transformers") @skip_if_package_missing("transformers")
def test_features_basic(): def test_features_basic():
"""Test basic feature contract functionality.""" """Test basic feature contract functionality."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -574,7 +574,7 @@ def test_features_basic():
assert attention_mask_feature.shape == (128,) assert attention_mask_feature.shape == (128,)
@require_package("transformers") @skip_if_package_missing("transformers")
def test_features_with_custom_max_length(): def test_features_with_custom_max_length():
"""Test feature contract with custom max_length.""" """Test feature contract with custom max_length."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -596,7 +596,7 @@ def test_features_with_custom_max_length():
assert attention_mask_feature.shape == (64,) assert attention_mask_feature.shape == (64,)
@require_package("transformers") @skip_if_package_missing("transformers")
def test_features_existing_features(): def test_features_existing_features():
"""Test feature contract when tokenized features already exist.""" """Test feature contract when tokenized features already exist."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -618,7 +618,7 @@ def test_features_existing_features():
assert output_features[PipelineFeatureType.OBSERVATION][f"{OBS_LANGUAGE}.attention_mask"].shape == (100,) assert output_features[PipelineFeatureType.OBSERVATION][f"{OBS_LANGUAGE}.attention_mask"].shape == (100,)
@require_package("transformers") @skip_if_package_missing("transformers")
@patch("lerobot.processor.tokenizer_processor.AutoTokenizer") @patch("lerobot.processor.tokenizer_processor.AutoTokenizer")
def test_tokenization_parameters(mock_auto_tokenizer): def test_tokenization_parameters(mock_auto_tokenizer):
"""Test that tokenization parameters are correctly passed to tokenizer.""" """Test that tokenization parameters are correctly passed to tokenizer."""
@@ -666,7 +666,7 @@ def test_tokenization_parameters(mock_auto_tokenizer):
assert tracking_tokenizer.last_call_kwargs["return_tensors"] == "pt" assert tracking_tokenizer.last_call_kwargs["return_tensors"] == "pt"
@require_package("transformers") @skip_if_package_missing("transformers")
@patch("lerobot.processor.tokenizer_processor.AutoTokenizer") @patch("lerobot.processor.tokenizer_processor.AutoTokenizer")
def test_preserves_other_complementary_data(mock_auto_tokenizer): def test_preserves_other_complementary_data(mock_auto_tokenizer):
"""Test that other complementary data fields are preserved.""" """Test that other complementary data fields are preserved."""
@@ -701,7 +701,7 @@ def test_preserves_other_complementary_data(mock_auto_tokenizer):
assert f"{OBS_LANGUAGE}.attention_mask" in observation assert f"{OBS_LANGUAGE}.attention_mask" in observation
@require_package("transformers") @skip_if_package_missing("transformers")
@patch("lerobot.processor.tokenizer_processor.AutoTokenizer") @patch("lerobot.processor.tokenizer_processor.AutoTokenizer")
def test_deterministic_tokenization(mock_auto_tokenizer): def test_deterministic_tokenization(mock_auto_tokenizer):
"""Test that tokenization is deterministic for the same input.""" """Test that tokenization is deterministic for the same input."""
@@ -729,7 +729,7 @@ def test_deterministic_tokenization(mock_auto_tokenizer):
assert torch.equal(attention_mask1, attention_mask2) assert torch.equal(attention_mask1, attention_mask2)
@require_package("transformers") @skip_if_package_missing("transformers")
@patch("lerobot.processor.tokenizer_processor.AutoTokenizer") @patch("lerobot.processor.tokenizer_processor.AutoTokenizer")
def test_empty_string_task(mock_auto_tokenizer): def test_empty_string_task(mock_auto_tokenizer):
"""Test handling of empty string task.""" """Test handling of empty string task."""
@@ -753,7 +753,7 @@ def test_empty_string_task(mock_auto_tokenizer):
assert tokens.shape == (8,) assert tokens.shape == (8,)
@require_package("transformers") @skip_if_package_missing("transformers")
@patch("lerobot.processor.tokenizer_processor.AutoTokenizer") @patch("lerobot.processor.tokenizer_processor.AutoTokenizer")
def test_very_long_task(mock_auto_tokenizer): def test_very_long_task(mock_auto_tokenizer):
"""Test handling of very long task strings.""" """Test handling of very long task strings."""
@@ -779,7 +779,7 @@ def test_very_long_task(mock_auto_tokenizer):
assert attention_mask.shape == (5,) assert attention_mask.shape == (5,)
@require_package("transformers") @skip_if_package_missing("transformers")
@patch("lerobot.processor.tokenizer_processor.AutoTokenizer") @patch("lerobot.processor.tokenizer_processor.AutoTokenizer")
def test_custom_padding_side(mock_auto_tokenizer): def test_custom_padding_side(mock_auto_tokenizer):
"""Test using custom padding_side parameter.""" """Test using custom padding_side parameter."""
@@ -833,7 +833,7 @@ def test_custom_padding_side(mock_auto_tokenizer):
assert tracking_tokenizer.padding_side_calls[-1] == "right" assert tracking_tokenizer.padding_side_calls[-1] == "right"
@require_package("transformers") @skip_if_package_missing("transformers")
def test_device_detection_cpu(): def test_device_detection_cpu():
"""Test that tokenized tensors stay on CPU when other tensors are on CPU.""" """Test that tokenized tensors stay on CPU when other tensors are on CPU."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -857,7 +857,7 @@ def test_device_detection_cpu():
@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA not available") @pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA not available")
@require_package("transformers") @skip_if_package_missing("transformers")
def test_device_detection_cuda(): def test_device_detection_cuda():
"""Test that tokenized tensors are moved to CUDA when other tensors are on CUDA.""" """Test that tokenized tensors are moved to CUDA when other tensors are on CUDA."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -882,7 +882,7 @@ def test_device_detection_cuda():
@pytest.mark.skipif(torch.cuda.device_count() < 2, reason="Requires at least 2 GPUs") @pytest.mark.skipif(torch.cuda.device_count() < 2, reason="Requires at least 2 GPUs")
@require_package("transformers") @skip_if_package_missing("transformers")
def test_device_detection_multi_gpu(): def test_device_detection_multi_gpu():
"""Test that tokenized tensors match device in multi-GPU setup.""" """Test that tokenized tensors match device in multi-GPU setup."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -906,7 +906,7 @@ def test_device_detection_multi_gpu():
assert attention_mask.device == device assert attention_mask.device == device
@require_package("transformers") @skip_if_package_missing("transformers")
def test_device_detection_no_tensors(): def test_device_detection_no_tensors():
"""Test that tokenized tensors stay on CPU when no other tensors exist.""" """Test that tokenized tensors stay on CPU when no other tensors exist."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -928,7 +928,7 @@ def test_device_detection_no_tensors():
assert attention_mask.device.type == "cpu" assert attention_mask.device.type == "cpu"
@require_package("transformers") @skip_if_package_missing("transformers")
def test_device_detection_mixed_devices(): def test_device_detection_mixed_devices():
"""Test device detection when tensors are on different devices (uses first found).""" """Test device detection when tensors are on different devices (uses first found)."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -956,7 +956,7 @@ def test_device_detection_mixed_devices():
@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA not available") @pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA not available")
@require_package("transformers") @skip_if_package_missing("transformers")
def test_device_detection_from_action(): def test_device_detection_from_action():
"""Test that device is detected from action tensor when no observation tensors exist.""" """Test that device is detected from action tensor when no observation tensors exist."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -979,7 +979,7 @@ def test_device_detection_from_action():
assert attention_mask.device.type == "cuda" assert attention_mask.device.type == "cuda"
@require_package("transformers") @skip_if_package_missing("transformers")
def test_device_detection_preserves_dtype(): def test_device_detection_preserves_dtype():
"""Test that device detection doesn't affect dtype of tokenized tensors.""" """Test that device detection doesn't affect dtype of tokenized tensors."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -1000,7 +1000,7 @@ def test_device_detection_preserves_dtype():
@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA not available") @pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA not available")
@require_package("transformers") @skip_if_package_missing("transformers")
@patch("lerobot.processor.tokenizer_processor.AutoTokenizer") @patch("lerobot.processor.tokenizer_processor.AutoTokenizer")
def test_integration_with_device_processor(mock_auto_tokenizer): def test_integration_with_device_processor(mock_auto_tokenizer):
"""Test that TokenizerProcessorStep works correctly with DeviceProcessorStep in pipeline.""" """Test that TokenizerProcessorStep works correctly with DeviceProcessorStep in pipeline."""
@@ -1039,7 +1039,7 @@ def test_integration_with_device_processor(mock_auto_tokenizer):
@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA not available") @pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA not available")
@require_package("transformers") @skip_if_package_missing("transformers")
def test_simulated_accelerate_scenario(): def test_simulated_accelerate_scenario():
"""Test scenario simulating Accelerate with data already on GPU.""" """Test scenario simulating Accelerate with data already on GPU."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -1077,7 +1077,7 @@ def test_simulated_accelerate_scenario():
# ============================================================================= # =============================================================================
@require_package("transformers") @skip_if_package_missing("transformers")
def test_get_subtask_missing_key(): def test_get_subtask_missing_key():
"""Test get_subtask returns None when subtask key is missing from complementary_data.""" """Test get_subtask returns None when subtask key is missing from complementary_data."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -1093,7 +1093,7 @@ def test_get_subtask_missing_key():
assert result is None assert result is None
@require_package("transformers") @skip_if_package_missing("transformers")
def test_get_subtask_none_value(): def test_get_subtask_none_value():
"""Test get_subtask returns None when subtask value is None.""" """Test get_subtask returns None when subtask value is None."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -1109,7 +1109,7 @@ def test_get_subtask_none_value():
assert result is None assert result is None
@require_package("transformers") @skip_if_package_missing("transformers")
def test_get_subtask_none_complementary_data(): def test_get_subtask_none_complementary_data():
"""Test get_subtask returns None when complementary_data is None.""" """Test get_subtask returns None when complementary_data is None."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -1125,7 +1125,7 @@ def test_get_subtask_none_complementary_data():
assert result is None assert result is None
@require_package("transformers") @skip_if_package_missing("transformers")
def test_get_subtask_string(): def test_get_subtask_string():
"""Test get_subtask returns list with single string when subtask is a string.""" """Test get_subtask returns list with single string when subtask is a string."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -1143,7 +1143,7 @@ def test_get_subtask_string():
assert len(result) == 1 assert len(result) == 1
@require_package("transformers") @skip_if_package_missing("transformers")
def test_get_subtask_list_of_strings(): def test_get_subtask_list_of_strings():
"""Test get_subtask returns the list when subtask is already a list of strings.""" """Test get_subtask returns the list when subtask is already a list of strings."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -1162,7 +1162,7 @@ def test_get_subtask_list_of_strings():
assert len(result) == 3 assert len(result) == 3
@require_package("transformers") @skip_if_package_missing("transformers")
def test_get_subtask_unsupported_type_integer(): def test_get_subtask_unsupported_type_integer():
"""Test get_subtask returns None when subtask is an unsupported type (integer).""" """Test get_subtask returns None when subtask is an unsupported type (integer)."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -1178,7 +1178,7 @@ def test_get_subtask_unsupported_type_integer():
assert result is None assert result is None
@require_package("transformers") @skip_if_package_missing("transformers")
def test_get_subtask_unsupported_type_mixed_list(): def test_get_subtask_unsupported_type_mixed_list():
"""Test get_subtask returns None when subtask is a list with mixed types.""" """Test get_subtask returns None when subtask is a list with mixed types."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -1194,7 +1194,7 @@ def test_get_subtask_unsupported_type_mixed_list():
assert result is None assert result is None
@require_package("transformers") @skip_if_package_missing("transformers")
def test_get_subtask_unsupported_type_dict(): def test_get_subtask_unsupported_type_dict():
"""Test get_subtask returns None when subtask is a dictionary.""" """Test get_subtask returns None when subtask is a dictionary."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -1210,7 +1210,7 @@ def test_get_subtask_unsupported_type_dict():
assert result is None assert result is None
@require_package("transformers") @skip_if_package_missing("transformers")
def test_get_subtask_empty_string(): def test_get_subtask_empty_string():
"""Test get_subtask with empty string returns list with empty string.""" """Test get_subtask with empty string returns list with empty string."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -1226,7 +1226,7 @@ def test_get_subtask_empty_string():
assert result == [""] assert result == [""]
@require_package("transformers") @skip_if_package_missing("transformers")
def test_get_subtask_empty_list(): def test_get_subtask_empty_list():
"""Test get_subtask with empty list returns empty list.""" """Test get_subtask with empty list returns empty list."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -1247,7 +1247,7 @@ def test_get_subtask_empty_list():
# ============================================================================= # =============================================================================
@require_package("transformers") @skip_if_package_missing("transformers")
def test_subtask_tokenization_when_present(): def test_subtask_tokenization_when_present():
"""Test that subtask is tokenized and added to observation when present.""" """Test that subtask is tokenized and added to observation when present."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -1276,7 +1276,7 @@ def test_subtask_tokenization_when_present():
assert subtask_attention_mask.dtype == torch.bool assert subtask_attention_mask.dtype == torch.bool
@require_package("transformers") @skip_if_package_missing("transformers")
def test_subtask_tokenization_not_added_when_none(): def test_subtask_tokenization_not_added_when_none():
"""Test that subtask tokens are NOT added to observation when subtask is None.""" """Test that subtask tokens are NOT added to observation when subtask is None."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -1300,7 +1300,7 @@ def test_subtask_tokenization_not_added_when_none():
assert f"{OBS_LANGUAGE}.attention_mask" in observation assert f"{OBS_LANGUAGE}.attention_mask" in observation
@require_package("transformers") @skip_if_package_missing("transformers")
def test_subtask_tokenization_not_added_when_subtask_value_is_none(): def test_subtask_tokenization_not_added_when_subtask_value_is_none():
"""Test that subtask tokens are NOT added when subtask value is explicitly None.""" """Test that subtask tokens are NOT added when subtask value is explicitly None."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -1320,7 +1320,7 @@ def test_subtask_tokenization_not_added_when_subtask_value_is_none():
assert OBS_LANGUAGE_SUBTASK_ATTENTION_MASK not in observation assert OBS_LANGUAGE_SUBTASK_ATTENTION_MASK not in observation
@require_package("transformers") @skip_if_package_missing("transformers")
def test_subtask_tokenization_list_of_strings(): def test_subtask_tokenization_list_of_strings():
"""Test subtask tokenization with list of strings.""" """Test subtask tokenization with list of strings."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -1346,7 +1346,7 @@ def test_subtask_tokenization_list_of_strings():
assert subtask_attention_mask.shape == (2, 8) assert subtask_attention_mask.shape == (2, 8)
@require_package("transformers") @skip_if_package_missing("transformers")
def test_subtask_tokenization_device_cpu(): def test_subtask_tokenization_device_cpu():
"""Test that subtask tokens are on CPU when other tensors are on CPU.""" """Test that subtask tokens are on CPU when other tensors are on CPU."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -1372,7 +1372,7 @@ def test_subtask_tokenization_device_cpu():
@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA not available") @pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA not available")
@require_package("transformers") @skip_if_package_missing("transformers")
def test_subtask_tokenization_device_cuda(): def test_subtask_tokenization_device_cuda():
"""Test that subtask tokens are moved to CUDA when other tensors are on CUDA.""" """Test that subtask tokens are moved to CUDA when other tensors are on CUDA."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -1397,7 +1397,7 @@ def test_subtask_tokenization_device_cuda():
assert subtask_attention_mask.device.type == "cuda" assert subtask_attention_mask.device.type == "cuda"
@require_package("transformers") @skip_if_package_missing("transformers")
def test_subtask_tokenization_preserves_other_observation_data(): def test_subtask_tokenization_preserves_other_observation_data():
"""Test that subtask tokenization preserves other observation data.""" """Test that subtask tokenization preserves other observation data."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -1423,7 +1423,7 @@ def test_subtask_tokenization_preserves_other_observation_data():
assert OBS_LANGUAGE_SUBTASK_ATTENTION_MASK in observation assert OBS_LANGUAGE_SUBTASK_ATTENTION_MASK in observation
@require_package("transformers") @skip_if_package_missing("transformers")
def test_subtask_attention_mask_dtype(): def test_subtask_attention_mask_dtype():
"""Test that subtask attention mask has correct dtype (bool).""" """Test that subtask attention mask has correct dtype (bool)."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -1442,7 +1442,7 @@ def test_subtask_attention_mask_dtype():
assert subtask_attention_mask.dtype == torch.bool assert subtask_attention_mask.dtype == torch.bool
@require_package("transformers") @skip_if_package_missing("transformers")
def test_subtask_tokenization_deterministic(): def test_subtask_tokenization_deterministic():
"""Test that subtask tokenization is deterministic for the same input.""" """Test that subtask tokenization is deterministic for the same input."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
@@ -1467,7 +1467,7 @@ def test_subtask_tokenization_deterministic():
assert torch.equal(subtask_mask1, subtask_mask2) assert torch.equal(subtask_mask1, subtask_mask2)
@require_package("transformers") @skip_if_package_missing("transformers")
@patch("lerobot.processor.tokenizer_processor.AutoTokenizer") @patch("lerobot.processor.tokenizer_processor.AutoTokenizer")
def test_subtask_tokenization_integration_with_pipeline(mock_auto_tokenizer): def test_subtask_tokenization_integration_with_pipeline(mock_auto_tokenizer):
"""Test subtask tokenization works correctly with DataProcessorPipeline.""" """Test subtask tokenization works correctly with DataProcessorPipeline."""
@@ -1504,7 +1504,7 @@ def test_subtask_tokenization_integration_with_pipeline(mock_auto_tokenizer):
assert observation[OBS_LANGUAGE_SUBTASK_TOKENS].shape == (6,) assert observation[OBS_LANGUAGE_SUBTASK_TOKENS].shape == (6,)
@require_package("transformers") @skip_if_package_missing("transformers")
def test_subtask_not_added_for_unsupported_types(): def test_subtask_not_added_for_unsupported_types():
"""Test that subtask tokens are not added when subtask has unsupported type.""" """Test that subtask tokens are not added when subtask has unsupported type."""
mock_tokenizer = MockTokenizer(vocab_size=100) mock_tokenizer = MockTokenizer(vocab_size=100)
+6 -6
View File
@@ -23,7 +23,7 @@ from torch.multiprocessing import Event, Queue
from lerobot.utils.constants import OBS_STR from lerobot.utils.constants import OBS_STR
from lerobot.utils.transition import Transition from lerobot.utils.transition import Transition
from tests.utils import require_package from tests.utils import skip_if_package_missing
def create_learner_service_stub(): def create_learner_service_stub():
@@ -64,7 +64,7 @@ def close_service_stub(channel, server):
server.stop(None) server.stop(None)
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_establish_learner_connection_success(): def test_establish_learner_connection_success():
from lerobot.rl.actor import establish_learner_connection from lerobot.rl.actor import establish_learner_connection
@@ -81,7 +81,7 @@ def test_establish_learner_connection_success():
close_service_stub(channel, server) close_service_stub(channel, server)
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_establish_learner_connection_failure(): def test_establish_learner_connection_failure():
from lerobot.rl.actor import establish_learner_connection from lerobot.rl.actor import establish_learner_connection
@@ -100,7 +100,7 @@ def test_establish_learner_connection_failure():
close_service_stub(channel, server) close_service_stub(channel, server)
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_push_transitions_to_transport_queue(): def test_push_transitions_to_transport_queue():
from lerobot.rl.actor import push_transitions_to_transport_queue from lerobot.rl.actor import push_transitions_to_transport_queue
from lerobot.transport.utils import bytes_to_transitions from lerobot.transport.utils import bytes_to_transitions
@@ -135,7 +135,7 @@ def test_push_transitions_to_transport_queue():
assert_transitions_equal(deserialized_transition, transitions[i]) assert_transitions_equal(deserialized_transition, transitions[i])
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
@pytest.mark.timeout(3) # force cross-platform watchdog @pytest.mark.timeout(3) # force cross-platform watchdog
def test_transitions_stream(): def test_transitions_stream():
from lerobot.rl.actor import transitions_stream from lerobot.rl.actor import transitions_stream
@@ -167,7 +167,7 @@ def test_transitions_stream():
assert streamed_data[2].data == b"transition_data_3" assert streamed_data[2].data == b"transition_data_3"
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
@pytest.mark.timeout(3) # force cross-platform watchdog @pytest.mark.timeout(3) # force cross-platform watchdog
def test_interactions_stream(): def test_interactions_stream():
from lerobot.rl.actor import interactions_stream from lerobot.rl.actor import interactions_stream
+4 -4
View File
@@ -26,7 +26,7 @@ from lerobot.configs.train import TrainRLServerPipelineConfig
from lerobot.policies.sac.configuration_sac import SACConfig from lerobot.policies.sac.configuration_sac import SACConfig
from lerobot.utils.constants import OBS_STR from lerobot.utils.constants import OBS_STR
from lerobot.utils.transition import Transition from lerobot.utils.transition import Transition
from tests.utils import require_package from tests.utils import skip_if_package_missing
def create_test_transitions(count: int = 3) -> list[Transition]: def create_test_transitions(count: int = 3) -> list[Transition]:
@@ -88,7 +88,7 @@ def cfg():
return cfg return cfg
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
@pytest.mark.timeout(10) # force cross-platform watchdog @pytest.mark.timeout(10) # force cross-platform watchdog
def test_end_to_end_transitions_flow(cfg): def test_end_to_end_transitions_flow(cfg):
from lerobot.rl.actor import ( from lerobot.rl.actor import (
@@ -150,7 +150,7 @@ def test_end_to_end_transitions_flow(cfg):
assert_transitions_equal(transition, input_transitions[i]) assert_transitions_equal(transition, input_transitions[i])
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
@pytest.mark.timeout(10) @pytest.mark.timeout(10)
def test_end_to_end_interactions_flow(cfg): def test_end_to_end_interactions_flow(cfg):
from lerobot.rl.actor import ( from lerobot.rl.actor import (
@@ -223,7 +223,7 @@ def test_end_to_end_interactions_flow(cfg):
assert received == expected assert received == expected
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
@pytest.mark.parametrize("data_size", ["small", "large"]) @pytest.mark.parametrize("data_size", ["small", "large"])
@pytest.mark.timeout(10) @pytest.mark.timeout(10)
def test_end_to_end_parameters_flow(cfg, data_size): def test_end_to_end_parameters_flow(cfg, data_size):
+9 -9
View File
@@ -20,7 +20,7 @@ from multiprocessing import Event, Queue
import pytest import pytest
from tests.utils import require_package # our gRPC servicer class from tests.utils import skip_if_package_missing # our gRPC servicer class
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
@@ -39,7 +39,7 @@ def learner_service_stub():
close_learner_service_stub(channel, server) close_learner_service_stub(channel, server)
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def create_learner_service_stub( def create_learner_service_stub(
shutdown_event: Event, shutdown_event: Event,
parameters_queue: Queue, parameters_queue: Queue,
@@ -75,7 +75,7 @@ def create_learner_service_stub(
return services_pb2_grpc.LearnerServiceStub(channel), channel, server return services_pb2_grpc.LearnerServiceStub(channel), channel, server
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def close_learner_service_stub(channel, server): def close_learner_service_stub(channel, server):
channel.close() channel.close()
server.stop(None) server.stop(None)
@@ -91,7 +91,7 @@ def test_ready_method(learner_service_stub):
assert response == services_pb2.Empty() assert response == services_pb2.Empty()
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
@pytest.mark.timeout(3) # force cross-platform watchdog @pytest.mark.timeout(3) # force cross-platform watchdog
def test_send_interactions(): def test_send_interactions():
from lerobot.transport import services_pb2 from lerobot.transport import services_pb2
@@ -135,7 +135,7 @@ def test_send_interactions():
assert interactions == [b"123", b"4", b"5", b"678"] assert interactions == [b"123", b"4", b"5", b"678"]
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
@pytest.mark.timeout(3) # force cross-platform watchdog @pytest.mark.timeout(3) # force cross-platform watchdog
def test_send_transitions(): def test_send_transitions():
from lerobot.transport import services_pb2 from lerobot.transport import services_pb2
@@ -181,7 +181,7 @@ def test_send_transitions():
assert transitions == [b"transition_1transition_2transition_3", b"batch_1batch_2"] assert transitions == [b"transition_1transition_2transition_3", b"batch_1batch_2"]
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
@pytest.mark.timeout(3) # force cross-platform watchdog @pytest.mark.timeout(3) # force cross-platform watchdog
def test_send_transitions_empty_stream(): def test_send_transitions_empty_stream():
from lerobot.transport import services_pb2 from lerobot.transport import services_pb2
@@ -209,7 +209,7 @@ def test_send_transitions_empty_stream():
assert transitions_queue.empty() assert transitions_queue.empty()
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
@pytest.mark.timeout(10) # force cross-platform watchdog @pytest.mark.timeout(10) # force cross-platform watchdog
def test_stream_parameters(): def test_stream_parameters():
import time import time
@@ -267,7 +267,7 @@ def test_stream_parameters():
assert time_diff == pytest.approx(seconds_between_pushes, abs=0.1) assert time_diff == pytest.approx(seconds_between_pushes, abs=0.1)
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
@pytest.mark.timeout(3) # force cross-platform watchdog @pytest.mark.timeout(3) # force cross-platform watchdog
def test_stream_parameters_with_shutdown(): def test_stream_parameters_with_shutdown():
from lerobot.transport import services_pb2 from lerobot.transport import services_pb2
@@ -319,7 +319,7 @@ def test_stream_parameters_with_shutdown():
assert received_params == [b"param_batch_1", b"stop"] assert received_params == [b"param_batch_1", b"stop"]
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
@pytest.mark.timeout(3) # force cross-platform watchdog @pytest.mark.timeout(3) # force cross-platform watchdog
def test_stream_parameters_waits_and_retries_on_empty_queue(): def test_stream_parameters_waits_and_retries_on_empty_queue():
import threading import threading
+5 -5
View File
@@ -5,7 +5,7 @@ from unittest.mock import MagicMock, patch
import pytest import pytest
from safetensors.torch import load_file from safetensors.torch import load_file
from .utils import require_package from .utils import skip_if_package_missing
# Skip this entire module in CI # Skip this entire module in CI
pytestmark = pytest.mark.skipif( pytestmark = pytest.mark.skipif(
@@ -37,7 +37,7 @@ def resolve_model_id_for_peft_training(policy_type):
@pytest.mark.parametrize("policy_type", ["smolvla"]) @pytest.mark.parametrize("policy_type", ["smolvla"])
@require_package("peft") @skip_if_package_missing("peft")
def test_peft_training_push_to_hub_works(policy_type, tmp_path): def test_peft_training_push_to_hub_works(policy_type, tmp_path):
"""Ensure that push to hub stores PEFT only the adapter, not the full model weights.""" """Ensure that push to hub stores PEFT only the adapter, not the full model weights."""
output_dir = tmp_path / f"output_{policy_type}" output_dir = tmp_path / f"output_{policy_type}"
@@ -76,7 +76,7 @@ def test_peft_training_push_to_hub_works(policy_type, tmp_path):
@pytest.mark.parametrize("policy_type", ["smolvla"]) @pytest.mark.parametrize("policy_type", ["smolvla"])
@require_package("peft") @skip_if_package_missing("peft")
def test_peft_training_works(policy_type, tmp_path): def test_peft_training_works(policy_type, tmp_path):
"""Check whether the standard case of fine-tuning a (partially) pre-trained policy with PEFT works.""" """Check whether the standard case of fine-tuning a (partially) pre-trained policy with PEFT works."""
output_dir = tmp_path / f"output_{policy_type}" output_dir = tmp_path / f"output_{policy_type}"
@@ -125,7 +125,7 @@ def test_peft_training_works(policy_type, tmp_path):
@pytest.mark.parametrize("policy_type", ["smolvla"]) @pytest.mark.parametrize("policy_type", ["smolvla"])
@require_package("peft") @skip_if_package_missing("peft")
def test_peft_training_params_are_fewer(policy_type, tmp_path): def test_peft_training_params_are_fewer(policy_type, tmp_path):
"""Check whether the standard case of fine-tuning a (partially) pre-trained policy with PEFT works.""" """Check whether the standard case of fine-tuning a (partially) pre-trained policy with PEFT works."""
output_dir = tmp_path / f"output_{policy_type}" output_dir = tmp_path / f"output_{policy_type}"
@@ -176,7 +176,7 @@ def dummy_make_robot_from_config(*args, **kwargs):
@pytest.mark.parametrize("policy_type", ["smolvla"]) @pytest.mark.parametrize("policy_type", ["smolvla"])
@require_package("peft") @skip_if_package_missing("peft")
def test_peft_record_loads_policy(policy_type, tmp_path): def test_peft_record_loads_policy(policy_type, tmp_path):
"""Train a policy with PEFT and attempt to load it with `lerobot-record`.""" """Train a policy with PEFT and attempt to load it with `lerobot-record`."""
from peft import PeftModel from peft import PeftModel
+32 -32
View File
@@ -23,10 +23,10 @@ import torch
from lerobot.utils.constants import ACTION from lerobot.utils.constants import ACTION
from lerobot.utils.transition import Transition from lerobot.utils.transition import Transition
from tests.utils import require_cuda, require_package from tests.utils import require_cuda, skip_if_package_missing
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_bytes_buffer_size_empty_buffer(): def test_bytes_buffer_size_empty_buffer():
from lerobot.transport.utils import bytes_buffer_size from lerobot.transport.utils import bytes_buffer_size
@@ -37,7 +37,7 @@ def test_bytes_buffer_size_empty_buffer():
assert buffer.tell() == 0 assert buffer.tell() == 0
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_bytes_buffer_size_small_buffer(): def test_bytes_buffer_size_small_buffer():
from lerobot.transport.utils import bytes_buffer_size from lerobot.transport.utils import bytes_buffer_size
@@ -47,7 +47,7 @@ def test_bytes_buffer_size_small_buffer():
assert buffer.tell() == 0 assert buffer.tell() == 0
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_bytes_buffer_size_large_buffer(): def test_bytes_buffer_size_large_buffer():
from lerobot.transport.utils import CHUNK_SIZE, bytes_buffer_size from lerobot.transport.utils import CHUNK_SIZE, bytes_buffer_size
@@ -58,7 +58,7 @@ def test_bytes_buffer_size_large_buffer():
assert buffer.tell() == 0 assert buffer.tell() == 0
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_send_bytes_in_chunks_empty_data(): def test_send_bytes_in_chunks_empty_data():
from lerobot.transport.utils import send_bytes_in_chunks, services_pb2 from lerobot.transport.utils import send_bytes_in_chunks, services_pb2
@@ -68,7 +68,7 @@ def test_send_bytes_in_chunks_empty_data():
assert len(chunks) == 0 assert len(chunks) == 0
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_single_chunk_small_data(): def test_single_chunk_small_data():
from lerobot.transport.utils import send_bytes_in_chunks, services_pb2 from lerobot.transport.utils import send_bytes_in_chunks, services_pb2
@@ -82,7 +82,7 @@ def test_single_chunk_small_data():
assert chunks[0].transfer_state == services_pb2.TransferState.TRANSFER_END assert chunks[0].transfer_state == services_pb2.TransferState.TRANSFER_END
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_not_silent_mode(): def test_not_silent_mode():
from lerobot.transport.utils import send_bytes_in_chunks, services_pb2 from lerobot.transport.utils import send_bytes_in_chunks, services_pb2
@@ -94,7 +94,7 @@ def test_not_silent_mode():
assert chunks[0].data == b"Some data" assert chunks[0].data == b"Some data"
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_send_bytes_in_chunks_large_data(): def test_send_bytes_in_chunks_large_data():
from lerobot.transport.utils import CHUNK_SIZE, send_bytes_in_chunks, services_pb2 from lerobot.transport.utils import CHUNK_SIZE, send_bytes_in_chunks, services_pb2
@@ -111,7 +111,7 @@ def test_send_bytes_in_chunks_large_data():
assert chunks[2].transfer_state == services_pb2.TransferState.TRANSFER_END assert chunks[2].transfer_state == services_pb2.TransferState.TRANSFER_END
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_send_bytes_in_chunks_large_data_with_exact_chunk_size(): def test_send_bytes_in_chunks_large_data_with_exact_chunk_size():
from lerobot.transport.utils import CHUNK_SIZE, send_bytes_in_chunks, services_pb2 from lerobot.transport.utils import CHUNK_SIZE, send_bytes_in_chunks, services_pb2
@@ -124,7 +124,7 @@ def test_send_bytes_in_chunks_large_data_with_exact_chunk_size():
assert chunks[0].transfer_state == services_pb2.TransferState.TRANSFER_END assert chunks[0].transfer_state == services_pb2.TransferState.TRANSFER_END
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_receive_bytes_in_chunks_empty_data(): def test_receive_bytes_in_chunks_empty_data():
from lerobot.transport.utils import receive_bytes_in_chunks from lerobot.transport.utils import receive_bytes_in_chunks
@@ -138,7 +138,7 @@ def test_receive_bytes_in_chunks_empty_data():
assert queue.empty() assert queue.empty()
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_receive_bytes_in_chunks_single_chunk(): def test_receive_bytes_in_chunks_single_chunk():
from lerobot.transport.utils import receive_bytes_in_chunks, services_pb2 from lerobot.transport.utils import receive_bytes_in_chunks, services_pb2
@@ -157,7 +157,7 @@ def test_receive_bytes_in_chunks_single_chunk():
assert queue.empty() assert queue.empty()
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_receive_bytes_in_chunks_single_not_end_chunk(): def test_receive_bytes_in_chunks_single_not_end_chunk():
from lerobot.transport.utils import receive_bytes_in_chunks, services_pb2 from lerobot.transport.utils import receive_bytes_in_chunks, services_pb2
@@ -175,7 +175,7 @@ def test_receive_bytes_in_chunks_single_not_end_chunk():
assert queue.empty() assert queue.empty()
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_receive_bytes_in_chunks_multiple_chunks(): def test_receive_bytes_in_chunks_multiple_chunks():
from lerobot.transport.utils import receive_bytes_in_chunks, services_pb2 from lerobot.transport.utils import receive_bytes_in_chunks, services_pb2
@@ -199,7 +199,7 @@ def test_receive_bytes_in_chunks_multiple_chunks():
assert queue.empty() assert queue.empty()
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_receive_bytes_in_chunks_multiple_messages(): def test_receive_bytes_in_chunks_multiple_messages():
from lerobot.transport.utils import receive_bytes_in_chunks, services_pb2 from lerobot.transport.utils import receive_bytes_in_chunks, services_pb2
@@ -235,7 +235,7 @@ def test_receive_bytes_in_chunks_multiple_messages():
assert queue.empty() assert queue.empty()
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_receive_bytes_in_chunks_shutdown_during_receive(): def test_receive_bytes_in_chunks_shutdown_during_receive():
from lerobot.transport.utils import receive_bytes_in_chunks, services_pb2 from lerobot.transport.utils import receive_bytes_in_chunks, services_pb2
@@ -259,7 +259,7 @@ def test_receive_bytes_in_chunks_shutdown_during_receive():
assert queue.empty() assert queue.empty()
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_receive_bytes_in_chunks_only_begin_chunk(): def test_receive_bytes_in_chunks_only_begin_chunk():
from lerobot.transport.utils import receive_bytes_in_chunks, services_pb2 from lerobot.transport.utils import receive_bytes_in_chunks, services_pb2
@@ -279,7 +279,7 @@ def test_receive_bytes_in_chunks_only_begin_chunk():
assert queue.empty() assert queue.empty()
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_receive_bytes_in_chunks_missing_begin(): def test_receive_bytes_in_chunks_missing_begin():
from lerobot.transport.utils import receive_bytes_in_chunks, services_pb2 from lerobot.transport.utils import receive_bytes_in_chunks, services_pb2
@@ -303,7 +303,7 @@ def test_receive_bytes_in_chunks_missing_begin():
# Tests for state_to_bytes and bytes_to_state_dict # Tests for state_to_bytes and bytes_to_state_dict
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_state_to_bytes_empty_dict(): def test_state_to_bytes_empty_dict():
from lerobot.transport.utils import bytes_to_state_dict, state_to_bytes from lerobot.transport.utils import bytes_to_state_dict, state_to_bytes
@@ -314,7 +314,7 @@ def test_state_to_bytes_empty_dict():
assert reconstructed == state_dict assert reconstructed == state_dict
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_bytes_to_state_dict_empty_data(): def test_bytes_to_state_dict_empty_data():
from lerobot.transport.utils import bytes_to_state_dict from lerobot.transport.utils import bytes_to_state_dict
@@ -323,7 +323,7 @@ def test_bytes_to_state_dict_empty_data():
bytes_to_state_dict(b"") bytes_to_state_dict(b"")
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_state_to_bytes_simple_dict(): def test_state_to_bytes_simple_dict():
from lerobot.transport.utils import bytes_to_state_dict, state_to_bytes from lerobot.transport.utils import bytes_to_state_dict, state_to_bytes
@@ -347,7 +347,7 @@ def test_state_to_bytes_simple_dict():
assert torch.allclose(state_dict[key], reconstructed[key]) assert torch.allclose(state_dict[key], reconstructed[key])
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_state_to_bytes_various_dtypes(): def test_state_to_bytes_various_dtypes():
from lerobot.transport.utils import bytes_to_state_dict, state_to_bytes from lerobot.transport.utils import bytes_to_state_dict, state_to_bytes
@@ -372,7 +372,7 @@ def test_state_to_bytes_various_dtypes():
assert torch.allclose(state_dict[key], reconstructed[key]) assert torch.allclose(state_dict[key], reconstructed[key])
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_bytes_to_state_dict_invalid_data(): def test_bytes_to_state_dict_invalid_data():
from lerobot.transport.utils import bytes_to_state_dict from lerobot.transport.utils import bytes_to_state_dict
@@ -382,7 +382,7 @@ def test_bytes_to_state_dict_invalid_data():
@require_cuda @require_cuda
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_state_to_bytes_various_dtypes_cuda(): def test_state_to_bytes_various_dtypes_cuda():
from lerobot.transport.utils import bytes_to_state_dict, state_to_bytes from lerobot.transport.utils import bytes_to_state_dict, state_to_bytes
@@ -407,7 +407,7 @@ def test_state_to_bytes_various_dtypes_cuda():
assert torch.allclose(state_dict[key], reconstructed[key]) assert torch.allclose(state_dict[key], reconstructed[key])
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_python_object_to_bytes_none(): def test_python_object_to_bytes_none():
from lerobot.transport.utils import bytes_to_python_object, python_object_to_bytes from lerobot.transport.utils import bytes_to_python_object, python_object_to_bytes
@@ -439,7 +439,7 @@ def test_python_object_to_bytes_none():
(1, 2, 3), (1, 2, 3),
], ],
) )
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_python_object_to_bytes_simple_types(obj): def test_python_object_to_bytes_simple_types(obj):
from lerobot.transport.utils import bytes_to_python_object, python_object_to_bytes from lerobot.transport.utils import bytes_to_python_object, python_object_to_bytes
@@ -450,7 +450,7 @@ def test_python_object_to_bytes_simple_types(obj):
assert type(reconstructed) is type(obj) assert type(reconstructed) is type(obj)
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_python_object_to_bytes_with_tensors(): def test_python_object_to_bytes_with_tensors():
from lerobot.transport.utils import bytes_to_python_object, python_object_to_bytes from lerobot.transport.utils import bytes_to_python_object, python_object_to_bytes
@@ -475,7 +475,7 @@ def test_python_object_to_bytes_with_tensors():
assert torch.equal(obj["nested"]["tensor2"], reconstructed["nested"]["tensor2"]) assert torch.equal(obj["nested"]["tensor2"], reconstructed["nested"]["tensor2"])
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_transitions_to_bytes_empty_list(): def test_transitions_to_bytes_empty_list():
from lerobot.transport.utils import bytes_to_transitions, transitions_to_bytes from lerobot.transport.utils import bytes_to_transitions, transitions_to_bytes
@@ -487,7 +487,7 @@ def test_transitions_to_bytes_empty_list():
assert isinstance(reconstructed, list) assert isinstance(reconstructed, list)
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_transitions_to_bytes_single_transition(): def test_transitions_to_bytes_single_transition():
from lerobot.transport.utils import bytes_to_transitions, transitions_to_bytes from lerobot.transport.utils import bytes_to_transitions, transitions_to_bytes
@@ -509,7 +509,7 @@ def test_transitions_to_bytes_single_transition():
assert_transitions_equal(transitions[0], reconstructed[0]) assert_transitions_equal(transitions[0], reconstructed[0])
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def assert_transitions_equal(t1: Transition, t2: Transition): def assert_transitions_equal(t1: Transition, t2: Transition):
"""Helper to assert two transitions are equal.""" """Helper to assert two transitions are equal."""
assert_observation_equal(t1["state"], t2["state"]) assert_observation_equal(t1["state"], t2["state"])
@@ -519,7 +519,7 @@ def assert_transitions_equal(t1: Transition, t2: Transition):
assert_observation_equal(t1["next_state"], t2["next_state"]) assert_observation_equal(t1["next_state"], t2["next_state"])
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def assert_observation_equal(o1: dict, o2: dict): def assert_observation_equal(o1: dict, o2: dict):
"""Helper to assert two observations are equal.""" """Helper to assert two observations are equal."""
assert set(o1.keys()) == set(o2.keys()) assert set(o1.keys()) == set(o2.keys())
@@ -527,7 +527,7 @@ def assert_observation_equal(o1: dict, o2: dict):
assert torch.allclose(o1[key], o2[key]) assert torch.allclose(o1[key], o2[key])
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_transitions_to_bytes_multiple_transitions(): def test_transitions_to_bytes_multiple_transitions():
from lerobot.transport.utils import bytes_to_transitions, transitions_to_bytes from lerobot.transport.utils import bytes_to_transitions, transitions_to_bytes
@@ -551,7 +551,7 @@ def test_transitions_to_bytes_multiple_transitions():
assert_transitions_equal(original, reconstructed_item) assert_transitions_equal(original, reconstructed_item)
@require_package("grpcio", "grpc") @skip_if_package_missing("grpcio", "grpc")
def test_receive_bytes_in_chunks_unknown_state(): def test_receive_bytes_in_chunks_unknown_state():
from lerobot.transport.utils import receive_bytes_in_chunks from lerobot.transport.utils import receive_bytes_in_chunks
+2 -2
View File
@@ -152,7 +152,7 @@ def require_env(func):
return wrapper return wrapper
def require_package_arg(func): def skip_if_package_arg_missing(func):
""" """
Decorator that skips the test if the required package is not installed. Decorator that skips the test if the required package is not installed.
This is similar to `require_env` but more general in that it can check any package (not just environments). This is similar to `require_env` but more general in that it can check any package (not just environments).
@@ -184,7 +184,7 @@ def require_package_arg(func):
return wrapper return wrapper
def require_package(package_name, import_name=None): def skip_if_package_missing(package_name, import_name=None):
""" """
Decorator that skips the test if the specified package is not installed. Decorator that skips the test if the specified package is not installed.
""" """
+3
View File
@@ -48,6 +48,9 @@ def mock_rerun(monkeypatch):
calls.append((key, obj, kwargs)) calls.append((key, obj, kwargs))
dummy_rr = SimpleNamespace( dummy_rr = SimpleNamespace(
__name__="rerun",
__package__="rerun",
__spec__=SimpleNamespace(name="rerun", submodule_search_locations=None),
Scalars=DummyScalar, Scalars=DummyScalar,
Image=DummyImage, Image=DummyImage,
log=dummy_log, log=dummy_log,
Generated
+175 -53
View File
@@ -2,11 +2,14 @@ version = 1
revision = 3 revision = 3
requires-python = ">=3.12" requires-python = ">=3.12"
resolution-markers = [ resolution-markers = [
"python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'linux'", "python_full_version >= '3.14' and platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and platform_machine != 's390x' and sys_platform == 'linux'",
"(python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.14' and platform_machine == 'arm64' and sys_platform == 'linux') or (python_full_version >= '3.14' and platform_machine == 'armv7l' and sys_platform == 'linux')",
"python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'linux'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'linux'",
"python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'linux'", "python_full_version == '3.13.*' and platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and platform_machine != 's390x' and sys_platform == 'linux'",
"(python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and platform_machine == 'arm64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and platform_machine == 'armv7l' and sys_platform == 'linux')",
"python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'linux'", "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'linux'",
"python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'linux'", "python_full_version < '3.13' and platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and platform_machine != 's390x' and sys_platform == 'linux'",
"(python_full_version < '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.13' and platform_machine == 'arm64' and sys_platform == 'linux') or (python_full_version < '3.13' and platform_machine == 'armv7l' and sys_platform == 'linux')",
"python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'linux'", "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'linux'",
"python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'linux'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'linux'",
"python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'linux'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'linux'",
@@ -820,7 +823,7 @@ name = "cuda-bindings"
version = "12.9.4" version = "12.9.4"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "cuda-pathfinder", marker = "sys_platform == 'linux'" }, { name = "cuda-pathfinder", marker = "platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and platform_machine != 's390x' and sys_platform == 'linux'" },
] ]
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/a9/c1/dabe88f52c3e3760d861401bb994df08f672ec893b8f7592dc91626adcf3/cuda_bindings-12.9.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fda147a344e8eaeca0c6ff113d2851ffca8f7dfc0a6c932374ee5c47caa649c8", size = 12151019, upload-time = "2025-10-21T14:51:43.167Z" }, { url = "https://files.pythonhosted.org/packages/a9/c1/dabe88f52c3e3760d861401bb994df08f672ec893b8f7592dc91626adcf3/cuda_bindings-12.9.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fda147a344e8eaeca0c6ff113d2851ffca8f7dfc0a6c932374ee5c47caa649c8", size = 12151019, upload-time = "2025-10-21T14:51:43.167Z" },
@@ -907,7 +910,7 @@ name = "decord"
version = "0.6.0" version = "0.6.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "numpy" }, { name = "numpy", marker = "(platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and platform_machine != 's390x') or (platform_machine != 's390x' and sys_platform != 'linux')" },
] ]
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/11/79/936af42edf90a7bd4e41a6cac89c913d4b47fa48a26b042d5129a9242ee3/decord-0.6.0-py3-none-manylinux2010_x86_64.whl", hash = "sha256:51997f20be8958e23b7c4061ba45d0efcd86bffd5fe81c695d0befee0d442976", size = 13602299, upload-time = "2021-06-14T21:30:55.486Z" }, { url = "https://files.pythonhosted.org/packages/11/79/936af42edf90a7bd4e41a6cac89c913d4b47fa48a26b042d5129a9242ee3/decord-0.6.0-py3-none-manylinux2010_x86_64.whl", hash = "sha256:51997f20be8958e23b7c4061ba45d0efcd86bffd5fe81c695d0befee0d442976", size = 13602299, upload-time = "2021-06-14T21:30:55.486Z" },
@@ -1010,7 +1013,8 @@ name = "dm-tree"
version = "0.1.9" version = "0.1.9"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
resolution-markers = [ resolution-markers = [
"python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'linux'", "python_full_version >= '3.14' and platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and platform_machine != 's390x' and sys_platform == 'linux'",
"(python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.14' and platform_machine == 'arm64' and sys_platform == 'linux') or (python_full_version >= '3.14' and platform_machine == 'armv7l' and sys_platform == 'linux')",
"python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'linux'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'linux'",
"python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'linux'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'linux'",
"python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'linux'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'linux'",
@@ -1043,9 +1047,11 @@ name = "dm-tree"
version = "0.1.10" version = "0.1.10"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
resolution-markers = [ resolution-markers = [
"python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'linux'", "python_full_version == '3.13.*' and platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and platform_machine != 's390x' and sys_platform == 'linux'",
"(python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and platform_machine == 'arm64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and platform_machine == 'armv7l' and sys_platform == 'linux')",
"python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'linux'", "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'linux'",
"python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'linux'", "python_full_version < '3.13' and platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and platform_machine != 's390x' and sys_platform == 'linux'",
"(python_full_version < '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.13' and platform_machine == 'arm64' and sys_platform == 'linux') or (python_full_version < '3.13' and platform_machine == 'armv7l' and sys_platform == 'linux')",
"python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'linux'", "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'linux'",
"python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'linux'", "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'linux'",
"python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'linux'", "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'linux'",
@@ -2187,37 +2193,28 @@ name = "lerobot"
version = "0.5.2" version = "0.5.2"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "accelerate" },
{ name = "av" },
{ name = "cmake" },
{ name = "datasets" },
{ name = "deepdiff" },
{ name = "diffusers" },
{ name = "draccus" }, { name = "draccus" },
{ name = "einops" }, { name = "einops" },
{ name = "gymnasium" }, { name = "gymnasium" },
{ name = "huggingface-hub" }, { name = "huggingface-hub" },
{ name = "imageio", extra = ["ffmpeg"] },
{ name = "jsonlines" },
{ name = "numpy" }, { name = "numpy" },
{ name = "opencv-python-headless" }, { name = "opencv-python-headless" },
{ name = "packaging" }, { name = "packaging" },
{ name = "pynput" },
{ name = "pyserial" },
{ name = "rerun-sdk" },
{ name = "setuptools" },
{ name = "termcolor" }, { name = "termcolor" },
{ name = "torch" }, { name = "torch" },
{ name = "torchcodec", marker = "(platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and sys_platform == 'linux') or (platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')" },
{ name = "torchvision" }, { name = "torchvision" },
{ name = "wandb" },
] ]
[package.optional-dependencies] [package.optional-dependencies]
all = [ all = [
{ name = "accelerate" }, { name = "accelerate" },
{ name = "av" },
{ name = "cmake" },
{ name = "contourpy" }, { name = "contourpy" },
{ name = "datasets" },
{ name = "debugpy" }, { name = "debugpy" },
{ name = "deepdiff" },
{ name = "diffusers" },
{ name = "dynamixel-sdk" }, { name = "dynamixel-sdk" },
{ name = "faker" }, { name = "faker" },
{ name = "fastapi" }, { name = "fastapi" },
@@ -2230,6 +2227,7 @@ all = [
{ name = "hebi-py" }, { name = "hebi-py" },
{ name = "hf-libero", marker = "sys_platform == 'linux'" }, { name = "hf-libero", marker = "sys_platform == 'linux'" },
{ name = "hidapi" }, { name = "hidapi" },
{ name = "jsonlines" },
{ name = "matplotlib" }, { name = "matplotlib" },
{ name = "metaworld" }, { name = "metaworld" },
{ name = "mock-serial", marker = "sys_platform != 'win32'" }, { name = "mock-serial", marker = "sys_platform != 'win32'" },
@@ -2242,24 +2240,34 @@ all = [
{ name = "protobuf" }, { name = "protobuf" },
{ name = "pygame" }, { name = "pygame" },
{ name = "pymunk" }, { name = "pymunk" },
{ name = "pynput" },
{ name = "pyrealsense2", marker = "sys_platform != 'darwin'" }, { name = "pyrealsense2", marker = "sys_platform != 'darwin'" },
{ name = "pyrealsense2-macosx", marker = "sys_platform == 'darwin'" }, { name = "pyrealsense2-macosx", marker = "sys_platform == 'darwin'" },
{ name = "pyserial" },
{ name = "pytest" }, { name = "pytest" },
{ name = "pytest-cov" }, { name = "pytest-cov" },
{ name = "pytest-timeout" }, { name = "pytest-timeout" },
{ name = "pyzmq" }, { name = "pyzmq" },
{ name = "qwen-vl-utils" }, { name = "qwen-vl-utils" },
{ name = "reachy2-sdk" }, { name = "reachy2-sdk" },
{ name = "rerun-sdk" },
{ name = "safetensors" }, { name = "safetensors" },
{ name = "scikit-image" }, { name = "scikit-image" },
{ name = "scipy" }, { name = "scipy" },
{ name = "setuptools" },
{ name = "teleop" }, { name = "teleop" },
{ name = "torchcodec", marker = "(platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and sys_platform == 'linux') or (platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')" },
{ name = "torchdiffeq" }, { name = "torchdiffeq" },
{ name = "transformers" }, { name = "transformers" },
{ name = "wandb" },
] ]
aloha = [ aloha = [
{ name = "av" },
{ name = "datasets" },
{ name = "gym-aloha" }, { name = "gym-aloha" },
{ name = "jsonlines" },
{ name = "scipy" }, { name = "scipy" },
{ name = "torchcodec", marker = "(platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and sys_platform == 'linux') or (platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')" },
] ]
async = [ async = [
{ name = "contourpy" }, { name = "contourpy" },
@@ -2267,23 +2275,54 @@ async = [
{ name = "matplotlib" }, { name = "matplotlib" },
{ name = "protobuf" }, { name = "protobuf" },
] ]
build = [
{ name = "cmake" },
{ name = "setuptools" },
]
can-dep = [ can-dep = [
{ name = "python-can" }, { name = "python-can" },
] ]
damiao = [ damiao = [
{ name = "python-can" }, { name = "python-can" },
] ]
dataset = [
{ name = "av" },
{ name = "datasets" },
{ name = "jsonlines" },
{ name = "torchcodec", marker = "(platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and sys_platform == 'linux') or (platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')" },
]
dataset-viz = [
{ name = "av" },
{ name = "datasets" },
{ name = "jsonlines" },
{ name = "rerun-sdk" },
{ name = "torchcodec", marker = "(platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and sys_platform == 'linux') or (platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')" },
]
dev = [ dev = [
{ name = "accelerate" },
{ name = "av" },
{ name = "datasets" },
{ name = "debugpy" }, { name = "debugpy" },
{ name = "deepdiff" },
{ name = "diffusers" },
{ name = "grpcio" }, { name = "grpcio" },
{ name = "grpcio-tools" }, { name = "grpcio-tools" },
{ name = "jsonlines" },
{ name = "mypy" }, { name = "mypy" },
{ name = "pre-commit" }, { name = "pre-commit" },
{ name = "protobuf" }, { name = "protobuf" },
{ name = "pynput" },
{ name = "pyserial" },
{ name = "rerun-sdk" },
{ name = "torchcodec", marker = "(platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and sys_platform == 'linux') or (platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')" },
{ name = "wandb" },
] ]
dynamixel = [ dynamixel = [
{ name = "dynamixel-sdk" }, { name = "dynamixel-sdk" },
] ]
evaluation = [
{ name = "av" },
]
feetech = [ feetech = [
{ name = "feetech-servo-sdk" }, { name = "feetech-servo-sdk" },
] ]
@@ -2307,6 +2346,11 @@ grpcio-dep = [
{ name = "grpcio" }, { name = "grpcio" },
{ name = "protobuf" }, { name = "protobuf" },
] ]
hardware = [
{ name = "deepdiff" },
{ name = "pynput" },
{ name = "pyserial" },
]
hilserl = [ hilserl = [
{ name = "grpcio" }, { name = "grpcio" },
{ name = "gym-hil" }, { name = "gym-hil" },
@@ -2330,8 +2374,12 @@ lekiwi = [
{ name = "pyzmq" }, { name = "pyzmq" },
] ]
libero = [ libero = [
{ name = "av" },
{ name = "datasets" },
{ name = "hf-libero", marker = "sys_platform == 'linux'" }, { name = "hf-libero", marker = "sys_platform == 'linux'" },
{ name = "jsonlines" },
{ name = "scipy" }, { name = "scipy" },
{ name = "torchcodec", marker = "(platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and sys_platform == 'linux') or (platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')" },
{ name = "transformers" }, { name = "transformers" },
] ]
matplotlib-dep = [ matplotlib-dep = [
@@ -2339,8 +2387,12 @@ matplotlib-dep = [
{ name = "matplotlib" }, { name = "matplotlib" },
] ]
metaworld = [ metaworld = [
{ name = "av" },
{ name = "datasets" },
{ name = "jsonlines" },
{ name = "metaworld" }, { name = "metaworld" },
{ name = "scipy" }, { name = "scipy" },
{ name = "torchcodec", marker = "(platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and sys_platform == 'linux') or (platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')" },
] ]
multi-task-dit = [ multi-task-dit = [
{ name = "transformers" }, { name = "transformers" },
@@ -2369,8 +2421,12 @@ placo-dep = [
{ name = "placo" }, { name = "placo" },
] ]
pusht = [ pusht = [
{ name = "av" },
{ name = "datasets" },
{ name = "gym-pusht" }, { name = "gym-pusht" },
{ name = "jsonlines" },
{ name = "pymunk" }, { name = "pymunk" },
{ name = "torchcodec", marker = "(platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and sys_platform == 'linux') or (platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')" },
] ]
pygame-dep = [ pygame-dep = [
{ name = "pygame" }, { name = "pygame" },
@@ -2381,6 +2437,16 @@ qwen-vl-utils-dep = [
reachy2 = [ reachy2 = [
{ name = "reachy2-sdk" }, { name = "reachy2-sdk" },
] ]
robot = [
{ name = "av" },
{ name = "datasets" },
{ name = "deepdiff" },
{ name = "jsonlines" },
{ name = "pynput" },
{ name = "pyserial" },
{ name = "rerun-sdk" },
{ name = "torchcodec", marker = "(platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and sys_platform == 'linux') or (platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')" },
]
robstride = [ robstride = [
{ name = "python-can" }, { name = "python-can" },
] ]
@@ -2401,10 +2467,39 @@ smolvla = [
{ name = "transformers" }, { name = "transformers" },
] ]
test = [ test = [
{ name = "accelerate" },
{ name = "av" },
{ name = "datasets" },
{ name = "deepdiff" },
{ name = "diffusers" },
{ name = "jsonlines" },
{ name = "mock-serial", marker = "sys_platform != 'win32'" }, { name = "mock-serial", marker = "sys_platform != 'win32'" },
{ name = "pynput" },
{ name = "pyserial" },
{ name = "pytest" }, { name = "pytest" },
{ name = "pytest-cov" }, { name = "pytest-cov" },
{ name = "pytest-timeout" }, { name = "pytest-timeout" },
{ name = "rerun-sdk" },
{ name = "torchcodec", marker = "(platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and sys_platform == 'linux') or (platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')" },
{ name = "wandb" },
]
train = [
{ name = "accelerate" },
{ name = "av" },
{ name = "datasets" },
{ name = "diffusers" },
{ name = "jsonlines" },
{ name = "torchcodec", marker = "(platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and sys_platform == 'linux') or (platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')" },
{ name = "wandb" },
]
training = [
{ name = "accelerate" },
{ name = "av" },
{ name = "datasets" },
{ name = "diffusers" },
{ name = "jsonlines" },
{ name = "torchcodec", marker = "(platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and sys_platform == 'linux') or (platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')" },
{ name = "wandb" },
] ]
transformers-dep = [ transformers-dep = [
{ name = "transformers" }, { name = "transformers" },
@@ -2422,6 +2517,9 @@ video-benchmark = [
{ name = "pandas" }, { name = "pandas" },
{ name = "scikit-image" }, { name = "scikit-image" },
] ]
viz = [
{ name = "rerun-sdk" },
]
wallx = [ wallx = [
{ name = "peft" }, { name = "peft" },
{ name = "qwen-vl-utils" }, { name = "qwen-vl-utils" },
@@ -2435,16 +2533,17 @@ xvla = [
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [
{ name = "accelerate", specifier = ">=1.10.0,<2.0.0" },
{ name = "accelerate", marker = "extra == 'smolvla'", specifier = ">=1.7.0,<2.0.0" }, { name = "accelerate", marker = "extra == 'smolvla'", specifier = ">=1.7.0,<2.0.0" },
{ name = "av", specifier = ">=15.0.0,<16.0.0" }, { name = "accelerate", marker = "extra == 'train'", specifier = ">=1.10.0,<2.0.0" },
{ name = "cmake", specifier = ">=3.29.0.1,<4.2.0" }, { name = "av", marker = "extra == 'dataset'", specifier = ">=15.0.0,<16.0.0" },
{ name = "av", marker = "extra == 'evaluation'", specifier = ">=15.0.0,<16.0.0" },
{ name = "cmake", marker = "extra == 'build'", specifier = ">=3.29.0.1,<4.2.0" },
{ name = "contourpy", marker = "extra == 'matplotlib-dep'", specifier = ">=1.3.0,<2.0.0" }, { name = "contourpy", marker = "extra == 'matplotlib-dep'", specifier = ">=1.3.0,<2.0.0" },
{ name = "datasets", specifier = ">=4.0.0,<5.0.0" }, { name = "datasets", marker = "extra == 'dataset'", specifier = ">=4.0.0,<5.0.0" },
{ name = "debugpy", marker = "extra == 'dev'", specifier = ">=1.8.1,<1.9.0" }, { name = "debugpy", marker = "extra == 'dev'", specifier = ">=1.8.1,<1.9.0" },
{ name = "decord", marker = "(platform_machine == 'AMD64' and extra == 'groot') or (platform_machine == 'x86_64' and extra == 'groot')", specifier = ">=0.6.0,<1.0.0" }, { name = "decord", marker = "(platform_machine == 'AMD64' and extra == 'groot') or (platform_machine == 'x86_64' and extra == 'groot')", specifier = ">=0.6.0,<1.0.0" },
{ name = "deepdiff", specifier = ">=7.0.1,<9.0.0" }, { name = "deepdiff", marker = "extra == 'hardware'", specifier = ">=7.0.1,<9.0.0" },
{ name = "diffusers", specifier = ">=0.27.2,<0.36.0" }, { name = "diffusers", marker = "extra == 'train'", specifier = ">=0.27.2,<0.36.0" },
{ name = "dm-tree", marker = "extra == 'groot'", specifier = ">=0.1.8,<1.0.0" }, { name = "dm-tree", marker = "extra == 'groot'", specifier = ">=0.1.8,<1.0.0" },
{ name = "draccus", specifier = "==0.10.0" }, { name = "draccus", specifier = "==0.10.0" },
{ name = "dynamixel-sdk", marker = "extra == 'dynamixel'", specifier = ">=3.7.31,<3.9.0" }, { name = "dynamixel-sdk", marker = "extra == 'dynamixel'", specifier = ">=3.7.31,<3.9.0" },
@@ -2463,13 +2562,23 @@ requires-dist = [
{ name = "hf-libero", marker = "sys_platform == 'linux' and extra == 'libero'", specifier = ">=0.1.3,<0.2.0" }, { name = "hf-libero", marker = "sys_platform == 'linux' and extra == 'libero'", specifier = ">=0.1.3,<0.2.0" },
{ name = "hidapi", marker = "extra == 'gamepad'", specifier = ">=0.14.0,<0.15.0" }, { name = "hidapi", marker = "extra == 'gamepad'", specifier = ">=0.14.0,<0.15.0" },
{ name = "huggingface-hub", specifier = ">=1.0.0,<2.0.0" }, { name = "huggingface-hub", specifier = ">=1.0.0,<2.0.0" },
{ name = "imageio", extras = ["ffmpeg"], specifier = ">=2.34.0,<3.0.0" }, { name = "jsonlines", marker = "extra == 'dataset'", specifier = ">=4.0.0,<5.0.0" },
{ name = "jsonlines", specifier = ">=4.0.0,<5.0.0" },
{ name = "lerobot", extras = ["aloha"], marker = "extra == 'all'" }, { name = "lerobot", extras = ["aloha"], marker = "extra == 'all'" },
{ name = "lerobot", extras = ["async"], marker = "extra == 'all'" }, { name = "lerobot", extras = ["async"], marker = "extra == 'all'" },
{ name = "lerobot", extras = ["build"], marker = "extra == 'all'" },
{ name = "lerobot", extras = ["can-dep"], marker = "extra == 'damiao'" }, { name = "lerobot", extras = ["can-dep"], marker = "extra == 'damiao'" },
{ name = "lerobot", extras = ["can-dep"], marker = "extra == 'robstride'" }, { name = "lerobot", extras = ["can-dep"], marker = "extra == 'robstride'" },
{ name = "lerobot", extras = ["damiao"], marker = "extra == 'openarms'" }, { name = "lerobot", extras = ["damiao"], marker = "extra == 'openarms'" },
{ name = "lerobot", extras = ["dataset"], marker = "extra == 'all'" },
{ name = "lerobot", extras = ["dataset"], marker = "extra == 'aloha'" },
{ name = "lerobot", extras = ["dataset"], marker = "extra == 'dataset-viz'" },
{ name = "lerobot", extras = ["dataset"], marker = "extra == 'dev'" },
{ name = "lerobot", extras = ["dataset"], marker = "extra == 'libero'" },
{ name = "lerobot", extras = ["dataset"], marker = "extra == 'metaworld'" },
{ name = "lerobot", extras = ["dataset"], marker = "extra == 'pusht'" },
{ name = "lerobot", extras = ["dataset"], marker = "extra == 'robot'" },
{ name = "lerobot", extras = ["dataset"], marker = "extra == 'test'" },
{ name = "lerobot", extras = ["dataset"], marker = "extra == 'train'" },
{ name = "lerobot", extras = ["dev"], marker = "extra == 'all'" }, { name = "lerobot", extras = ["dev"], marker = "extra == 'all'" },
{ name = "lerobot", extras = ["dynamixel"], marker = "extra == 'all'" }, { name = "lerobot", extras = ["dynamixel"], marker = "extra == 'all'" },
{ name = "lerobot", extras = ["feetech"], marker = "extra == 'hopejr'" }, { name = "lerobot", extras = ["feetech"], marker = "extra == 'hopejr'" },
@@ -2478,6 +2587,10 @@ requires-dist = [
{ name = "lerobot", extras = ["grpcio-dep"], marker = "extra == 'async'" }, { name = "lerobot", extras = ["grpcio-dep"], marker = "extra == 'async'" },
{ name = "lerobot", extras = ["grpcio-dep"], marker = "extra == 'dev'" }, { name = "lerobot", extras = ["grpcio-dep"], marker = "extra == 'dev'" },
{ name = "lerobot", extras = ["grpcio-dep"], marker = "extra == 'hilserl'" }, { name = "lerobot", extras = ["grpcio-dep"], marker = "extra == 'hilserl'" },
{ name = "lerobot", extras = ["hardware"], marker = "extra == 'all'" },
{ name = "lerobot", extras = ["hardware"], marker = "extra == 'dev'" },
{ name = "lerobot", extras = ["hardware"], marker = "extra == 'robot'" },
{ name = "lerobot", extras = ["hardware"], marker = "extra == 'test'" },
{ name = "lerobot", extras = ["hilserl"], marker = "extra == 'all'" }, { name = "lerobot", extras = ["hilserl"], marker = "extra == 'all'" },
{ name = "lerobot", extras = ["hopejr"], marker = "extra == 'all'" }, { name = "lerobot", extras = ["hopejr"], marker = "extra == 'all'" },
{ name = "lerobot", extras = ["intelrealsense"], marker = "extra == 'all'" }, { name = "lerobot", extras = ["intelrealsense"], marker = "extra == 'all'" },
@@ -2512,6 +2625,10 @@ requires-dist = [
{ name = "lerobot", extras = ["scipy-dep"], marker = "extra == 'wallx'" }, { name = "lerobot", extras = ["scipy-dep"], marker = "extra == 'wallx'" },
{ name = "lerobot", extras = ["smolvla"], marker = "extra == 'all'" }, { name = "lerobot", extras = ["smolvla"], marker = "extra == 'all'" },
{ name = "lerobot", extras = ["test"], marker = "extra == 'all'" }, { name = "lerobot", extras = ["test"], marker = "extra == 'all'" },
{ name = "lerobot", extras = ["train"], marker = "extra == 'all'" },
{ name = "lerobot", extras = ["train"], marker = "extra == 'dev'" },
{ name = "lerobot", extras = ["train"], marker = "extra == 'test'" },
{ name = "lerobot", extras = ["train"], marker = "extra == 'training'" },
{ name = "lerobot", extras = ["transformers-dep"], marker = "extra == 'groot'" }, { name = "lerobot", extras = ["transformers-dep"], marker = "extra == 'groot'" },
{ name = "lerobot", extras = ["transformers-dep"], marker = "extra == 'hilserl'" }, { name = "lerobot", extras = ["transformers-dep"], marker = "extra == 'hilserl'" },
{ name = "lerobot", extras = ["transformers-dep"], marker = "extra == 'libero'" }, { name = "lerobot", extras = ["transformers-dep"], marker = "extra == 'libero'" },
@@ -2523,6 +2640,11 @@ requires-dist = [
{ name = "lerobot", extras = ["transformers-dep"], marker = "extra == 'wallx'" }, { name = "lerobot", extras = ["transformers-dep"], marker = "extra == 'wallx'" },
{ name = "lerobot", extras = ["transformers-dep"], marker = "extra == 'xvla'" }, { name = "lerobot", extras = ["transformers-dep"], marker = "extra == 'xvla'" },
{ name = "lerobot", extras = ["video-benchmark"], marker = "extra == 'all'" }, { name = "lerobot", extras = ["video-benchmark"], marker = "extra == 'all'" },
{ name = "lerobot", extras = ["viz"], marker = "extra == 'all'" },
{ name = "lerobot", extras = ["viz"], marker = "extra == 'dataset-viz'" },
{ name = "lerobot", extras = ["viz"], marker = "extra == 'dev'" },
{ name = "lerobot", extras = ["viz"], marker = "extra == 'robot'" },
{ name = "lerobot", extras = ["viz"], marker = "extra == 'test'" },
{ name = "lerobot", extras = ["wallx"], marker = "extra == 'all'" }, { name = "lerobot", extras = ["wallx"], marker = "extra == 'all'" },
{ name = "lerobot", extras = ["xvla"], marker = "extra == 'all'" }, { name = "lerobot", extras = ["xvla"], marker = "extra == 'all'" },
{ name = "matplotlib", marker = "extra == 'matplotlib-dep'", specifier = ">=3.10.3,<4.0.0" }, { name = "matplotlib", marker = "extra == 'matplotlib-dep'", specifier = ">=3.10.3,<4.0.0" },
@@ -2545,10 +2667,10 @@ requires-dist = [
{ name = "protobuf", marker = "extra == 'grpcio-dep'", specifier = ">=6.31.1,<6.32.0" }, { name = "protobuf", marker = "extra == 'grpcio-dep'", specifier = ">=6.31.1,<6.32.0" },
{ name = "pygame", marker = "extra == 'pygame-dep'", specifier = ">=2.5.1,<2.7.0" }, { name = "pygame", marker = "extra == 'pygame-dep'", specifier = ">=2.5.1,<2.7.0" },
{ name = "pymunk", marker = "extra == 'pusht'", specifier = ">=6.6.0,<7.0.0" }, { name = "pymunk", marker = "extra == 'pusht'", specifier = ">=6.6.0,<7.0.0" },
{ name = "pynput", specifier = ">=1.7.8,<1.9.0" }, { name = "pynput", marker = "extra == 'hardware'", specifier = ">=1.7.8,<1.9.0" },
{ name = "pyrealsense2", marker = "sys_platform != 'darwin' and extra == 'intelrealsense'", specifier = ">=2.55.1.6486,<2.57.0" }, { name = "pyrealsense2", marker = "sys_platform != 'darwin' and extra == 'intelrealsense'", specifier = ">=2.55.1.6486,<2.57.0" },
{ name = "pyrealsense2-macosx", marker = "sys_platform == 'darwin' and extra == 'intelrealsense'", specifier = ">=2.54,<2.57.0" }, { name = "pyrealsense2-macosx", marker = "sys_platform == 'darwin' and extra == 'intelrealsense'", specifier = ">=2.54,<2.57.0" },
{ name = "pyserial", specifier = ">=3.5,<4.0" }, { name = "pyserial", marker = "extra == 'hardware'", specifier = ">=3.5,<4.0" },
{ name = "pytest", marker = "extra == 'test'", specifier = ">=8.1.0,<9.0.0" }, { name = "pytest", marker = "extra == 'test'", specifier = ">=8.1.0,<9.0.0" },
{ name = "pytest-cov", marker = "extra == 'test'", specifier = ">=5.0.0,<8.0.0" }, { name = "pytest-cov", marker = "extra == 'test'", specifier = ">=5.0.0,<8.0.0" },
{ name = "pytest-timeout", marker = "extra == 'test'", specifier = ">=2.4.0,<3.0.0" }, { name = "pytest-timeout", marker = "extra == 'test'", specifier = ">=2.4.0,<3.0.0" },
@@ -2557,24 +2679,24 @@ requires-dist = [
{ name = "pyzmq", marker = "extra == 'unitree-g1'", specifier = ">=26.2.1,<28.0.0" }, { name = "pyzmq", marker = "extra == 'unitree-g1'", specifier = ">=26.2.1,<28.0.0" },
{ name = "qwen-vl-utils", marker = "extra == 'qwen-vl-utils-dep'", specifier = ">=0.0.11,<0.1.0" }, { name = "qwen-vl-utils", marker = "extra == 'qwen-vl-utils-dep'", specifier = ">=0.0.11,<0.1.0" },
{ name = "reachy2-sdk", marker = "extra == 'reachy2'", specifier = ">=1.0.15,<1.1.0" }, { name = "reachy2-sdk", marker = "extra == 'reachy2'", specifier = ">=1.0.15,<1.1.0" },
{ name = "rerun-sdk", specifier = ">=0.24.0,<0.27.0" }, { name = "rerun-sdk", marker = "extra == 'viz'", specifier = ">=0.24.0,<0.27.0" },
{ name = "safetensors", marker = "extra == 'groot'", specifier = ">=0.4.3,<1.0.0" }, { name = "safetensors", marker = "extra == 'groot'", specifier = ">=0.4.3,<1.0.0" },
{ name = "safetensors", marker = "extra == 'smolvla'", specifier = ">=0.4.3,<1.0.0" }, { name = "safetensors", marker = "extra == 'smolvla'", specifier = ">=0.4.3,<1.0.0" },
{ name = "scikit-image", marker = "extra == 'video-benchmark'", specifier = ">=0.23.2,<0.26.0" }, { name = "scikit-image", marker = "extra == 'video-benchmark'", specifier = ">=0.23.2,<0.26.0" },
{ name = "scipy", marker = "extra == 'all'", specifier = ">=1.14.0,<2.0.0" }, { name = "scipy", marker = "extra == 'all'", specifier = ">=1.14.0,<2.0.0" },
{ name = "scipy", marker = "extra == 'scipy-dep'", specifier = ">=1.14.0,<2.0.0" }, { name = "scipy", marker = "extra == 'scipy-dep'", specifier = ">=1.14.0,<2.0.0" },
{ name = "setuptools", specifier = ">=71.0.0,<81.0.0" }, { name = "setuptools", marker = "extra == 'build'", specifier = ">=71.0.0,<81.0.0" },
{ name = "teleop", marker = "extra == 'phone'", specifier = ">=0.1.0,<0.2.0" }, { name = "teleop", marker = "extra == 'phone'", specifier = ">=0.1.0,<0.2.0" },
{ name = "termcolor", specifier = ">=2.4.0,<4.0.0" }, { name = "termcolor", specifier = ">=2.4.0,<4.0.0" },
{ name = "timm", marker = "extra == 'groot'", specifier = ">=1.0.0,<1.1.0" }, { name = "timm", marker = "extra == 'groot'", specifier = ">=1.0.0,<1.1.0" },
{ name = "torch", specifier = ">=2.7,<2.11.0" }, { name = "torch", specifier = ">=2.7,<2.11.0" },
{ name = "torchcodec", marker = "(platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and sys_platform == 'linux') or (platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')", specifier = ">=0.3.0,<0.11.0" }, { name = "torchcodec", marker = "(platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and sys_platform == 'linux' and extra == 'dataset') or (platform_machine != 'x86_64' and sys_platform == 'darwin' and extra == 'dataset') or (sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32' and extra == 'dataset')", specifier = ">=0.3.0,<0.11.0" },
{ name = "torchdiffeq", marker = "extra == 'wallx'", specifier = ">=0.2.4,<0.3.0" }, { name = "torchdiffeq", marker = "extra == 'wallx'", specifier = ">=0.2.4,<0.3.0" },
{ name = "torchvision", specifier = ">=0.22.0,<0.26.0" }, { name = "torchvision", specifier = ">=0.22.0,<0.26.0" },
{ name = "transformers", marker = "extra == 'transformers-dep'", specifier = "==5.3.0" }, { name = "transformers", marker = "extra == 'transformers-dep'", specifier = "==5.3.0" },
{ name = "wandb", specifier = ">=0.24.0,<0.25.0" }, { name = "wandb", marker = "extra == 'train'", specifier = ">=0.24.0,<0.25.0" },
] ]
provides-extras = ["pygame-dep", "placo-dep", "transformers-dep", "grpcio-dep", "can-dep", "peft-dep", "scipy-dep", "qwen-vl-utils-dep", "matplotlib-dep", "feetech", "dynamixel", "damiao", "robstride", "openarms", "gamepad", "hopejr", "lekiwi", "unitree-g1", "reachy2", "kinematics", "intelrealsense", "phone", "wallx", "pi", "smolvla", "multi-task-dit", "groot", "sarm", "xvla", "hilserl", "async", "peft", "dev", "test", "video-benchmark", "aloha", "pusht", "libero", "metaworld", "all"] provides-extras = ["dataset", "train", "hardware", "viz", "build", "robot", "evaluation", "training", "dataset-viz", "pygame-dep", "placo-dep", "transformers-dep", "grpcio-dep", "can-dep", "peft-dep", "scipy-dep", "qwen-vl-utils-dep", "matplotlib-dep", "feetech", "dynamixel", "damiao", "robstride", "openarms", "gamepad", "hopejr", "lekiwi", "unitree-g1", "reachy2", "kinematics", "intelrealsense", "phone", "wallx", "pi", "smolvla", "multi-task-dit", "groot", "sarm", "xvla", "hilserl", "async", "peft", "dev", "test", "video-benchmark", "aloha", "pusht", "libero", "metaworld", "all"]
[[package]] [[package]]
name = "librt" name = "librt"
@@ -3359,7 +3481,7 @@ name = "nvidia-cudnn-cu12"
version = "9.10.2.21" version = "9.10.2.21"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" }, { name = "nvidia-cublas-cu12", marker = "platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and platform_machine != 's390x' and sys_platform == 'linux'" },
] ]
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" },
@@ -3370,7 +3492,7 @@ name = "nvidia-cufft-cu12"
version = "11.3.3.83" version = "11.3.3.83"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, { name = "nvidia-nvjitlink-cu12", marker = "platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and platform_machine != 's390x' and sys_platform == 'linux'" },
] ]
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" },
@@ -3397,9 +3519,9 @@ name = "nvidia-cusolver-cu12"
version = "11.7.3.90" version = "11.7.3.90"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" }, { name = "nvidia-cublas-cu12", marker = "platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and platform_machine != 's390x' and sys_platform == 'linux'" },
{ name = "nvidia-cusparse-cu12", marker = "sys_platform == 'linux'" }, { name = "nvidia-cusparse-cu12", marker = "platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and platform_machine != 's390x' and sys_platform == 'linux'" },
{ name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, { name = "nvidia-nvjitlink-cu12", marker = "platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and platform_machine != 's390x' and sys_platform == 'linux'" },
] ]
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" },
@@ -3410,7 +3532,7 @@ name = "nvidia-cusparse-cu12"
version = "12.5.8.93" version = "12.5.8.93"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, { name = "nvidia-nvjitlink-cu12", marker = "platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l' and platform_machine != 's390x' and sys_platform == 'linux'" },
] ]
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" },
@@ -4231,10 +4353,10 @@ name = "pyobjc-framework-applicationservices"
version = "12.1" version = "12.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "pyobjc-core", marker = "sys_platform != 'linux'" }, { name = "pyobjc-core", marker = "sys_platform != 'emscripten' and sys_platform != 'linux'" },
{ name = "pyobjc-framework-cocoa", marker = "sys_platform != 'linux'" }, { name = "pyobjc-framework-cocoa", marker = "sys_platform != 'emscripten' and sys_platform != 'linux'" },
{ name = "pyobjc-framework-coretext", marker = "sys_platform != 'linux'" }, { name = "pyobjc-framework-coretext", marker = "sys_platform != 'emscripten' and sys_platform != 'linux'" },
{ name = "pyobjc-framework-quartz", marker = "sys_platform != 'linux'" }, { name = "pyobjc-framework-quartz", marker = "sys_platform != 'emscripten' and sys_platform != 'linux'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/be/6a/d4e613c8e926a5744fc47a9e9fea08384a510dc4f27d844f7ad7a2d793bd/pyobjc_framework_applicationservices-12.1.tar.gz", hash = "sha256:c06abb74f119bc27aeb41bf1aef8102c0ae1288aec1ac8665ea186a067a8945b", size = 103247, upload-time = "2025-11-14T10:08:52.18Z" } sdist = { url = "https://files.pythonhosted.org/packages/be/6a/d4e613c8e926a5744fc47a9e9fea08384a510dc4f27d844f7ad7a2d793bd/pyobjc_framework_applicationservices-12.1.tar.gz", hash = "sha256:c06abb74f119bc27aeb41bf1aef8102c0ae1288aec1ac8665ea186a067a8945b", size = 103247, upload-time = "2025-11-14T10:08:52.18Z" }
wheels = [ wheels = [
@@ -4250,7 +4372,7 @@ name = "pyobjc-framework-cocoa"
version = "12.1" version = "12.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "pyobjc-core", marker = "sys_platform != 'linux'" }, { name = "pyobjc-core", marker = "sys_platform != 'emscripten' and sys_platform != 'linux'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/02/a3/16ca9a15e77c061a9250afbae2eae26f2e1579eb8ca9462ae2d2c71e1169/pyobjc_framework_cocoa-12.1.tar.gz", hash = "sha256:5556c87db95711b985d5efdaaf01c917ddd41d148b1e52a0c66b1a2e2c5c1640", size = 2772191, upload-time = "2025-11-14T10:13:02.069Z" } sdist = { url = "https://files.pythonhosted.org/packages/02/a3/16ca9a15e77c061a9250afbae2eae26f2e1579eb8ca9462ae2d2c71e1169/pyobjc_framework_cocoa-12.1.tar.gz", hash = "sha256:5556c87db95711b985d5efdaaf01c917ddd41d148b1e52a0c66b1a2e2c5c1640", size = 2772191, upload-time = "2025-11-14T10:13:02.069Z" }
wheels = [ wheels = [
@@ -4266,9 +4388,9 @@ name = "pyobjc-framework-coretext"
version = "12.1" version = "12.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "pyobjc-core", marker = "sys_platform != 'linux'" }, { name = "pyobjc-core", marker = "sys_platform != 'emscripten' and sys_platform != 'linux'" },
{ name = "pyobjc-framework-cocoa", marker = "sys_platform != 'linux'" }, { name = "pyobjc-framework-cocoa", marker = "sys_platform != 'emscripten' and sys_platform != 'linux'" },
{ name = "pyobjc-framework-quartz", marker = "sys_platform != 'linux'" }, { name = "pyobjc-framework-quartz", marker = "sys_platform != 'emscripten' and sys_platform != 'linux'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/29/da/682c9c92a39f713bd3c56e7375fa8f1b10ad558ecb075258ab6f1cdd4a6d/pyobjc_framework_coretext-12.1.tar.gz", hash = "sha256:e0adb717738fae395dc645c9e8a10bb5f6a4277e73cba8fa2a57f3b518e71da5", size = 90124, upload-time = "2025-11-14T10:14:38.596Z" } sdist = { url = "https://files.pythonhosted.org/packages/29/da/682c9c92a39f713bd3c56e7375fa8f1b10ad558ecb075258ab6f1cdd4a6d/pyobjc_framework_coretext-12.1.tar.gz", hash = "sha256:e0adb717738fae395dc645c9e8a10bb5f6a4277e73cba8fa2a57f3b518e71da5", size = 90124, upload-time = "2025-11-14T10:14:38.596Z" }
wheels = [ wheels = [
@@ -4284,8 +4406,8 @@ name = "pyobjc-framework-quartz"
version = "12.1" version = "12.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "pyobjc-core", marker = "sys_platform != 'linux'" }, { name = "pyobjc-core", marker = "sys_platform != 'emscripten' and sys_platform != 'linux'" },
{ name = "pyobjc-framework-cocoa", marker = "sys_platform != 'linux'" }, { name = "pyobjc-framework-cocoa", marker = "sys_platform != 'emscripten' and sys_platform != 'linux'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/94/18/cc59f3d4355c9456fc945eae7fe8797003c4da99212dd531ad1b0de8a0c6/pyobjc_framework_quartz-12.1.tar.gz", hash = "sha256:27f782f3513ac88ec9b6c82d9767eef95a5cf4175ce88a1e5a65875fee799608", size = 3159099, upload-time = "2025-11-14T10:21:24.31Z" } sdist = { url = "https://files.pythonhosted.org/packages/94/18/cc59f3d4355c9456fc945eae7fe8797003c4da99212dd531ad1b0de8a0c6/pyobjc_framework_quartz-12.1.tar.gz", hash = "sha256:27f782f3513ac88ec9b6c82d9767eef95a5cf4175ce88a1e5a65875fee799608", size = 3159099, upload-time = "2025-11-14T10:21:24.31Z" }
wheels = [ wheels = [