Use embedded public hand assets and allow model estimates without login
Browse files- README.md +4 -1
- flora-worker-image.json +3 -2
- hand-pipeline-catalog.json +1 -1
- hand-worker-image.json +15 -5
- hand_actions.py +1 -11
- pipeline-source.tar.gz +2 -2
- pipeline-version.json +2 -2
- test_hand_actions.py +16 -8
README.md
CHANGED
|
@@ -51,7 +51,10 @@ not apply. Aria Gen1 image rotation is explicit. The annotated copy retains ever
|
|
| 51 |
source frame, writes 156D actions and native SOMA parameters, and masks frames
|
| 52 |
outside directly observed bimanual spans of at least 30 frames. This recipe has
|
| 53 |
no motion infiller and needs no MANO files. Model assets come from a pinned,
|
| 54 |
-
checksummed
|
|
|
|
|
|
|
|
|
|
| 55 |
The worker image and asset identities are verified before native inference.
|
| 56 |
|
| 57 |
The measured development reference is 39.41–41.04 input FPS on RTX 5090 over ten
|
|
|
|
| 51 |
source frame, writes 156D actions and native SOMA parameters, and masks frames
|
| 52 |
outside directly observed bimanual spans of at least 30 frames. This recipe has
|
| 53 |
no motion infiller and needs no MANO files. Model assets come from a pinned,
|
| 54 |
+
checksummed package embedded in the public worker image. The Space build fetches
|
| 55 |
+
the large weights from public upstream sources; Jobs need no model-repository access.
|
| 56 |
+
Browsing and estimating public datasets need no login. Sign in to launch HF Jobs
|
| 57 |
+
or access private datasets.
|
| 58 |
The worker image and asset identities are verified before native inference.
|
| 59 |
|
| 60 |
The measured development reference is 39.41–41.04 input FPS on RTX 5090 over ten
|
flora-worker-image.json
CHANGED
|
@@ -31,7 +31,7 @@
|
|
| 31 |
"flora_batcher.py": "38e03e2032af8d5f3ed34980cc4d89927fe70c9742aa2ce13f4d0ccdd6fba727",
|
| 32 |
"flora_speed_entry.py": "192b18d75b9d876c2a65d88de340c8cea7c8da90543d0510b5ee6865d123d524",
|
| 33 |
"studio_runtime.py": "2b393bf7fafcdead638ad1cea35e49df8c6edce725f5545b76fd68e0a8fedd80",
|
| 34 |
-
"pipeline-version.json": "
|
| 35 |
"flora_vision.py": "a5a4231abf7b615164c40986df5f87047f745a23c55d86385d88f2a09e70f1e8"
|
| 36 |
},
|
| 37 |
"acceptance": {
|
|
@@ -99,6 +99,7 @@
|
|
| 99 |
},
|
| 100 |
"inference_or_benchmark_rerun": false,
|
| 101 |
"core_change_scope": "Hand package only; other src files byte-identical to accepted pipeline bd4c312dac38068440edf59e764ba0c68ab7ce0b",
|
| 102 |
-
"pipeline_revision": "
|
|
|
|
| 103 |
}
|
| 104 |
}
|
|
|
|
| 31 |
"flora_batcher.py": "38e03e2032af8d5f3ed34980cc4d89927fe70c9742aa2ce13f4d0ccdd6fba727",
|
| 32 |
"flora_speed_entry.py": "192b18d75b9d876c2a65d88de340c8cea7c8da90543d0510b5ee6865d123d524",
|
| 33 |
"studio_runtime.py": "2b393bf7fafcdead638ad1cea35e49df8c6edce725f5545b76fd68e0a8fedd80",
|
| 34 |
+
"pipeline-version.json": "fa74c6924bcb98c81dd90cfd05ad78209b6ca89bbb0b92e1e451ca14549b6f23",
|
| 35 |
"flora_vision.py": "a5a4231abf7b615164c40986df5f87047f745a23c55d86385d88f2a09e70f1e8"
|
| 36 |
},
|
| 37 |
"acceptance": {
|
|
|
|
| 99 |
},
|
| 100 |
"inference_or_benchmark_rerun": false,
|
| 101 |
"core_change_scope": "Hand package only; other src files byte-identical to accepted pipeline bd4c312dac38068440edf59e764ba0c68ab7ce0b",
|
| 102 |
+
"pipeline_revision": "33c4ca366114b8717bb5df6ed4f412387af12dfe",
|
| 103 |
+
"public_asset_distribution": "Only the hand asset provisioning helper changed; all Flora and execution-engine source bytes remain identical to the accepted release."
|
| 104 |
}
|
| 105 |
}
|
hand-pipeline-catalog.json
CHANGED
|
@@ -44,5 +44,5 @@
|
|
| 44 |
"soma": "https://huggingface.co/nvidia/SOMA-X",
|
| 45 |
"runtime": "https://github.com/NVlabs/GEM-X/blob/20555befe2fbbe5b2015e1fff7ff214dc18ea9d6/ATTRIBUTIONS.md"
|
| 46 |
},
|
| 47 |
-
"lerobot_revision": "
|
| 48 |
}
|
|
|
|
| 44 |
"soma": "https://huggingface.co/nvidia/SOMA-X",
|
| 45 |
"runtime": "https://github.com/NVlabs/GEM-X/blob/20555befe2fbbe5b2015e1fff7ff214dc18ea9d6/ATTRIBUTIONS.md"
|
| 46 |
},
|
| 47 |
+
"lerobot_revision": "33c4ca366114b8717bb5df6ed4f412387af12dfe"
|
| 48 |
}
|
hand-worker-image.json
CHANGED
|
@@ -2,13 +2,15 @@
|
|
| 2 |
"recipe": "egohand-soma-v3-checkpoint-overlap-v4",
|
| 3 |
"build_verified": true,
|
| 4 |
"image_repo": "finerobotics/annotation-hands-worker",
|
| 5 |
-
"image_revision": "
|
| 6 |
"image": "hf.co/spaces/finerobotics/annotation-hands-worker",
|
| 7 |
-
"image_version_sha256": "
|
| 8 |
"assets": {
|
| 9 |
-
"repo": "finerobotics/
|
| 10 |
-
"
|
| 11 |
-
"
|
|
|
|
|
|
|
| 12 |
},
|
| 13 |
"backend": {
|
| 14 |
"root": "/opt/camera-adapter",
|
|
@@ -50,5 +52,13 @@
|
|
| 50 |
"calibration_mode": "estimate-moge",
|
| 51 |
"metric_accuracy_validated": false,
|
| 52 |
"compute_flavor": "h200"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
}
|
| 54 |
}
|
|
|
|
| 2 |
"recipe": "egohand-soma-v3-checkpoint-overlap-v4",
|
| 3 |
"build_verified": true,
|
| 4 |
"image_repo": "finerobotics/annotation-hands-worker",
|
| 5 |
+
"image_revision": "a42cb4f1194cf843c14ad3a8b4689b7303ffc36e",
|
| 6 |
"image": "hf.co/spaces/finerobotics/annotation-hands-worker",
|
| 7 |
+
"image_version_sha256": "f151a235a263afe5c3e3c5e7ab16ab7d15f16927edb9fb0a1666f81d9eb537d3",
|
| 8 |
"assets": {
|
| 9 |
+
"repo": "finerobotics/annotation-hands-worker",
|
| 10 |
+
"repo_type": "space",
|
| 11 |
+
"revision": "a42cb4f1194cf843c14ad3a8b4689b7303ffc36e",
|
| 12 |
+
"manifest_sha256": "f6fb26f2f1c2d26a4622a67c5e775701564e22fe869180df6c2803de00580c91",
|
| 13 |
+
"embedded": true
|
| 14 |
},
|
| 15 |
"backend": {
|
| 16 |
"root": "/opt/camera-adapter",
|
|
|
|
| 52 |
"calibration_mode": "estimate-moge",
|
| 53 |
"metric_accuracy_validated": false,
|
| 54 |
"compute_flavor": "h200"
|
| 55 |
+
},
|
| 56 |
+
"distribution_validation": {
|
| 57 |
+
"previous_gpu_accepted_image_revision": "775ee404b1c327b6f952561f761c5be27d455122",
|
| 58 |
+
"model_manifest_unchanged": true,
|
| 59 |
+
"native_runtime_unchanged": true,
|
| 60 |
+
"public_source_downloads": true,
|
| 61 |
+
"all_model_files_verified_at_build": true,
|
| 62 |
+
"new_gpu_inference_run": false
|
| 63 |
}
|
| 64 |
}
|
hand_actions.py
CHANGED
|
@@ -95,15 +95,11 @@ def catalog():
|
|
| 95 |
|
| 96 |
|
| 97 |
def prepare_hands(selection, camera, options, token):
|
| 98 |
-
"""Validate calibration, orientation
|
| 99 |
-
from huggingface_hub import HfApi
|
| 100 |
-
|
| 101 |
options = options or {}
|
| 102 |
compute = options.get("compute", "h200")
|
| 103 |
if compute not in COMPUTE:
|
| 104 |
raise ValueError("Choose a supported HF Jobs GPU")
|
| 105 |
-
if not token:
|
| 106 |
-
raise ValueError("Sign in with Hugging Face to use the hand runtime assets")
|
| 107 |
feature = selection["info"]["features"][camera]
|
| 108 |
names = feature.get("names") or ["height", "width", "channel"]
|
| 109 |
shape = dict(zip(names, feature["shape"], strict=True))
|
|
@@ -130,12 +126,6 @@ def prepare_hands(selection, camera, options, token):
|
|
| 130 |
"Choose a source dataset whose actions have not already been replaced by hand annotations"
|
| 131 |
)
|
| 132 |
profile = runtime_configuration()
|
| 133 |
-
try:
|
| 134 |
-
HfApi(token=token).model_info(profile["assets"]["repo"], revision=profile["assets"]["revision"])
|
| 135 |
-
except Exception as exc:
|
| 136 |
-
raise ValueError(
|
| 137 |
-
"Your Hugging Face login needs read access to the private EgoHand/SOMA runtime assets"
|
| 138 |
-
) from exc
|
| 139 |
return {
|
| 140 |
"compute": compute,
|
| 141 |
"calibration": calibration,
|
|
|
|
| 95 |
|
| 96 |
|
| 97 |
def prepare_hands(selection, camera, options, token):
|
| 98 |
+
"""Validate calibration, orientation and compute without a model-access login."""
|
|
|
|
|
|
|
| 99 |
options = options or {}
|
| 100 |
compute = options.get("compute", "h200")
|
| 101 |
if compute not in COMPUTE:
|
| 102 |
raise ValueError("Choose a supported HF Jobs GPU")
|
|
|
|
|
|
|
| 103 |
feature = selection["info"]["features"][camera]
|
| 104 |
names = feature.get("names") or ["height", "width", "channel"]
|
| 105 |
shape = dict(zip(names, feature["shape"], strict=True))
|
|
|
|
| 126 |
"Choose a source dataset whose actions have not already been replaced by hand annotations"
|
| 127 |
)
|
| 128 |
profile = runtime_configuration()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
return {
|
| 130 |
"compute": compute,
|
| 131 |
"calibration": calibration,
|
pipeline-source.tar.gz
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:41ba290372651fd9d16c9403ec36d8adc1c90313ae89ec9cd623319340ba9663
|
| 3 |
+
size 1869708
|
pipeline-version.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
{
|
| 2 |
-
"git_commit": "
|
| 3 |
-
"sha256": "
|
| 4 |
"preset": "abc_phases_schema_with_original_window_recovery",
|
| 5 |
"preset_date": "2026-09-25",
|
| 6 |
"engine": "lerobot.annotations.runtime",
|
|
|
|
| 1 |
{
|
| 2 |
+
"git_commit": "33c4ca366114b8717bb5df6ed4f412387af12dfe",
|
| 3 |
+
"sha256": "41ba290372651fd9d16c9403ec36d8adc1c90313ae89ec9cd623319340ba9663",
|
| 4 |
"preset": "abc_phases_schema_with_original_window_recovery",
|
| 5 |
"preset_date": "2026-09-25",
|
| 6 |
"engine": "lerobot.annotations.runtime",
|
test_hand_actions.py
CHANGED
|
@@ -217,13 +217,21 @@ def test_launch_uses_only_hf_gpu_image_and_private_dataset(monkeypatch):
|
|
| 217 |
assert api.run_job.call_count == 1
|
| 218 |
|
| 219 |
|
| 220 |
-
|
|
|
|
| 221 |
import hand_actions
|
| 222 |
|
| 223 |
camera = "observation.images.ego"
|
| 224 |
-
calibration =
|
| 225 |
-
model
|
| 226 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
selection = {
|
| 228 |
"episodes": [{"length": 90}],
|
| 229 |
"info": {
|
|
@@ -239,20 +247,20 @@ def test_hand_quote_requires_measured_metadata_and_access_before_dispatch(monkey
|
|
| 239 |
}
|
| 240 |
},
|
| 241 |
}
|
| 242 |
-
profile = {"assets": {"repo": "
|
| 243 |
monkeypatch.setattr(hand_actions, "runtime_configuration", lambda: profile)
|
| 244 |
api = Mock()
|
| 245 |
monkeypatch.setattr("huggingface_hub.HfApi", lambda **kwargs: api)
|
| 246 |
quote = hand_actions.prepare_hands(
|
| 247 |
-
selection, camera, {"compute": "h200", "orientation": "dataset"},
|
| 248 |
)
|
| 249 |
assert quote["calibration"] == calibration and quote["orientation"] == "aria-gen1-cw90"
|
| 250 |
assert quote["timing"]["seconds"] is None
|
| 251 |
-
api.model_info.
|
| 252 |
selection["info"]["features"][camera]["info"].pop("camera.intrinsics")
|
| 253 |
with pytest.raises(ValueError, match="measured"):
|
| 254 |
hand_actions.prepare_hands(selection, camera, {}, "user-token")
|
| 255 |
-
|
| 256 |
quote = hand_actions.prepare_hands(selection, camera, {"calibration_mode": "estimate-moge"}, "user-token")
|
| 257 |
assert quote["calibration"]["method"] == "moge-v2-fov-median-v1"
|
| 258 |
assert "fx" not in quote["calibration"]
|
|
|
|
| 217 |
assert api.run_job.call_count == 1
|
| 218 |
|
| 219 |
|
| 220 |
+
@pytest.mark.parametrize("token", [None, "user-token"])
|
| 221 |
+
def test_hand_quote_validates_metadata_without_model_access_login(monkeypatch, token):
|
| 222 |
import hand_actions
|
| 223 |
|
| 224 |
camera = "observation.images.ego"
|
| 225 |
+
calibration = {
|
| 226 |
+
"model": "pinhole",
|
| 227 |
+
"width": 64,
|
| 228 |
+
"height": 48,
|
| 229 |
+
"fx": 50.0,
|
| 230 |
+
"fy": 51.0,
|
| 231 |
+
"cx": 32.0,
|
| 232 |
+
"cy": 24.0,
|
| 233 |
+
"distortion": [],
|
| 234 |
+
}
|
| 235 |
selection = {
|
| 236 |
"episodes": [{"length": 90}],
|
| 237 |
"info": {
|
|
|
|
| 247 |
}
|
| 248 |
},
|
| 249 |
}
|
| 250 |
+
profile = {"assets": {"repo": "public/hand-runtime", "repo_type": "space", "revision": "a" * 40}}
|
| 251 |
monkeypatch.setattr(hand_actions, "runtime_configuration", lambda: profile)
|
| 252 |
api = Mock()
|
| 253 |
monkeypatch.setattr("huggingface_hub.HfApi", lambda **kwargs: api)
|
| 254 |
quote = hand_actions.prepare_hands(
|
| 255 |
+
selection, camera, {"compute": "h200", "orientation": "dataset"}, token
|
| 256 |
)
|
| 257 |
assert quote["calibration"] == calibration and quote["orientation"] == "aria-gen1-cw90"
|
| 258 |
assert quote["timing"]["seconds"] is None
|
| 259 |
+
api.model_info.assert_not_called()
|
| 260 |
selection["info"]["features"][camera]["info"].pop("camera.intrinsics")
|
| 261 |
with pytest.raises(ValueError, match="measured"):
|
| 262 |
hand_actions.prepare_hands(selection, camera, {}, "user-token")
|
| 263 |
+
api.model_info.assert_not_called()
|
| 264 |
quote = hand_actions.prepare_hands(selection, camera, {"calibration_mode": "estimate-moge"}, "user-token")
|
| 265 |
assert quote["calibration"]["method"] == "moge-v2-fov-median-v1"
|
| 266 |
assert "fx" not in quote["calibration"]
|