Spaces:
Running
Download README.md from armnet/armnet-eval: direct link, hf CLI and curl.
- Browser
- Download file 10 kB
-
https://huggingface.co/spaces/armnet/armnet-eval/resolve/main/README.md
- Command line
-
hf download hf://spaces/armnet/armnet-eval/README.md
-
curl -L -o README.md https://huggingface.co/spaces/armnet/armnet-eval/resolve/main/README.md
A newer version of the Gradio SDK is available: 6.31.0
title: armnet eval
emoji: π¦Ύ
colorFrom: indigo
colorTo: purple
sdk: gradio
sdk_version: 6.20.0
python_version: '3.12'
app_file: app.py
pinned: false
short_description: Evaluate a robot policy on real hardware, no setup.
armnet eval β evaluate a policy on real robots
A deliberately constrained public demo of the armnet infrastructure. No install, no auth setup, no Docker. Pick a task, paste (or select) a policy repo id, press Run, and watch 20 rollouts on a real SO-101 cell:
- Live video of the rollout, streamed from the cell.
- Cell availability light: a blinking dot β green when at least one BusyBox
cell is free, yellow when every cell is busy, red when none are online β with
a count such as
1 cell available. - Auto-scored per-rollout success from the instrumented BusyBox β no human judges, and the scene resets itself between rollouts.
- Shared: everyone who opens the Space follows the same fleet in real time. If more than one cell is running an eval, the Live tab names each policy and lets you pick which feed to watch.
- Queue-aware: while every cell is busy, submitters can queue another
20-rollout eval. A client-side abuse guard limits this Space to 10 waiting
evals; it does not cap the fleet-wide orchestrator queue. The Space only sees
jobs owned by its API user and renders unrelated ones as
other job. The Run button remains available while a Space eval is live, but the same policy repo cannot be running and/or queued more than once. Once its run finishes, it can be submitted again. - Leaderboard: every run is recorded and pooled by policy repo + revision, ranked by the 95% lower-bound success rate (so more rollouts only help a genuinely good policy β spamming evals can't game it).
Tasks
A selector above the tabs picks the task, and it scopes everything below it: the
dataset the instructions point at, the policy the Live tab defaults to, and the
board the Leaderboard shows. Both run on BusyBox cells assigned the busybox
environment β the orchestrator resolves the task slug to that environment when
it routes a job, and any free cell serving it can pick the work up.
| Task | Slug | Dataset | Reference policy |
|---|---|---|---|
| Push the green button | push_green_button |
armnet/busybox_push_green_button |
β¦_push_green_button_diffusion |
| Flip left switch off | busybox_left_toggle_down |
armnet/busybox_flip_left_switch_off |
β¦_flip_left_switch_off_diffusion |
Either task-specific dataset can be replaced with
armnet/busybox_multitask
for language-conditioned multi-task training.
Adding a third is one entry in TASKS in config.py. The only hard requirement
is that it resets itself β a task needing a human to tidy up between rollouts
can't be run unattended by the public.
Persistence
Gradio has no built-in database and a Space's disk is ephemeral, so the
leaderboard is stored in a Hugging Face dataset repo (ARMNET_DEMO_LEADERBOARD_REPO,
default armnet/armnet-demo-leaderboard): one JSONL line per completed
run, reloaded on restart. Reads are public; writing new runs needs HF_TOKEN
(the same token used to pull gated policies).
Each run records source, cell_id, embodiment and task alongside the
success counts. Each task gets its own board: rows are filtered to this
Space's embodiment (lerobot/so-101) and to the selected task exactly, since a
policy that presses buttons well says nothing about one that flips a switch.
Runs from every BusyBox cell in the demo fleet are pooled together. The board
shows a Source column: a verified checkmark ("result verified by Armnet")
when every pooled rollout for a policy came from this demo, otherwise
script/mixed. Armnet eval scripts can also write to the same dataset
(source="script") via ctx.submit_results_to_leaderboard(); that path
currently relies on the eval container's HF token having write access to the
dataset β a server-side submission endpoint is planned so external users can
submit without that token.
How it works
- The Space submits a job to the armnet orchestrator (baked-in demo API key)
naming only the embodiment (
lerobot/so-101) and the selected task's slug, passing the policy repo id as a job arg (use_rerun=Trueturns on the video feed). No cell is named in the submission β the task is what routes it. If every matching cell is occupied, the job waits in the shared queue and the Space adopts it for live display when the scheduler dispatches it. - The orchestrator resolves that task to its environment (
busybox) and routes the job to a cell assigned that environment. Cells are assigned an environment in the FMS, not a task, and serve every task within it β so if no cell is set tobusyboxthe availability light goes red and nothing can be submitted. - The chosen cell pulls the pre-built eval image, loads the policy from the Hub, and runs the rollouts. It streams observation frames (Rerun packets) and per-rollout results back over the orchestrator websockets. When a queued Space eval starts, the Space attaches a live follower to that job even if another cell is already running.
- The Space decodes those into per-cell live video + results, held in process-global slots so all viewers share the same feeds. Each browser receives the newest frame of the cell it is watching through one cancellable async Gradio stream, capped at 5 FPS; slow viewers skip stale frames instead of accumulating video latency. Frames are resized once process-wide rather than once per viewer.
Space configuration
Set these in Settings β Variables and secrets:
| Variable | Required | Purpose |
|---|---|---|
ARMNET_DEMO_EVAL_IMAGE |
no | Registry ref of the pre-built lerobot-eval runtime image (below). A current ref is baked into config.py; set this only to override it without editing code. A Space can't build Docker, so this is built + pushed once by an operator. |
ARMNET_API_KEY |
yes | Orchestrator API key for the demo (a low-privilege throwaway key). Provided as a secret β nothing is baked into the repo. |
HF_TOKEN |
recommended | The Space stores it once as a named orchestrator secret (ARMNET_DEMO_HF_SECRET_NAME, default hf_token) under the demo user and the eval references it as $HF_TOKEN to pull a gated policy; it also authenticates leaderboard writes. Without it the leaderboard is read-only and gated policies won't load. |
ARMNET_DEMO_N_EPISODES |
no | Rollouts per run (default 20). |
ARMNET_DEMO_MAX_QUEUED_JOBS |
no | Client-side cap on this Space's queued eval jobs (default 10); it does not limit the orchestrator queue. |
ARMNET_DEMO_LEADERBOARD_REPO |
no | HF dataset repo backing the leaderboard (default armnet/armnet-demo-leaderboard). |
ARMNET_DEMO_DATASET_NAMESPACE |
no | Owner of the recorded eval dataset (default armnet). Without this the eval uses whoever owns HF_TOKEN. |
ARMNET_DEMO_CELL_STATUS_POLL_S |
no | How often the shared availability lights refresh (default 5s). One process-wide poll per task, not per viewer. |
DEBUG_ARMNET_ORCHESTRATOR_URI |
no | Override the orchestrator URL (default: the one compiled into armnet-client). |
No keys are baked into the repo β both
ARMNET_API_KEYandHF_TOKENare read from the Space's secrets. The demo API key is a low-privilege throwaway key; rotate/invalidate it freely.
Deploying the Space itself
The same Armnet Demo Eval Image workflow uploads this directory (minus tests/)
to the Space on every push to main, so a fix to the app or its requirements goes
live without anyone pushing by hand. It compares the checkout against what the
Space actually holds and commits only the files that differ β a commit restarts
the Space, so an unchanged Space is left alone. Because that check is on content
rather than on which paths a commit touched, a deploy that failed is simply
retried by the next run.
To preview or do it by hand:
cd armnet
uv run python examples/ci_deploy_demo_space.py --dry-run # what would change
uv run python examples/ci_deploy_demo_space.py # deploy it
Files it finds on the Space but not in the checkout are reported, not deleted.
Building the eval image
The policy is not baked into the image β it is loaded at runtime from the repo id β so one image serves every policy, and building it needs no robot.
The Armnet Demo Eval Image workflow keeps this current: on every push to main
it works out the image ref from the runtime source, builds and pushes it if that
ref is not already in the registry, and sets ARMNET_DEMO_EVAL_IMAGE on the
Space. Because the tag is a hash of the build context, a commit that leaves the
eval runtime alone resolves to an image that already exists, so the build is
skipped and the Space is left running exactly what it was.
The workflow targets armnet/armnet-eval and needs the repository's HF_TOKEN
secret to have write access to that Space.
To do it by hand β to test a change before it lands on main, say:
cd armnet
# Builds runtime_lerobot_eval (which bakes in the molmoact2 plugin) and pushes it,
# or prints the existing ref if this source is already published.
uv run python examples/ci_publish_eval_image.py
Then set ARMNET_DEMO_EVAL_IMAGE on the Space to the ref it prints, or pass
--space <owner>/<space> to have it do that for you.
Local dev
cd armnet/demo_space
ARMNET_DEMO_EVAL_IMAGE=<ref> python app.py