Compare commits

...

9 Commits

Author SHA1 Message Date
Steven Palma 2aba372b4e feat(ci): re-enable stale issues countdown (#4279) 2026-07-31 19:17:23 +02:00
Steven Palma c841a0c258 docs(evo1): add LIBERO reproduction recipe (#4278)
* docs(evo1): add LIBERO reproduction recipe

* docs(evo1): link verified LIBERO checkpoint

* docs(policies): address comments libero results

---------

Co-authored-by: Xingdong Zuo <18168681+zuoxingdong@users.noreply.github.com>
2026-07-31 17:23:10 +02:00
Steven Palma a3ddba2454 feat(cameras): prefer MJPG when fourcc unspecified in ZMQ (#4277)
* feat(cameras): prefer MJPG when fourcc unspecified; allow fourcc in ZMQ image server

With no fourcc set, OpenCV's V4L2 auto-negotiation selects uncompressed
YUYV when the camera offers it: ~16x the USB bandwidth of MJPG for
identical frames, which silently caps the frame rate on shared USB 2.0
buses (e.g. a Raspberry Pi with multiple cameras).

OpenCVCamera now prefers MJPG when config.fourcc is None: it requests
MJPG and keeps it only if the camera reports support (read-back),
falling back to the camera's own negotiation otherwise. An explicit
config.fourcc always takes precedence, so opting out is fourcc="YUYV".
The preference mirrors the existing pre/post size-and-fps ordering to
preserve the Windows DSHOW FOURCC-override handling.

Also adds a fourcc passthrough to the ZMQ image server, which previously
could not request a pixel format at all.

Behavior change: cameras that relied on implicit uncompressed capture
now receive camera-side JPEG unless fourcc is set explicitly.

* chore(cameras): address feedback

---------

Co-authored-by: Xingdong Zuo <18168681+zuoxingdong@users.noreply.github.com>
2026-07-31 17:13:32 +02:00
Steven Palma 99443a936d fix(train): honor policy.use_amp when the policy has no dtype field (#4274)
* fix(train): honor policy.use_amp when the policy has no dtype field

Since the Accelerate migration, accelerator.autocast() has been a no-op for
policies that do not expose a string dtype field (act, diffusion,
multi_task_dit, ...): PR #3912 wires mixed_precision from policy.dtype, so
these policies resolve to None and always train in full fp32 regardless of
--policy.use_amp=true. Meanwhile lerobot-eval does honor use_amp, and
PreTrainedConfig documents the flag as applying to training and evaluation.

Fall back to use_amp when no dtype field is present: bf16 where supported,
fp16 otherwise (matching torch.autocast's cuda default used by eval).

Measured on multi_task_dit / pusht, batch 256, H100: peak allocated drops
from ~75 GiB (fp32) to ~46.5 GiB under bf16 autocast.

* fix(train): conservative check

---------

Co-authored-by: Reece O'Mahoney <reece.omahoney3@gmail.com>
2026-07-31 16:46:54 +02:00
Steven Palma 7a3298ea26 fix(libero): don't reset inside step() on termination (#4273)
* fix(libero): don't reset inside step() on termination

LiberoEnv.step() called self.reset() when an episode terminated. Gymnasium's
vector envs default to AutoresetMode.NEXT_STEP, so the vector env resets the
sub-env again on the following step -- every termination paid two full resets.

The self-reset was also pure waste: `observation` is built from the terminal
raw_obs before it, so the reset's return value was discarded outright.

Worse, LiberoEnv.reset() advances init_state_id by _reset_stride, so the extra
reset skipped an initial state on every episode.

Measured with a counting subclass under SyncVectorEnv (gymnasium 1.3.0,
terminating every 4 steps over a 14-step loop):

  n_envs=1: 3 terminations -> 6 resets = 1 initial + 3 self + 2 autoreset
  n_envs=2: 6 terminations -> 12 resets = 2 initial + 6 self + 4 autoreset

(the final termination's autoreset does not fire before the loop ends)

Each LiberoEnv.reset() is a full LIBERO reset plus num_steps_wait settle steps,
so on the current default reset path this duplicates roughly 1.3 s per
termination.

Standalone (non-vectorised) users must now call reset() themselves after
termination, which is the Gymnasium contract.

* test(libero): pin the autoreset *default*, not the enum's spelling

The previous assertion checked `AutoresetMode.NEXT_STEP.value == "NextStep"`,
which is a naming detail. If a future Gymnasium flipped the vector-env default
to SAME_STEP, that assertion would still pass and the bug this fixes would come
back as a missing reset instead of a double one.

Assert the observable default instead. Verified on gymnasium 1.1.1 (the floor in
pyproject) and 1.3.0, for both SyncVectorEnv and AsyncVectorEnv. Negative control:
constructing with autoreset_mode=SAME_STEP fails the new assertion and passes the
old one.

Also document why `env._env = inner` is not redundant with the monkeypatched
factory: `LiberoEnv.__init__` defers simulator creation, so pre-binding keeps
`_ensure_env()` a no-op and avoids a stray `reset()` on the mock.

* test(libero): drop the redundant `env._env` prebind

@noron12234 flagged this as redundant. Their stated reason was wrong -- `__init__`
defers simulator creation (`self._env = None`, libero.py:180), so the monkeypatched
factory has not been called by the time it returns -- but the conclusion holds:
`_ensure_env()` pulls the same `inner` from the mocked factory on first `step()`.

I claimed the prebind was load-bearing because a stray `reset()` would pollute the
tests' call counts. That was wrong and I had not run it. Ran both variants against
the real class: the stray reset lands on `inner.reset`, which no assertion touches,
and both tests pass with or without the line. Dropping it.

* refactor(env): add explicit NEXT_STEP

* fix(style): pre-commit

---------

Co-authored-by: Dimitar Dimitrov <dvdimitrov13@gmail.com>
Co-authored-by: Dimitar Dimitrov <60075474+dvdimitrov13@users.noreply.github.com>
2026-07-31 16:39:49 +02:00
Steven Palma 2d8f5f314e feat(env): config to skip the discarded scene rebuild on reset Libero (#4272)
* perf(libero): skip the discarded scene rebuild on reset

LIBERO's OffScreenRenderEnv defaults to hard_reset=True, so every reset() frees
the MjSim, re-serialises the scene with model.get_xml(), recompiles it with
MjSim.from_xml_string(), constructs a fresh offscreen GL context and re-wires
every observable.

When init states are in use, LiberoEnv.reset() immediately calls
set_init_state(), which overwrites the whole sim state -- so all of that work is
discarded. This passes hard_reset=not init_states instead. Without init states
the randomisation reset() performs is the only thing placing the objects, so the
hard reset is kept.

Measured on an RTX 3060 Ti (EGL, robosuite 1.4.0, mujoco 3.2.7, 256x256 x2
cameras), through LiberoEnv.reset(), fresh env per arm:

  suite            hard      soft     saved
  libero_spatial   1697 ms   233 ms   1464 ms
  libero_object    1394 ms   172 ms   1222 ms
  libero_goal      1177 ms   164 ms   1013 ms
  libero_10        1528 ms   207 ms   1321 ms

Equivalence
-----------
Immediately after set_init_state, qpos, qvel, ctrl and act are bit-identical
between the two paths on every suite tested.

After the 10 settle steps that reset() runs, 9 of 41 qpos entries differ:
robot0_joint1..7 (<= 2.4e-5 rad) and gripper0_finger_joint1/2 (<= 2.1e-4 rad).
No object joint differs on any suite. The drift is driven by the gripper
component of the settle action ([0,0,0,0,0,0,-1]); replacing it with zeros keeps
the two paths bit-identical for 12 further steps, and with num_steps_wait=0 there
is no divergence at all.

Wrist-camera pixels can differ by up to ~87/255, because a sub-millimetre finger
displacement crosses rasterisation boundaries at 256x256. The pixel metric badly
overstates the physical difference here; 2.1e-4 rad is 0.012 degrees.

So this is not bit-identical end to end, and reviewers should decide whether
0.012 degrees of gripper drift is acceptable for the benchmark. It does not
change object placement, which is what the fixed init states exist to control.


* refactor(env): config param libero + docs

---------

Co-authored-by: Dimitar Dimitrov <dvdimitrov13@gmail.com>
2026-07-31 16:28:12 +02:00
Lin Junrong 732a12108e fix(so_follower): check the observation for None before copying it (#4255)
Four kinematic processor steps read the observation as

    observation = self.transition.get(TransitionKey.OBSERVATION).copy()
    if observation is None:
        raise ValueError("Joints observation is require for computing robot kinematics")

so `.copy()` runs first and the guard below it is unreachable. A transition
without an observation raises `AttributeError: 'NoneType' object has no
attribute 'copy'` instead of the intended message.

That transition is not hypothetical: `RobotProcessorPipeline.process_action`
builds one with `create_transition(action=action)`, which sets
`TransitionKey.OBSERVATION` to None.

Reads the value first, checks it, then copies. Affects EEReferenceAndDelta,
InverseKinematicsEEToJoints, GripperVelocityToJoint and InverseKinematicsRLStep.
Adds a parametrised regression test covering all four; each fails with the
AttributeError if the fix is reverted.

Co-authored-by: Steven Palma <imstevenpmwork@ieee.org>
2026-07-31 16:12:18 +02:00
Caroline Pascal 29fcf057dc fix(edit-dataset): redirect dataset.root via backup_path from get_output_path (#4271)
* fix(edit-dataset): redirect dataset.root via backup_path returned from get_output_path

The in-place backup logic compared `output_dir` (run through `.resolve()`) to
`dataset.root` (unresolved), so when `HF_LEROBOT_HOME` was a symlink the swap
to the `_old` backup never fired and `_copy_and_reindex_videos` then read from
the now-empty output directory. Have `get_output_path` return the backup path
directly so callers don't rely on path equality.

* feat(samefile): making same file detection more robust using os.path.samefile

---------

Co-authored-by: Reece O'Mahoney <reece.omahoney3@gmail.com>
2026-07-31 16:11:57 +02:00
Steven Palma 81db623b44 chore(dependecies): bump setuptools + pytest + uv version in CI (#4270) 2026-07-31 15:30:33 +02:00
20 changed files with 515 additions and 189 deletions
+1 -1
View File
@@ -53,7 +53,7 @@ permissions:
contents: read
env:
UV_VERSION: "0.8.0"
UV_VERSION: "0.11.30"
PYTHON_VERSION: "3.12"
# Cancel in-flight runs for the same branch/PR.
+1 -1
View File
@@ -27,7 +27,7 @@ on:
# Sets up the environment variables
env:
UV_VERSION: "0.8.0"
UV_VERSION: "0.11.30"
PYTHON_VERSION: "3.12"
DOCKER_IMAGE_NAME_CPU: huggingface/lerobot-cpu:latest
DOCKER_IMAGE_NAME_GPU: huggingface/lerobot-gpu:latest
+1 -1
View File
@@ -48,7 +48,7 @@ permissions:
# Sets up the environment variables
env:
UV_VERSION: "0.8.0"
UV_VERSION: "0.11.30"
PYTHON_VERSION: "3.12"
# Ensures that only the latest commit for a PR or branch is built, canceling older runs.
+1 -1
View File
@@ -37,7 +37,7 @@ permissions:
# Sets up the environment variables
env:
UV_VERSION: "0.8.0"
UV_VERSION: "0.11.30"
PYTHON_VERSION: "3.12"
DOCKER_IMAGE_NAME: huggingface/lerobot-gpu
+1 -1
View File
@@ -27,7 +27,7 @@ on:
# Sets up the environment variables
env:
UV_VERSION: "0.8.0"
UV_VERSION: "0.11.30"
PYTHON_VERSION: "3.12"
DOCKER_IMAGE_NAME: huggingface/lerobot-gpu:latest-deps
+1 -1
View File
@@ -21,7 +21,7 @@ on:
# Sets up the environment variables
env:
UV_VERSION: "0.8.0"
UV_VERSION: "0.11.30"
PYTHON_VERSION: "3.12"
jobs:
+5 -5
View File
@@ -19,8 +19,8 @@ on:
workflow_dispatch:
# Runs at 02:00
# schedule:
# - cron: "0 2 * * *"
schedule:
- cron: "0 2 * * *"
env:
CLOSE_ISSUE_MESSAGE: >
@@ -31,7 +31,7 @@ env:
Feel free to reopen if is still relevant, or to ping a collaborator if you have any questions.
WARN_ISSUE_MESSAGE: >
This issue has been automatically marked as stale because it has not had
recent activity (1 year). It will be closed if no further activity occurs.
recent activity (1 year). It will be closed if no further activity occurs within 30 days.
Any change, comment or update to this issue will reset this count.
Thank you for your contributions.
WARN_PR_MESSAGE: >
@@ -61,8 +61,8 @@ jobs:
exempt-pr-labels: never-stale
days-before-issue-stale: 365
days-before-issue-close: 30
days-before-pr-stale: 365
days-before-pr-close: 30
days-before-pr-stale: -1
days-before-pr-close: -1
delete-branch: true
close-issue-message: ${{ env.CLOSE_ISSUE_MESSAGE }}
close-pr-message: ${{ env.CLOSE_PR_MESSAGE }}
+218 -11
View File
@@ -23,18 +23,18 @@ The broader EVO1 project may include additional training scripts and dataset too
2. Install EVO1 dependencies:
```bash
pip install -e ".[evo1]"
pip install -e ".[training,evo1]"
```
For LIBERO evaluation, install the LIBERO extra as well:
For LIBERO training and evaluation, install the LIBERO extra as well:
```bash
pip install -e ".[evo1,libero]"
pip install -e ".[training,evo1,libero]"
```
3. Install a `flash-attn` wheel only if it is compatible with your Python, PyTorch, CUDA, and GPU stack. EVO1 falls back to standard attention when `flash_attn` is not available.
EVO1 uses the native Hugging Face `transformers` InternVL implementation, so `policy.vlm_model_name` must point to a natively converted checkpoint such as `OpenGVLab/InternVL3-1B-hf` (note the `-hf` suffix). The first run may download the configured VLM checkpoint unless `policy.vlm_model_name` points to a local model directory.
EVO1 uses the native Hugging Face `transformers` InternVL implementation, so `policy.vlm_model_name` must point to a natively converted checkpoint such as `OpenGVLab/InternVL3-1B-hf` (note the `-hf` suffix). The first run downloads the configured VLM checkpoint and later runs reuse it from the Hugging Face cache.
## Data Requirements
@@ -92,7 +92,7 @@ lerobot-train \
### Stage 2
Stage 2 finetunes the VLM branches and action head. A common workflow starts from a Stage 1 checkpoint:
Stage 2 loads the Stage 1 policy, but starts a fresh optimizer and scheduler:
```bash
lerobot-train \
@@ -152,16 +152,154 @@ lerobot-rollout \
### LIBERO Evaluation
> [!NOTE]
> Benchmark results for a `lerobot`-hosted LIBERO checkpoint trained with this implementation
> will be added once training completes.
#### Reference result
The official EVO1 LIBERO rollout protocol uses the raw LIBERO camera feature names
> [!NOTE]
> The released Stage-2 checkpoint passed clean-download and rollout verification:
> [`zuoxingdong/evo1_libero`](https://huggingface.co/zuoxingdong/evo1_libero), revision
> [`515921f4a2c1d3f3ad523721eafa26fdf2af315b`](https://huggingface.co/zuoxingdong/evo1_libero/commit/515921f4a2c1d3f3ad523721eafa26fdf2af315b).
> The clean-download evaluation used LeRobot revision
> [`e40b58a8dfa9e7b86918c374791599d070518d11`](https://github.com/huggingface/lerobot/commit/e40b58a8dfa9e7b86918c374791599d070518d11).
The single-run Stage-2 checkpoint at step 70,000 produced:
| Suite | Successful episodes | Episodes | Success rate |
| -------------- | ------------------: | --------: | -----------: |
| LIBERO Spatial | 485 | 500 | 97.0% |
| LIBERO Object | 496 | 500 | 99.2% |
| LIBERO Goal | 483 | 500 | 96.6% |
| LIBERO-10 | 469 | 500 | 93.8% |
| **Overall** | **1,933** | **2,000** | **96.65%** |
These results use one trained checkpoint and evaluation seed `1000`; they are not a multi-seed
mean or confidence estimate.
#### Reference training recipe
The released checkpoint records the complete resolved Stage-2 configuration in
[`train_config.json`](https://huggingface.co/zuoxingdong/evo1_libero/blob/515921f4a2c1d3f3ad523721eafa26fdf2af315b/train_config.json).
The measured run used two H100 GPUs with two DDP processes and batch 64 per process, giving global batch 128. Both stages used the same topology. The base VLM came from revision
`014c0583a0d4bedf29fbe2dbff4f865eb998e171` of `OpenGVLab/InternVL3-1B-hf`.
The released artifact does not record the exact LeRobot training commit or its original dependency lock,
so the commands below reproduce the recorded configuration and topology from a current checkout rather
than reconstructing the software environment bit for bit.
From a LeRobot source checkout, install the locked dependencies and download that exact VLM revision:
```bash
uv sync --locked --extra training --extra evo1 --extra libero
VLM_DIR=$(uv run hf download OpenGVLab/InternVL3-1B-hf \
--revision=014c0583a0d4bedf29fbe2dbff4f865eb998e171)
```
Stage 1 freezes the VLM and trains the action head for 5,000 steps:
```bash
uv run accelerate launch --num_processes=2 -m lerobot.scripts.lerobot_train \
--dataset.repo_id=lerobot/libero \
--dataset.revision=a1aaacb7f6cd6ee5fb43120f673cebb0cfea7dd4 \
--dataset.video_backend=torchcodec \
--dataset.return_uint8=true \
--dataset.image_transforms.enable=true \
--dataset.use_imagenet_stats=true \
--dataset.eval_split=0.0 \
--policy.type=evo1 \
--policy.training_stage=stage1 \
--policy.apply_training_stage_defaults=true \
--policy.vlm_model_name="${VLM_DIR}" \
--policy.vlm_num_layers=14 \
--policy.vlm_dtype=bfloat16 \
--policy.device=cuda \
--policy.use_amp=true \
--policy.use_flash_attn=true \
--policy.enable_gradient_checkpointing=true \
--policy.gradient_checkpointing_use_reentrant=false \
--policy.image_resolution='[448,448]' \
--policy.chunk_size=50 \
--policy.n_action_steps=50 \
--policy.max_state_dim=24 \
--policy.max_action_dim=24 \
--policy.dropout=0.2 \
--policy.optimizer_lr=1e-5 \
--policy.optimizer_weight_decay=1e-3 \
--policy.optimizer_grad_clip_norm=1.0 \
--policy.scheduler_warmup_steps=1000 \
--policy.push_to_hub=false \
--use_policy_training_preset=true \
--batch_size=64 \
--steps=5000 \
--save_checkpoint=true \
--save_checkpoint_to_hub=false \
--save_freq=2500 \
--log_freq=10 \
--env_eval_freq=0 \
--num_workers=4 \
--prefetch_factor=2 \
--persistent_workers=true \
--seed=1000 \
--wandb.enable=false \
--output_dir=./outputs/evo1-libero-stage1-g128-5k
```
Stage 2 loads the Stage-1 policy but starts a fresh optimizer and scheduler. It trains for 80,000 steps;
the reported checkpoint is the save at step 70,000:
```bash
uv run accelerate launch --num_processes=2 -m lerobot.scripts.lerobot_train \
--dataset.repo_id=lerobot/libero \
--dataset.revision=a1aaacb7f6cd6ee5fb43120f673cebb0cfea7dd4 \
--dataset.video_backend=torchcodec \
--dataset.return_uint8=true \
--dataset.image_transforms.enable=true \
--dataset.use_imagenet_stats=true \
--dataset.eval_split=0.0 \
--policy.path=./outputs/evo1-libero-stage1-g128-5k/checkpoints/005000/pretrained_model \
--policy.training_stage=stage2 \
--policy.apply_training_stage_defaults=true \
--policy.vlm_model_name="${VLM_DIR}" \
--policy.vlm_num_layers=14 \
--policy.vlm_dtype=float32 \
--policy.device=cuda \
--policy.use_amp=true \
--policy.use_flash_attn=true \
--policy.enable_gradient_checkpointing=true \
--policy.gradient_checkpointing_use_reentrant=false \
--policy.image_resolution='[448,448]' \
--policy.chunk_size=50 \
--policy.n_action_steps=50 \
--policy.max_state_dim=24 \
--policy.max_action_dim=24 \
--policy.dropout=0.2 \
--policy.optimizer_lr=1e-5 \
--policy.optimizer_weight_decay=1e-3 \
--policy.optimizer_grad_clip_norm=1.0 \
--policy.scheduler_warmup_steps=1000 \
--policy.push_to_hub=false \
--use_policy_training_preset=true \
--batch_size=64 \
--steps=80000 \
--resume=false \
--save_checkpoint=true \
--save_checkpoint_to_hub=false \
--save_freq=10000 \
--log_freq=10 \
--env_eval_freq=0 \
--num_workers=4 \
--prefetch_factor=2 \
--persistent_workers=true \
--seed=1000 \
--wandb.enable=false \
--output_dir=./outputs/evo1-libero-stage2-g128-80k
```
#### Author-format evaluation profile
The author-format EVO1 LIBERO profile uses the raw LIBERO camera feature names
(`observation.images.agentview_image` and `observation.images.robot0_eye_in_hand_image`), replans every
14 actions, and binarizes the gripper command before stepping the simulator. The EVO1 policy postprocessor
can crop the padded 24D action back to the 7D LIBERO action space and apply that gripper binarization. To
evaluate a LIBERO checkpoint under the same one-episode-per-task setting, keep the raw camera names instead
of the default `image`/`image2` mapping and set the LIBERO action postprocessing flags:
evaluate an author-format checkpoint under the same one-episode-per-task setting, keep the raw camera names
instead of the default `image`/`image2` mapping and set the LIBERO action postprocessing flags:
```bash
lerobot-eval \
@@ -181,6 +319,75 @@ lerobot-eval \
--eval.n_episodes=1
```
#### Native `lerobot/libero` v3 profile
Revision `a1aaacb7f6cd6ee5fb43120f673cebb0cfea7dd4` stores camera features as `image` and
`image2`. This example evaluates all ten LIBERO Object tasks, launching each task in a fresh process:
```bash
export MUJOCO_GL=egl
export PYOPENGL_PLATFORM=egl
suite=libero_object
horizon=280
for task_id in {0..9}; do
lerobot-eval \
--policy.path=zuoxingdong/evo1_libero \
--policy.pretrained_revision=515921f4a2c1d3f3ad523721eafa26fdf2af315b \
--policy.vlm_model_name=OpenGVLab/InternVL3-1B-hf \
--policy.device=cuda \
--policy.use_amp=true \
--policy.vlm_dtype=bfloat16 \
--policy.use_flash_attn=false \
--policy.enable_gradient_checkpointing=false \
--policy.vlm_num_layers=14 \
--policy.image_resolution='[448,448]' \
--policy.max_text_length=1024 \
--policy.chunk_size=50 \
--policy.n_action_steps=14 \
--policy.max_state_dim=24 \
--policy.max_action_dim=24 \
--policy.num_inference_timesteps=32 \
--policy.postprocess_action_dim=7 \
--policy.binarize_gripper=true \
--policy.gripper_threshold=0.0 \
--policy.gripper_below_threshold_value=-1.0 \
--policy.gripper_above_threshold_value=1.0 \
--env.type=libero \
--env.task="${suite}" \
--env.task_ids="[${task_id}]" \
--env.camera_name=agentview_image,robot0_eye_in_hand_image \
--env.camera_name_mapping="{agentview_image: image, robot0_eye_in_hand_image: image2}" \
--env.control_mode=relative \
--env.obs_type=pixels_agent_pos \
--env.observation_width=448 \
--env.observation_height=448 \
--env.init_states=true \
--env.episode_length="${horizon}" \
--env.render_mode=rgb_array \
--env.max_parallel_tasks=1 \
--eval.n_episodes=50 \
--eval.batch_size=1 \
--eval.use_async_envs=false \
--eval.recording=false \
--seed=1000 \
--output_dir="./outputs/evo1-libero-stage2-70k-eval/${suite}/task-${task_id}" \
--job_name="evo1-libero-stage2-70k-${suite}-task-${task_id}"
done
```
Run all ten task IDs for each suite with these horizons:
| `env.task` | `env.episode_length` |
| ---------------- | -------------------: |
| `libero_spatial` | `280` |
| `libero_object` | `280` |
| `libero_goal` | `300` |
| `libero_10` | `520` |
Set `suite` and `horizon` for each row. This gives 500 episodes per suite and 2,000 episodes overall, while
the loop's fresh process per task matches the measured RNG-reset topology.
## References
- [EVO1 repository](https://github.com/MINT-SJTU/Evo-1)
+14
View File
@@ -92,6 +92,20 @@ LIBERO supports two control modes — `relative` (default) and `absolute`. Diffe
--env.control_mode=relative # or "absolute"
```
### Reset performance
By default, LeRobot preserves LIBERO's hard-reset behavior. With fixed initial
states enabled, you can opt into soft resets to skip rebuilding the simulator
model and renderer on every episode:
```bash
--env.init_states=true --env.hard_reset=false
```
Soft resets are faster but are not bit-identical to hard resets after the
environment's settling steps, so camera observations and policy results may
differ slightly. Use hard resets when reproducing benchmark results.
### Policy inputs and outputs
**Observations:**
+14
View File
@@ -134,6 +134,20 @@ LIBERO-plus supports two control modes — `relative` (default) and `absolute`.
--env.control_mode=relative # or "absolute"
```
### Reset performance
By default, LeRobot preserves LIBERO's hard-reset behavior. With fixed initial
states enabled, you can opt into soft resets to skip rebuilding the simulator
model and renderer on every episode:
```bash
--env.init_states=true --env.hard_reset=false
```
Soft resets are faster but are not bit-identical to hard resets after the
environment's settling steps, so camera observations and policy results may
differ slightly. Use hard resets when reproducing benchmark results.
### Policy inputs and outputs
**Observations:**
+2 -2
View File
@@ -87,7 +87,7 @@ dependencies = [
# Build tools (required by opencv-python-headless on some platforms)
"cmake>=3.29.0.1,<4.2.0",
"setuptools>=71.0.0,<81.0.0",
"setuptools>=71.0.0,<82.0.0", # torch 2.11 requires setuptools<82; a higher cap makes the resolver downgrade torch
]
# Optional dependencies
@@ -261,7 +261,7 @@ annotations = [
# 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,<2.0.0", "mypy>=1.19.1", "ruff>=0.14.1", "lerobot[notebook]"]
notebook = ["jupyter>=1.0.0,<2.0.0", "ipykernel>=6.0.0,<7.0.0"]
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 = ["pytest>=8.1.0,<10.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"]
# Simulation
+1
View File
@@ -102,6 +102,7 @@ class ImageServer:
fps=self.fps,
width=shape[1],
height=shape[0],
fourcc=cfg.get("fourcc", "MJPG"),
color_mode=ColorMode.RGB,
)
camera = OpenCVCamera(cam_config)
+4
View File
@@ -328,6 +328,7 @@ class LiberoEnv(EnvConfig):
render_mode: str = "rgb_array"
camera_name: str = "agentview_image,robot0_eye_in_hand_image"
init_states: bool = True
hard_reset: bool = True
camera_name_mapping: dict[str, str] | None = None
observation_height: int = 360
observation_width: int = 360
@@ -356,6 +357,8 @@ class LiberoEnv(EnvConfig):
def __post_init__(self):
if self.fps <= 0:
raise ValueError(f"fps must be positive, got {self.fps}")
if not self.hard_reset and not self.init_states:
raise ValueError("hard_reset=False requires init_states=True")
if self.obs_type == "pixels":
self.features[LIBERO_KEY_PIXELS_AGENTVIEW] = PolicyFeature(
@@ -416,6 +419,7 @@ class LiberoEnv(EnvConfig):
"observation_height": self.observation_height,
"observation_width": self.observation_width,
"control_freq": self.fps,
"hard_reset": self.hard_reset,
}
if self.task_ids is not None:
kwargs["task_ids"] = self.task_ids
+15 -2
View File
@@ -128,10 +128,13 @@ class LiberoEnv(gym.Env):
control_freq: int = 20,
control_mode: str = "relative",
is_libero_plus: bool = False,
hard_reset: bool = True,
):
super().__init__()
if control_freq <= 0:
raise ValueError(f"control_freq must be positive, got {control_freq}")
if not hard_reset and not init_states:
raise ValueError("hard_reset=False requires init_states=True")
self.task_id = task_id
self.is_libero_plus = is_libero_plus
self.obs_type = obs_type
@@ -158,6 +161,7 @@ class LiberoEnv(gym.Env):
self.camera_name_mapping = camera_name_mapping
self.num_steps_wait = num_steps_wait
self.control_freq = control_freq
self.hard_reset = hard_reset
self.episode_index = episode_index
self.episode_length = episode_length
# Load once and keep
@@ -265,6 +269,9 @@ class LiberoEnv(gym.Env):
camera_heights=self.observation_height,
camera_widths=self.observation_width,
control_freq=self.control_freq,
# Soft resets skip LIBERO's model and renderer rebuild. They are opt-in
# because settle steps can make their observations differ from hard resets.
hard_reset=self.hard_reset,
)
env.reset()
self._env = env
@@ -377,8 +384,9 @@ class LiberoEnv(gym.Env):
}
)
observation = self._format_raw_obs(raw_obs)
if terminated:
self.reset()
# Return the terminal observation unchanged. The caller owns resetting after
# termination; vector envs created below use NEXT_STEP autoreset. Resetting here
# would therefore reset twice and skip an initial state.
truncated = False
return observation, reward, terminated, truncated, info
@@ -476,6 +484,7 @@ def create_libero_envs(
print(f"Restricting to task_ids={task_ids_filter}")
is_async = env_cls is gym.vector.AsyncVectorEnv
is_sync = env_cls is gym.vector.SyncVectorEnv
out: dict[str, dict[int, Any]] = defaultdict(dict)
for suite_name in suite_names:
@@ -512,6 +521,10 @@ def create_libero_envs(
cached_act_space = lazy.action_space
cached_metadata = lazy.metadata
out[suite_name][tid] = lazy
elif is_sync:
out[suite_name][tid] = gym.vector.SyncVectorEnv(
fns, autoreset_mode=gym.vector.AutoresetMode.NEXT_STEP
)
else:
out[suite_name][tid] = env_cls(fns)
print(f"Built vec env | suite={suite_name} | task_id={tid} | n_envs={n_envs}")
+6 -1
View File
@@ -212,7 +212,12 @@ class _LazyAsyncVectorEnv:
def _ensure(self) -> None:
if self._env is None:
self._env = gym.vector.AsyncVectorEnv(self._env_fns, context="forkserver", shared_memory=True)
self._env = gym.vector.AsyncVectorEnv(
self._env_fns,
context="forkserver",
shared_memory=True,
autoreset_mode=gym.vector.AutoresetMode.NEXT_STEP,
)
@property
def unwrapped(self):
@@ -77,11 +77,13 @@ class EEReferenceAndDelta(RobotActionProcessorStep):
_command_when_disabled: np.ndarray | None = field(default=None, init=False, repr=False)
def action(self, action: RobotAction) -> RobotAction:
observation = self.transition.get(TransitionKey.OBSERVATION).copy()
raw_observation = self.transition.get(TransitionKey.OBSERVATION)
if observation is None:
if raw_observation is None:
raise ValueError("Joints observation is require for computing robot kinematics")
observation = raw_observation.copy()
if self.use_ik_solution and "IK_solution" in self.transition.get(TransitionKey.COMPLEMENTARY_DATA):
q_raw = self.transition.get(TransitionKey.COMPLEMENTARY_DATA)["IK_solution"]
else:
@@ -311,10 +313,12 @@ class InverseKinematicsEEToJoints(RobotActionProcessorStep):
"Missing required end-effector pose components: ee.x, ee.y, ee.z, ee.wx, ee.wy, ee.wz, ee.gripper_pos must all be present in action"
)
observation = self.transition.get(TransitionKey.OBSERVATION).copy()
if observation is None:
raw_observation = self.transition.get(TransitionKey.OBSERVATION)
if raw_observation is None:
raise ValueError("Joints observation is require for computing robot kinematics")
observation = raw_observation.copy()
q_raw = np.array(
[float(v) for k, v in observation.items() if isinstance(k, str) and k.endswith(".pos")],
dtype=float,
@@ -391,13 +395,15 @@ class GripperVelocityToJoint(RobotActionProcessorStep):
discrete_gripper: bool = False
def action(self, action: RobotAction) -> RobotAction:
observation = self.transition.get(TransitionKey.OBSERVATION).copy()
raw_observation = self.transition.get(TransitionKey.OBSERVATION)
gripper_vel = action.pop("ee.gripper_vel")
if observation is None:
if raw_observation is None:
raise ValueError("Joints observation is require for computing robot kinematics")
observation = raw_observation.copy()
q_raw = np.array(
[float(v) for k, v in observation.items() if isinstance(k, str) and k.endswith(".pos")],
dtype=float,
@@ -583,10 +589,12 @@ class InverseKinematicsRLStep(ProcessorStep):
"Missing required end-effector pose components: ee.x, ee.y, ee.z, ee.wx, ee.wy, ee.wz, ee.gripper_pos must all be present in action"
)
observation = new_transition.get(TransitionKey.OBSERVATION).copy()
if observation is None:
raw_observation = new_transition.get(TransitionKey.OBSERVATION)
if raw_observation is None:
raise ValueError("Joints observation is require for computing robot kinematics")
observation = raw_observation.copy()
q_raw = np.array(
[float(v) for k, v in observation.items() if isinstance(k, str) and k.endswith(".pos")],
dtype=float,
+26 -14
View File
@@ -240,6 +240,7 @@ Using JSON config file:
import abc
import logging
import os
import shutil
import sys
from dataclasses import dataclass, field
@@ -384,24 +385,35 @@ def _resolve_io_paths(
return output_repo_id, input_path, output_path
def _is_in_place(input_path: Path, output_path: Path) -> bool:
"""Whether both paths point to the same dataset directory.
Uses os.path.samefile (device+inode) which is robust to case-insensitive filesystems, hardlinks
and symlinks.
"""
try:
return os.path.samefile(input_path, output_path)
except OSError:
return False
def get_output_path(
repo_id: str,
new_repo_id: str | None,
root: Path | str | None,
new_root: Path | str | None,
) -> tuple[str, Path]:
) -> tuple[str, Path, Path | None]:
output_repo_id, input_path, output_path = _resolve_io_paths(repo_id, new_repo_id, root, new_root)
# In case of in-place modification, create a backup of the original dataset (if it exists)
if output_path == input_path:
# In case of in-place modification, create a backup of the original dataset (if it exists).
backup_path: Path | None = None
if _is_in_place(input_path, output_path):
backup_path = input_path.with_name(input_path.name + "_old")
if input_path.exists():
if backup_path.exists():
shutil.rmtree(backup_path)
shutil.move(input_path, backup_path)
return output_repo_id, output_path
return output_repo_id, output_path, backup_path
def handle_delete_episodes(cfg: EditDatasetConfig) -> None:
@@ -412,7 +424,7 @@ def handle_delete_episodes(cfg: EditDatasetConfig) -> None:
raise ValueError("episode_indices must be specified for delete_episodes operation")
dataset = LeRobotDataset(cfg.repo_id, root=cfg.root)
output_repo_id, output_dir = get_output_path(
output_repo_id, output_dir, backup_path = get_output_path(
cfg.repo_id,
new_repo_id=cfg.new_repo_id,
root=cfg.root,
@@ -420,8 +432,8 @@ def handle_delete_episodes(cfg: EditDatasetConfig) -> None:
)
# In case of in-place modification, make the dataset point to the backup directory
if output_dir == dataset.root:
dataset.root = dataset.root.with_name(dataset.root.name + "_old")
if backup_path is not None:
dataset.root = backup_path
logging.info(f"Deleting episodes {cfg.operation.episode_indices} from {cfg.repo_id}")
new_dataset = delete_episodes(
@@ -525,7 +537,7 @@ def handle_remove_feature(cfg: EditDatasetConfig) -> None:
raise ValueError("feature_names must be specified for remove_feature operation")
dataset = LeRobotDataset(cfg.repo_id, root=cfg.root)
output_repo_id, output_dir = get_output_path(
output_repo_id, output_dir, backup_path = get_output_path(
cfg.repo_id,
new_repo_id=cfg.new_repo_id,
root=cfg.root,
@@ -533,8 +545,8 @@ def handle_remove_feature(cfg: EditDatasetConfig) -> None:
)
# In case of in-place modification, make the dataset point to the backup directory
if output_dir == dataset.root:
dataset.root = dataset.root.with_name(dataset.root.name + "_old")
if backup_path is not None:
dataset.root = backup_path
logging.info(f"Removing features {cfg.operation.feature_names} from {cfg.repo_id}")
new_dataset = remove_feature(
@@ -671,7 +683,7 @@ def handle_recompute_stats(cfg: EditDatasetConfig) -> None:
cfg.new_root,
default_new_repo_id=f"{cfg.repo_id}_recomputed_stats",
)
in_place = output_root == input_root
in_place = _is_in_place(input_root, output_root)
if in_place and not cfg.operation.overwrite:
raise ValueError(
@@ -731,7 +743,7 @@ def handle_reencode_videos(cfg: EditDatasetConfig) -> None:
cfg.new_root,
default_new_repo_id=f"{cfg.repo_id}_reencoded",
)
in_place = output_root == input_root
in_place = _is_in_place(input_root, output_root)
if in_place and not cfg.operation.overwrite:
raise ValueError(
+9 -1
View File
@@ -243,9 +243,17 @@ def train(cfg: TrainPipelineConfig, accelerator: "Accelerator | None" = None):
# Accelerate auto-detects the device based on the available hardware and ignores the policy.device setting.
# Force the device to be CPU when the active config's device is set to CPU (works for both policy and reward model training).
force_cpu = cfg.trainable_config.device == "cpu"
# Drive Accelerate's autocast from policy.dtype (bf16/fp16 activate it; float32/absent -> launcher default).
# Drive Accelerate's autocast from policy.dtype (bf16/fp16 activate it; float32 -> full precision).
has_policy_dtype = hasattr(cfg.trainable_config, "dtype")
policy_dtype = getattr(cfg.trainable_config, "dtype", None)
mixed_precision = {"bfloat16": "bf16", "float16": "fp16", "float32": "no"}.get(policy_dtype)
# Policies without a `dtype` field fall back to `use_amp`, which would otherwise be
# silently ignored here while lerobot-eval honors it. Follow torch.autocast's default
# for the configured device so training and evaluation use the same precision.
if not has_policy_dtype and getattr(cfg.trainable_config, "use_amp", False):
device_type = torch.device(cfg.trainable_config.device).type
autocast_dtype = torch.get_autocast_dtype(device_type)
mixed_precision = {torch.bfloat16: "bf16", torch.float16: "fp16"}[autocast_dtype]
accelerator = Accelerator(
step_scheduler_with_optimizer=False,
mixed_precision=mixed_precision,
@@ -17,9 +17,14 @@
import pytest
from lerobot.configs import FeatureType, PipelineFeatureType, PolicyFeature
from lerobot.processor.converters import create_transition
from lerobot.robots.so_follower.robot_kinematic_processor import (
EEReferenceAndDelta,
ForwardKinematicsJointsToEEAction,
ForwardKinematicsJointsToEEObservation,
GripperVelocityToJoint,
InverseKinematicsEEToJoints,
InverseKinematicsRLStep,
)
MOTOR_NAMES = ["shoulder_pan", "shoulder_lift", "elbow_flex", "wrist_flex", "wrist_roll", "gripper"]
@@ -43,3 +48,38 @@ def test_fk_feature_schema(step_cls, bucket, feature_type):
out = step_cls(kinematics=None, motor_names=MOTOR_NAMES).transform_features(features)[bucket]
assert set(out) == EE_KEYS
assert {feature.type for feature in out.values()} == {feature_type}
EE_ACTION = dict.fromkeys(EE_KEYS, 0.0)
@pytest.mark.parametrize(
("step", "action"),
[
(
EEReferenceAndDelta(kinematics=None, end_effector_step_sizes={}, motor_names=MOTOR_NAMES),
dict(EE_ACTION),
),
(InverseKinematicsEEToJoints(kinematics=None, motor_names=MOTOR_NAMES), dict(EE_ACTION)),
(GripperVelocityToJoint(), {**EE_ACTION, "ee.gripper_vel": 0.0}),
(InverseKinematicsRLStep(kinematics=None, motor_names=MOTOR_NAMES), dict(EE_ACTION)),
],
ids=[
"ee_reference_and_delta",
"inverse_kinematics_ee_to_joints",
"gripper_velocity_to_joint",
"inverse_kinematics_rl_step",
],
)
def test_missing_observation_raises_value_error(step, action):
"""A transition without an observation must surface the documented ValueError.
`RobotProcessorPipeline.process_action` builds its transition with
`create_transition(action=...)`, which sets `TransitionKey.OBSERVATION` to None.
These steps used to call `.copy()` on that before the None check, so the guard
below them was unreachable and an AttributeError escaped instead.
"""
transition = create_transition(action=action)
with pytest.raises(ValueError, match="Joints observation"):
step(transition)
Generated
+136 -136
View File
@@ -1,5 +1,5 @@
version = 1
revision = 2
revision = 3
requires-python = ">=3.12"
resolution-markers = [
"(python_full_version >= '3.15' and platform_machine == 'AMD64' and sys_platform == 'linux') or (python_full_version >= '3.15' and platform_machine == 'x86_64' and sys_platform == 'linux')",
@@ -402,10 +402,10 @@ name = "bddl"
version = "1.0.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jupytext", marker = "sys_platform == 'linux'" },
{ name = "networkx", marker = "sys_platform == 'linux'" },
{ name = "numpy", marker = "sys_platform == 'linux'" },
{ name = "pytest", marker = "sys_platform == 'linux'" },
{ name = "jupytext" },
{ name = "networkx" },
{ name = "numpy" },
{ name = "pytest" },
]
sdist = { url = "https://files.pythonhosted.org/packages/5c/37/0211f82891a9f14efcfd2b2096f8d9e4351398ad637fdd1ee59cfc580b0e/bddl-1.0.1.tar.gz", hash = "sha256:1fa4e6e5050b93888ff6fd8455c39bfb29d3864ce06b4c37c0f781f513a2ae26", size = 164809, upload-time = "2022-03-08T01:48:23.564Z" }
@@ -1010,7 +1010,7 @@ name = "cuda-bindings"
version = "12.9.7"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cuda-pathfinder", marker = "sys_platform == 'linux'" },
{ name = "cuda-pathfinder" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/32/45/557d4ed1fa54f0c7db8aee083229f624990d69f7d00f55477eed5c7e169a/cuda_bindings-12.9.7-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0666d3c082ef8f4b2d670950589373550e9f3bf564d635dd883f24a0b40402ff", size = 7071026, upload-time = "2026-05-27T18:44:13.356Z" },
@@ -1043,37 +1043,37 @@ wheels = [
[package.optional-dependencies]
cublas = [
{ name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-cublas-cu12" },
]
cudart = [
{ name = "nvidia-cuda-runtime-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-cuda-runtime-cu12" },
]
cufft = [
{ name = "nvidia-cufft-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-cufft-cu12" },
]
cufile = [
{ name = "nvidia-cufile-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-cufile-cu12" },
]
cupti = [
{ name = "nvidia-cuda-cupti-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-cuda-cupti-cu12" },
]
curand = [
{ name = "nvidia-curand-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-curand-cu12" },
]
cusolver = [
{ name = "nvidia-cusolver-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-cusolver-cu12" },
]
cusparse = [
{ name = "nvidia-cusparse-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-cusparse-cu12" },
]
nvjitlink = [
{ name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-nvjitlink-cu12" },
]
nvrtc = [
{ name = "nvidia-cuda-nvrtc-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-cuda-nvrtc-cu12" },
]
nvtx = [
{ name = "nvidia-nvtx-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-nvtx-cu12" },
]
[[package]]
@@ -1145,7 +1145,7 @@ name = "decord"
version = "0.6.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "numpy", marker = "(platform_machine != 'arm64' and sys_platform == 'darwin') or (platform_machine == 'AMD64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
{ name = "numpy" },
]
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" },
@@ -1283,10 +1283,10 @@ resolution-markers = [
"python_full_version == '3.14.*' and sys_platform == 'win32'",
]
dependencies = [
{ name = "absl-py", marker = "python_full_version >= '3.14'" },
{ name = "attrs", marker = "python_full_version >= '3.14'" },
{ name = "numpy", marker = "python_full_version >= '3.14'" },
{ name = "wrapt", marker = "python_full_version >= '3.14'" },
{ name = "absl-py" },
{ name = "attrs" },
{ name = "numpy" },
{ name = "wrapt" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a6/83/ce29720ccf934c6cfa9b9c95ebbe96558386e66886626066632b5e44afed/dm_tree-0.1.9.tar.gz", hash = "sha256:a4c7db3d3935a5a2d5e4b383fc26c6b0cd6f78c6d4605d3e7b518800ecd5342b", size = 35623, upload-time = "2025-01-30T20:45:37.13Z" }
wheels = [
@@ -1324,10 +1324,10 @@ resolution-markers = [
"python_full_version < '3.13' and sys_platform == 'win32'",
]
dependencies = [
{ name = "absl-py", marker = "python_full_version < '3.14'" },
{ name = "attrs", marker = "python_full_version < '3.14'" },
{ name = "numpy", marker = "python_full_version < '3.14'" },
{ name = "wrapt", marker = "python_full_version < '3.14'" },
{ name = "absl-py" },
{ name = "attrs" },
{ name = "numpy" },
{ name = "wrapt" },
]
sdist = { url = "https://files.pythonhosted.org/packages/5a/66/a3ec619d22b6baffa5ab853e8dc6ec9d0c837127948af59bb15b988d7312/dm_tree-0.1.10.tar.gz", hash = "sha256:22f37b599e01cc3402a17f79c257a802aebd8d326de05b54657650845956208a", size = 35748, upload-time = "2026-03-31T17:35:39.03Z" }
wheels = [
@@ -1911,7 +1911,7 @@ name = "h5py"
version = "3.16.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "numpy", marker = "sys_platform == 'linux'" },
{ name = "numpy" },
]
sdist = { url = "https://files.pythonhosted.org/packages/db/33/acd0ce6863b6c0d7735007df01815403f5589a21ff8c2e1ee2587a38f548/h5py-3.16.0.tar.gz", hash = "sha256:a0dbaad796840ccaa67a4c144a0d0c8080073c34c76d5a6941d6818678ef2738", size = 446526, upload-time = "2026-03-06T13:49:08.07Z" }
wheels = [
@@ -1955,23 +1955,23 @@ name = "hf-libero"
version = "0.1.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "bddl", marker = "sys_platform == 'linux'" },
{ name = "cloudpickle", marker = "sys_platform == 'linux'" },
{ name = "easydict", marker = "sys_platform == 'linux'" },
{ name = "einops", marker = "sys_platform == 'linux'" },
{ name = "future", marker = "sys_platform == 'linux'" },
{ name = "gymnasium", marker = "sys_platform == 'linux'" },
{ name = "hf-egl-probe", marker = "sys_platform == 'linux'" },
{ name = "hydra-core", marker = "sys_platform == 'linux'" },
{ name = "matplotlib", marker = "sys_platform == 'linux'" },
{ name = "mujoco", marker = "sys_platform == 'linux'" },
{ name = "numpy", marker = "sys_platform == 'linux'" },
{ name = "opencv-python", marker = "sys_platform == 'linux'" },
{ name = "robomimic", marker = "sys_platform == 'linux'" },
{ name = "robosuite", marker = "sys_platform == 'linux'" },
{ name = "thop", marker = "sys_platform == 'linux'" },
{ name = "transformers", marker = "sys_platform == 'linux'" },
{ name = "wandb", marker = "sys_platform == 'linux'" },
{ name = "bddl" },
{ name = "cloudpickle" },
{ name = "easydict" },
{ name = "einops" },
{ name = "future" },
{ name = "gymnasium" },
{ name = "hf-egl-probe" },
{ name = "hydra-core" },
{ name = "matplotlib" },
{ name = "mujoco" },
{ name = "numpy" },
{ name = "opencv-python" },
{ name = "robomimic" },
{ name = "robosuite" },
{ name = "thop" },
{ name = "transformers" },
{ name = "wandb" },
]
sdist = { url = "https://files.pythonhosted.org/packages/af/aa/4e9eb8715e0bff9cb6553db563a35d253393097d446f82bd53575e8b253d/hf_libero-0.1.4.tar.gz", hash = "sha256:c058d67ad5a2b589529c14d614282ef4cca3a7763dafa134f58a6c9039657e34", size = 2961319, upload-time = "2026-06-10T09:56:13.994Z" }
wheels = [
@@ -2122,9 +2122,9 @@ name = "hydra-core"
version = "1.3.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "antlr4-python3-runtime", marker = "sys_platform == 'linux'" },
{ name = "omegaconf", marker = "sys_platform == 'linux'" },
{ name = "packaging", marker = "sys_platform == 'linux'" },
{ name = "antlr4-python3-runtime" },
{ name = "omegaconf" },
{ name = "packaging" },
]
sdist = { url = "https://files.pythonhosted.org/packages/10/dd/220f0e91743136725352497e98540772a01fc7c3ab96ff16c3c74424e984/hydra_core-1.3.4.tar.gz", hash = "sha256:ad0f7b05a0242255a8984d5a4ed2f6847f7b783ed727368a2c0155ec52d6c34c", size = 3263348, upload-time = "2026-07-04T16:25:38.891Z" }
wheels = [
@@ -2677,11 +2677,11 @@ name = "jupytext"
version = "1.19.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown-it-py", marker = "sys_platform == 'linux'" },
{ name = "mdit-py-plugins", marker = "sys_platform == 'linux'" },
{ name = "nbformat", marker = "sys_platform == 'linux'" },
{ name = "packaging", marker = "sys_platform == 'linux'" },
{ name = "pyyaml", marker = "sys_platform == 'linux'" },
{ name = "markdown-it-py" },
{ name = "mdit-py-plugins" },
{ name = "nbformat" },
{ name = "packaging" },
{ name = "pyyaml" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/473f8ebb101553fb2ea6ab1d34324d6677844c968947ac050c759d539f2c/jupytext-1.19.5.tar.gz", hash = "sha256:605026446d605aa54fd7f7fc69df6ae51c7a46053d4cebf05afdc64d66de3df0", size = 4600916, upload-time = "2026-07-21T22:00:29.198Z" }
wheels = [
@@ -3471,7 +3471,7 @@ requires-dist = [
{ 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 = "pyserial", marker = "extra == 'pyserial-dep'", 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,<10.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 = "python-can", marker = "extra == 'can-dep'", specifier = ">=4.2.0,<5.0.0" },
@@ -3485,7 +3485,7 @@ requires-dist = [
{ 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 == 'scipy-dep'", specifier = ">=1.14.0,<2.0.0" },
{ name = "setuptools", specifier = ">=71.0.0,<81.0.0" },
{ name = "setuptools", specifier = ">=71.0.0,<82.0.0" },
{ name = "teleop", marker = "extra == 'phone'", specifier = ">=0.1.0,<0.2.0" },
{ name = "termcolor", specifier = ">=2.4.0,<4.0.0" },
{ name = "timm", marker = "extra == 'timm-dep'", specifier = ">=1.0.0,<1.1.0" },
@@ -3816,7 +3816,7 @@ name = "mdit-py-plugins"
version = "0.6.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown-it-py", marker = "sys_platform == 'linux'" },
{ name = "markdown-it-py" },
]
sdist = { url = "https://files.pythonhosted.org/packages/59/fc/f8d0863f8862f25602c0404d75568e89fb6b4109804645e5cdfb1be5cf56/mdit_py_plugins-0.6.1.tar.gz", hash = "sha256:a2bca0f039f39dbd35fb74ae1b5f998608c437463371f0ff7f49a19a17a114d0", size = 56114, upload-time = "2026-05-13T09:03:38.91Z" }
wheels = [
@@ -4295,8 +4295,8 @@ name = "numba"
version = "0.66.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "llvmlite", marker = "sys_platform == 'linux'" },
{ name = "numpy", marker = "sys_platform == 'linux'" },
{ name = "llvmlite" },
{ name = "numpy" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ae/a0/570e3dc53e5602b49108f62a13e529f1eec8bfc7ef37d49c825924dcf546/numba-0.66.0.tar.gz", hash = "sha256:b900e63a0e26c05ea9a6d5a3a5a0a177cb64c5011887bf43edb8c3ed2c38d363", size = 2806181, upload-time = "2026-07-01T23:12:46.36Z" }
wheels = [
@@ -4389,7 +4389,7 @@ name = "nvidia-cudnn-cu12"
version = "9.19.0.56"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-cublas-cu12" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/09/b8/277c51962ee46fa3e5b203ac5f76107c650f781d6891e681e28e6f3e9fe6/nvidia_cudnn_cu12-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:08caaf27fe556aca82a3ee3b5aa49a77e7de0cfcb7ff4e5c29da426387a8267e", size = 656910700, upload-time = "2026-02-03T20:40:25.508Z" },
@@ -4401,7 +4401,7 @@ name = "nvidia-cufft-cu12"
version = "11.3.3.83"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-nvjitlink-cu12" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/60/bc/7771846d3a0272026c416fbb7e5f4c1f146d6d80704534d0b187dd6f4800/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:848ef7224d6305cdb2a4df928759dca7b1201874787083b6e7550dd6765ce69a", size = 193109211, upload-time = "2025-03-07T01:44:56.873Z" },
@@ -4431,9 +4431,9 @@ name = "nvidia-cusolver-cu12"
version = "11.7.3.90"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-cusparse-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-cublas-cu12" },
{ name = "nvidia-cusparse-cu12" },
{ name = "nvidia-nvjitlink-cu12" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/c8/32/f7cd6ce8a7690544d084ea21c26e910a97e077c9b7f07bf5de623ee19981/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:db9ed69dbef9715071232caa9b69c52ac7de3a95773c2db65bdba85916e4e5c0", size = 267229841, upload-time = "2025-03-07T01:46:54.356Z" },
@@ -4445,7 +4445,7 @@ name = "nvidia-cusparse-cu12"
version = "12.5.8.93"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-nvjitlink-cu12" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/bc/f7/cd777c4109681367721b00a106f491e0d0d15cfa1fd59672ce580ce42a97/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b6c161cb130be1a07a27ea6923df8141f3c295852f4b260c65f18f3e0a091dc", size = 288117129, upload-time = "2025-03-07T01:47:40.407Z" },
@@ -4502,8 +4502,8 @@ name = "omegaconf"
version = "2.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "antlr4-python3-runtime", marker = "sys_platform == 'linux'" },
{ name = "pyyaml", marker = "sys_platform == 'linux'" },
{ name = "antlr4-python3-runtime" },
{ name = "pyyaml" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ce/3d/e4b57b8d9008c6ebe0d5eff901f91d5700cf7bdb8c8863df817463a7fd5e/omegaconf-2.3.1.tar.gz", hash = "sha256:e5e7de64aeebeddaf8e6d3f7a783b32ac2a01c0fbd9c878012caecb891a1f42a", size = 3298472, upload-time = "2026-06-11T05:05:12.885Z" }
wheels = [
@@ -4743,7 +4743,7 @@ name = "pexpect"
version = "4.9.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "ptyprocess", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" },
{ name = "ptyprocess" },
]
sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" }
wheels = [
@@ -5317,10 +5317,10 @@ name = "pyobjc-framework-applicationservices"
version = "12.2.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyobjc-core", marker = "sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "pyobjc-framework-cocoa", marker = "sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "pyobjc-framework-coretext", marker = "sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "pyobjc-framework-quartz", marker = "sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "pyobjc-core" },
{ name = "pyobjc-framework-cocoa" },
{ name = "pyobjc-framework-coretext" },
{ name = "pyobjc-framework-quartz" },
]
sdist = { url = "https://files.pythonhosted.org/packages/5e/4d/0ebdd8144aba94b8fe9828ccee5616a4bf53d1f8bc51cff55f3cce86d695/pyobjc_framework_applicationservices-12.2.1.tar.gz", hash = "sha256:048ea663c9ac75c44a15dc7d5b8d78cbb4c97bf1c76e83835e8d5498e184001f", size = 109342, upload-time = "2026-06-19T16:19:46.149Z" }
wheels = [
@@ -5338,7 +5338,7 @@ name = "pyobjc-framework-cocoa"
version = "12.2.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyobjc-core", marker = "sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "pyobjc-core" },
]
sdist = { url = "https://files.pythonhosted.org/packages/51/34/fbe38a204643aa4e1b91391cdce07a34da565a69171ebcad08de7438a556/pyobjc_framework_cocoa-12.2.1.tar.gz", hash = "sha256:b94b37fe5730e5ae1fb0052912cd174e6ec329b0bfba4a012ae5db1014b5864b", size = 3125751, upload-time = "2026-06-19T16:20:05.159Z" }
wheels = [
@@ -5356,9 +5356,9 @@ name = "pyobjc-framework-coretext"
version = "12.2.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyobjc-core", marker = "sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "pyobjc-framework-cocoa", marker = "sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "pyobjc-framework-quartz", marker = "sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "pyobjc-core" },
{ name = "pyobjc-framework-cocoa" },
{ name = "pyobjc-framework-quartz" },
]
sdist = { url = "https://files.pythonhosted.org/packages/5a/9c/4c7f452059dc1d3845b8e627b9113c247a997b9b07518e848c2ab7ff3149/pyobjc_framework_coretext-12.2.1.tar.gz", hash = "sha256:af740e784d7c592c34025ec7165f4f6c1a69b5a2d9075f06e41e4f77c212aed2", size = 97349, upload-time = "2026-06-19T16:20:22.508Z" }
wheels = [
@@ -5376,8 +5376,8 @@ name = "pyobjc-framework-quartz"
version = "12.2.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyobjc-core", marker = "sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "pyobjc-framework-cocoa", marker = "sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "pyobjc-core" },
{ name = "pyobjc-framework-cocoa" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3b/f6/2a8b84dbf1fe7c04dd96ea73d991678d4e09a909f51971ecc51629bb2ab4/pyobjc_framework_quartz-12.2.1.tar.gz", hash = "sha256:b3b8b6f71e66147f8ff9e6213864cc8527e3a0b1ee90835b93ce221f4802d9b0", size = 3215521, upload-time = "2026-06-19T16:21:30.199Z" }
wheels = [
@@ -5940,18 +5940,18 @@ name = "robomimic"
version = "0.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "egl-probe", marker = "sys_platform == 'linux'" },
{ name = "h5py", marker = "sys_platform == 'linux'" },
{ name = "imageio", marker = "sys_platform == 'linux'" },
{ name = "imageio-ffmpeg", marker = "sys_platform == 'linux'" },
{ name = "numpy", marker = "sys_platform == 'linux'" },
{ name = "psutil", marker = "sys_platform == 'linux'" },
{ name = "tensorboard", marker = "sys_platform == 'linux'" },
{ name = "tensorboardx", marker = "sys_platform == 'linux'" },
{ name = "termcolor", marker = "sys_platform == 'linux'" },
{ name = "torch", version = "2.11.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "sys_platform == 'linux'" },
{ name = "torchvision", version = "0.26.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "sys_platform == 'linux'" },
{ name = "tqdm", marker = "sys_platform == 'linux'" },
{ name = "egl-probe" },
{ name = "h5py" },
{ name = "imageio" },
{ name = "imageio-ffmpeg" },
{ name = "numpy" },
{ name = "psutil" },
{ name = "tensorboard" },
{ name = "tensorboardx" },
{ name = "termcolor" },
{ name = "torch", version = "2.11.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" } },
{ name = "torchvision", version = "0.26.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" } },
{ name = "tqdm" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3d/c3/44b1d1ea4bcb4bbed43d19e09505f4142714451ded74020d4f679cdc89fb/robomimic-0.2.0.tar.gz", hash = "sha256:ee3bb5cf9c3e1feead6b57b43c5db738fd0a8e0c015fdf6419808af8fffdc463", size = 192919, upload-time = "2021-12-17T19:00:33.279Z" }
@@ -5960,12 +5960,12 @@ name = "robosuite"
version = "1.4.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mujoco", marker = "sys_platform == 'linux'" },
{ name = "numba", marker = "sys_platform == 'linux'" },
{ name = "numpy", marker = "sys_platform == 'linux'" },
{ name = "opencv-python", marker = "sys_platform == 'linux'" },
{ name = "pillow", marker = "sys_platform == 'linux'" },
{ name = "scipy", marker = "sys_platform == 'linux'" },
{ name = "mujoco" },
{ name = "numba" },
{ name = "numpy" },
{ name = "opencv-python" },
{ name = "pillow" },
{ name = "scipy" },
]
sdist = { url = "https://files.pythonhosted.org/packages/25/a1/9dd07a9a5e09c6aa032faf531da985808b34437cbf6c8f358fe8f7c47118/robosuite-1.4.0.tar.gz", hash = "sha256:a8a6233d7458dbd91bf00a86cab15aa1c178bd9d1b28d515db2cf3d152cb48e6", size = 192182147, upload-time = "2022-12-01T07:31:55.791Z" }
wheels = [
@@ -6386,16 +6386,16 @@ name = "tensorboard"
version = "2.20.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "absl-py", marker = "sys_platform == 'linux'" },
{ name = "grpcio", marker = "sys_platform == 'linux'" },
{ name = "markdown", marker = "sys_platform == 'linux'" },
{ name = "numpy", marker = "sys_platform == 'linux'" },
{ name = "packaging", marker = "sys_platform == 'linux'" },
{ name = "pillow", marker = "sys_platform == 'linux'" },
{ name = "protobuf", marker = "sys_platform == 'linux'" },
{ name = "setuptools", marker = "sys_platform == 'linux'" },
{ name = "tensorboard-data-server", marker = "sys_platform == 'linux'" },
{ name = "werkzeug", marker = "sys_platform == 'linux'" },
{ name = "absl-py" },
{ name = "grpcio" },
{ name = "markdown" },
{ name = "numpy" },
{ name = "packaging" },
{ name = "pillow" },
{ name = "protobuf" },
{ name = "setuptools" },
{ name = "tensorboard-data-server" },
{ name = "werkzeug" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/9c/d9/a5db55f88f258ac669a92858b70a714bbbd5acd993820b41ec4a96a4d77f/tensorboard-2.20.0-py3-none-any.whl", hash = "sha256:9dc9f978cb84c0723acf9a345d96c184f0293d18f166bb8d59ee098e6cfaaba6", size = 5525680, upload-time = "2025-07-17T19:20:49.638Z" },
@@ -6415,9 +6415,9 @@ name = "tensorboardx"
version = "2.6.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "numpy", marker = "sys_platform == 'linux'" },
{ name = "packaging", marker = "sys_platform == 'linux'" },
{ name = "protobuf", marker = "sys_platform == 'linux'" },
{ name = "numpy" },
{ name = "packaging" },
{ name = "protobuf" },
]
sdist = { url = "https://files.pythonhosted.org/packages/48/a9/fc520ea91ab1f3ba51cbf3fe24f2b6364ed3b49046969e0868d46d6da372/tensorboardx-2.6.5.tar.gz", hash = "sha256:ca176db3997ee8c07d2eb77381225956a3fd1c10c91beafab1f17069adc47017", size = 4770195, upload-time = "2026-04-03T15:40:23.803Z" }
wheels = [
@@ -6452,7 +6452,7 @@ name = "thop"
version = "0.1.1.post2209072238"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "torch", version = "2.11.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "sys_platform == 'linux'" },
{ name = "torch", version = "2.11.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" } },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/bb/0f/72beeab4ff5221dc47127c80f8834b4bcd0cb36f6ba91c0b1d04a1233403/thop-0.1.1.post2209072238-py3-none-any.whl", hash = "sha256:01473c225231927d2ad718351f78ebf7cffe6af3bed464c4f1ba1ef0f7cdda27", size = 15443, upload-time = "2022-09-07T14:38:37.211Z" },
@@ -6558,13 +6558,13 @@ resolution-markers = [
"python_full_version < '3.13' and sys_platform == 'win32'",
]
dependencies = [
{ name = "filelock", marker = "sys_platform != 'linux'" },
{ name = "fsspec", marker = "sys_platform != 'linux'" },
{ name = "jinja2", marker = "sys_platform != 'linux'" },
{ name = "networkx", marker = "sys_platform != 'linux'" },
{ name = "setuptools", marker = "sys_platform != 'linux'" },
{ name = "sympy", marker = "sys_platform != 'linux'" },
{ name = "typing-extensions", marker = "sys_platform != 'linux'" },
{ name = "filelock" },
{ name = "fsspec" },
{ name = "jinja2" },
{ name = "networkx" },
{ name = "setuptools" },
{ name = "sympy" },
{ name = "typing-extensions" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/6f/8b/69e3008d78e5cee2b30183340cc425081b78afc5eff3d080daab0adda9aa/torch-2.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b5866312ee6e52ea625cd211dcb97d6a2cdc1131a5f15cc0d87eec948f6dd34", size = 80606338, upload-time = "2026-03-23T18:11:34.781Z" },
@@ -6598,20 +6598,20 @@ resolution-markers = [
"python_full_version < '3.13' and platform_machine != 'AMD64' and platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'x86_64' and sys_platform == 'linux'",
]
dependencies = [
{ name = "cuda-bindings", marker = "sys_platform == 'linux'" },
{ name = "cuda-toolkit", extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" },
{ name = "filelock", marker = "sys_platform == 'linux'" },
{ name = "fsspec", marker = "sys_platform == 'linux'" },
{ name = "jinja2", marker = "sys_platform == 'linux'" },
{ name = "networkx", marker = "sys_platform == 'linux'" },
{ name = "nvidia-cudnn-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-cusparselt-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-nccl-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-nvshmem-cu12", marker = "sys_platform == 'linux'" },
{ name = "setuptools", marker = "sys_platform == 'linux'" },
{ name = "sympy", marker = "sys_platform == 'linux'" },
{ name = "triton", marker = "sys_platform == 'linux'" },
{ name = "typing-extensions", marker = "sys_platform == 'linux'" },
{ name = "cuda-bindings" },
{ name = "cuda-toolkit", extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"] },
{ name = "filelock" },
{ name = "fsspec" },
{ name = "jinja2" },
{ name = "networkx" },
{ name = "nvidia-cudnn-cu12" },
{ name = "nvidia-cusparselt-cu12" },
{ name = "nvidia-nccl-cu12" },
{ name = "nvidia-nvshmem-cu12" },
{ name = "setuptools" },
{ name = "sympy" },
{ name = "triton" },
{ name = "typing-extensions" },
]
wheels = [
{ url = "https://download-r2.pytorch.org/whl/cu128/torch-2.11.0%2Bcu128-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:9c8f38efee365cb9d334de8a83ce52fc7e5fc9e5a7b0853285efa1b69e00b0f2", upload-time = "2026-04-27T17:41:30Z" },
@@ -6682,9 +6682,9 @@ resolution-markers = [
"python_full_version < '3.13' and sys_platform == 'win32'",
]
dependencies = [
{ name = "numpy", marker = "sys_platform != 'linux'" },
{ name = "pillow", marker = "sys_platform != 'linux'" },
{ name = "torch", version = "2.11.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux'" },
{ name = "numpy" },
{ name = "pillow" },
{ name = "torch", version = "2.11.0", source = { registry = "https://pypi.org/simple" } },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/ae/e7/56b47cc3b132aea90ccce22bcb8975dec688b002150012acc842846039d0/torchvision-0.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c409e1c3fdebec7a3834465086dbda8bf7680eff79abf7fd2f10c6b59520a7a4", size = 1863502, upload-time = "2026-03-23T18:12:57.326Z" },
@@ -6718,9 +6718,9 @@ resolution-markers = [
"python_full_version < '3.13' and platform_machine != 'AMD64' and platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'x86_64' and sys_platform == 'linux'",
]
dependencies = [
{ name = "numpy", marker = "sys_platform == 'linux'" },
{ name = "pillow", marker = "sys_platform == 'linux'" },
{ name = "torch", version = "2.11.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "sys_platform == 'linux'" },
{ name = "numpy" },
{ name = "pillow" },
{ name = "torch", version = "2.11.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" } },
]
wheels = [
{ url = "https://download-r2.pytorch.org/whl/cu128/torchvision-0.26.0%2Bcu128-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:63e35234aed13b6edda37056f417b5c281249669db631e706811917af36b21d7", upload-time = "2026-04-09T23:21:35Z" },
@@ -7222,7 +7222,7 @@ name = "werkzeug"
version = "3.1.8"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markupsafe", marker = "sys_platform == 'linux'" },
{ name = "markupsafe" },
]
sdist = { url = "https://files.pythonhosted.org/packages/dd/b2/381be8cfdee792dd117872481b6e378f85c957dd7c5bca38897b08f765fd/werkzeug-3.1.8.tar.gz", hash = "sha256:9bad61a4268dac112f1c5cd4630a56ede601b6ed420300677a869083d70a4c44", size = 875852, upload-time = "2026-04-02T18:49:14.268Z" }
wheels = [