File size: 4,666 Bytes
32502a8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
[build-system]
requires = ["uv_build>=0.8.19,<0.9.0"]
build-backend = "uv_build"

[project]
name = "mjlab-microduck"
version = "0.1.0"
description = "RL training environments for the Microduck robot, built on mjlab"
readme = "README.md"
license = "Apache-2.0"
license-files = ["LICENSE"]
# <3.13: bam (better-actuator-models) pins requires-python <3.13, and jobs must
# run the same interpreter we test locally (3.12) — a floating upper bound let
# HF jobs pick 3.13.14 (2026-07-21).
requires-python = ">=3.12, <3.13"
dependencies = [
    "mjlab==1.3.0",
    "warp-lang==1.12.0",
    # BAM distribution name is `better-actuator-models`; the import stays `bam`.
    "better-actuator-models",
    "onnxruntime>=1.24.4",
    "rustypot>=1.4.2",
    "huggingface_hub>=0.27.0",
    "matplotlib>=3.10.9",
    # mjlab 1.3.0 imports scipy (terrains/heightfield_terrains.py) but forgets
    # to declare it — without this line a fresh `uv sync` (e.g. on HF jobs)
    # can't even `import mjlab_microduck` (found 2026-07-21).
    "scipy>=1.16",
    # Direct dep ONLY so [tool.uv.sources] can bind torch to the CUDA index on
    # aarch64 (see the torch source below) — uv applies sources to DIRECT
    # dependencies only, so as a purely transitive dep (mjlab, rsl_rl) the
    # source entry is silently ignored.
    # Pinned to the exact version uv.lock already resolved from PyPI so this
    # changes only the SOURCE of the wheel on aarch64, not the version: a
    # floating `>=` lets the CUDA index (which carries newer builds than the
    # PyPI pin) drag torch 2.9.1 -> 2.13.0, an unvetted bump for mjlab/rsl_rl.
    "torch==2.9.1",
]

[project.entry-points."mjlab.tasks"]
mjlab_microduck = "mjlab_microduck.tasks"

[project.scripts]
# Shadows mjlab's `train` entry point: identical behavior, plus a --hf-jobs
# flag that submits the run to Hugging Face Jobs instead (see train_cli.py).
train = "mjlab_microduck.train_cli:main"
  
[tool.ruff]
src = ["src"]  # Helpful for recognizing first-party imports.
indent-width = 4
exclude = [
  "src/mjlab/third_party",
  "typings",
]

[tool.uv]
override-dependencies = [
    # NOTE: do NOT override mujoco — mjlab 1.3.0's mujoco-warp pins a compatible
    # mujoco (3.10.x); an override here previously forced mujoco down to 3.4.0,
    # which lacks mjDSBL_MULTICCD that mujoco-warp 3.8.1 imports → import crash.
    # bam pins protobuf<4.0 (for its zmq/dynamixel messaging, which we don't
    # use — we only import bam.model/bam.actuator). Without this override the
    # downgrade cascades onnx down to 1.17.0, which has no wheel and fails to
    # build from source.
    "protobuf>=4.0,<7.0",
    # Keep onnx on a version with prebuilt wheels (1.17.0 has no py3.13 wheel and
    # fails to build from source). Pulling in bam otherwise nudges it downward.
    "onnx>=1.20.1",
    # bam depends on the PyPI stub `zmq==0.0.0`, whose prebuilt wheel is invalid
    # (missing .dist-info) -> `uv sync` fails on a FRESH install (e.g. HF Jobs
    # remote build), even though a warm local cache tolerated it. We only use
    # bam.mjlab (no zmq/erob messaging), so drop zmq via an always-false marker.
    "zmq ; python_version < '3.0'",
]

[tool.uv.sources]
# mjlab now comes from PyPI (==1.3.0, pinned in [project.dependencies]); no git source.
# Official BAM actuator model. Switch to `main` (or a tag) once the
# mjlab-facing API lands there.
better-actuator-models = { git = "https://github.com/Rhoban/bam.git", branch = "mjlab_frictionloss" }
# For local BAM development, comment the line above and use:
# better-actuator-models = { path = "/home/antoine/Rhoban/bam", editable = true }
# On linux-aarch64 (DGX Spark / GB10) PyPI's torch wheel is CPU-ONLY:
# torch.__version__ == "2.9.1+cpu", torch.version.cuda is None, so
# torch.cuda.device_count() == 0 and mjlab's select_gpus() indexes an empty
# list -> `IndexError: list index out of range` before training even starts
# (mjlab/utils/gpu.py:70). Route torch to PyTorch's CUDA index there.
# cu129 (not cu130) matches the CUDA toolkit warp 1.12.0 bundles, so the
# zero-copy warp<->torch interop stays on one runtime major version.
# The marker keeps x86_64 (HF Jobs) on PyPI, where the wheel already bundles
# CUDA via its nvidia-*-cu12 deps — that resolution is unchanged.
torch = [
  { index = "pytorch-cu129", marker = "sys_platform == 'linux' and platform_machine == 'aarch64'" },
]

[[tool.uv.index]]
name = "pytorch-cu129"
url = "https://download.pytorch.org/whl/cu129"
# explicit: only packages that name this index resolve from it.
explicit = true