Skip to content

Narrow WorldForge to the latent planning-and-scoring backbone (#337) #177

Narrow WorldForge to the latent planning-and-scoring backbone (#337)

Narrow WorldForge to the latent planning-and-scoring backbone (#337) #177

name: Robotics Showcase
on:
push:
branches:
- main
pull_request:
permissions:
contents: read
concurrency:
group: robotics-showcase-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
UV_LOCKED: "true"
UV_VERSION: "0.9.18"
PYTHON_VERSION: "3.13"
LEROBOT_VERSION: "0.5.1"
LEWORLDMODEL_REVISION: "22b330c28c27ead4bfd1888615af1340e3fe9052"
LEROBOT_POLICY_PATH: "lerobot/diffusion_pusht"
LEROBOT_POLICY_TYPE: "diffusion"
LEROBOT_DEVICE: "cpu"
LEWORLDMODEL_POLICY: "pusht/lewm"
LEWORLDMODEL_DEVICE: "cpu"
PYGAME_HIDE_SUPPORT_PROMPT: "1"
jobs:
real-inference:
name: Real LeRobot + LeWorldModel inference
runs-on: ubuntu-latest
timeout-minutes: 90
env:
HF_HOME: ${{ github.workspace }}/.cache/huggingface
LEROBOT_CACHE_DIR: ${{ github.workspace }}/.cache/huggingface/lerobot
STABLEWM_HOME: ${{ github.workspace }}/.cache/stable-wm
LEWORLDMODEL_ASSET_CACHE_DIR: ${{ github.workspace }}/.cache/worldforge/leworldmodel
WORLDFORGE_ROBOTICS_RUN_DIR: ${{ github.workspace }}/.worldforge/robotics-showcase-ci
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Set up uv
uses: astral-sh/setup-uv@v4
with:
version: ${{ env.UV_VERSION }}
enable-cache: true
cache-dependency-glob: uv.lock
- name: Verify lockfile
run: uv lock --check
- name: Restore model and checkpoint caches
uses: actions/cache@v4
with:
path: |
.cache/huggingface
.cache/stable-wm
.cache/worldforge/leworldmodel
key: robotics-showcase-${{ runner.os }}-py${{ env.PYTHON_VERSION }}-uv${{ env.UV_VERSION }}-lerobot-${{ env.LEROBOT_VERSION }}-lewm-${{ env.LEWORLDMODEL_REVISION }}-${{ hashFiles('scripts/robotics-showcase', 'scripts/lewm-lerobot-real', 'src/worldforge/smoke/leworldmodel_checkpoint.py', 'src/worldforge/smoke/pusht_showcase_inputs.py') }}
restore-keys: |
robotics-showcase-${{ runner.os }}-py${{ env.PYTHON_VERSION }}-uv${{ env.UV_VERSION }}-lerobot-${{ env.LEROBOT_VERSION }}-lewm-${{ env.LEWORLDMODEL_REVISION }}-
- name: Run non-interactive real robotics showcase
run: |
mkdir -p "$WORLDFORGE_ROBOTICS_RUN_DIR"
scripts/robotics-showcase \
--policy-path "$LEROBOT_POLICY_PATH" \
--policy-type "$LEROBOT_POLICY_TYPE" \
--stablewm-home "$STABLEWM_HOME" \
--lewm-cache-dir "$STABLEWM_HOME" \
--lewm-asset-cache-dir "$LEWORLDMODEL_ASSET_CACHE_DIR" \
--lewm-revision "$LEWORLDMODEL_REVISION" \
--lerobot-cache-dir "$LEROBOT_CACHE_DIR" \
--device cpu \
--json-only \
--no-tui \
--no-rerun \
--json-output "$WORLDFORGE_ROBOTICS_RUN_DIR/real-run.json" \
--run-manifest "$WORLDFORGE_ROBOTICS_RUN_DIR/run_manifest.json" \
> "$WORLDFORGE_ROBOTICS_RUN_DIR/stdout.json"
- name: Validate robotics showcase evidence
run: |
python - <<'PY'
import json
import os
from pathlib import Path
run_dir = Path(os.environ["WORLDFORGE_ROBOTICS_RUN_DIR"])
summary_path = run_dir / "real-run.json"
manifest_path = run_dir / "run_manifest.json"
stdout_path = run_dir / "stdout.json"
summary = json.loads(summary_path.read_text(encoding="utf-8"))
stdout_payload = json.loads(stdout_path.read_text(encoding="utf-8"))
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
failures = []
def require(condition, message):
if not condition:
failures.append(message)
require(summary == stdout_payload, "stdout JSON and summary artifact differ")
require(
summary.get("mode") == "real_lerobot_policy_plus_real_leworldmodel_score",
"unexpected run mode",
)
health = summary.get("health", {})
require(health.get("lerobot", {}).get("healthy") is True, "LeRobot health failed")
require(
health.get("leworldmodel", {}).get("healthy") is True,
"LeWorldModel health failed",
)
inputs = summary.get("inputs", {})
require(inputs.get("score_action_candidates_shape") == [1, 3, 4, 10], "unexpected candidate tensor shape")
score_result = summary.get("score_result", {})
scores = score_result.get("scores")
require(isinstance(scores, list) and len(scores) == 3, "expected three LeWorldModel scores")
require(isinstance(score_result.get("best_index"), int), "missing best_index")
events = {
(event.get("provider"), event.get("operation"), event.get("phase"))
for event in summary.get("provider_events", [])
}
require(("lerobot", "policy", "success") in events, "missing successful LeRobot event")
require(
("leworldmodel", "score", "success") in events,
"missing successful LeWorldModel event",
)
require("rerun" not in summary, "non-interactive CI run should not write Rerun output")
require(manifest.get("status") == "passed", "run manifest did not pass")
require(manifest.get("capability") == "policy+score", "unexpected manifest capability")
if failures:
raise SystemExit("\n".join(failures))
summary_file = Path(os.environ["GITHUB_STEP_SUMMARY"])
with summary_file.open("a", encoding="utf-8") as handle:
handle.write("## Robotics Showcase Evidence\n\n")
handle.write(f"- LeWorldModel revision: `{os.environ['LEWORLDMODEL_REVISION']}`\n")
handle.write(f"- Policy: `{inputs.get('policy_path')}`\n")
handle.write(f"- Scores: `{scores}`\n")
handle.write(f"- Best index: `{score_result.get('best_index')}`\n")
handle.write(f"- Summary: `{summary_path.relative_to(Path.cwd())}`\n")
handle.write(f"- Manifest: `{manifest_path.relative_to(Path.cwd())}`\n")
PY
- name: Upload robotics showcase evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: robotics-showcase-real-inference-${{ github.run_id }}
path: |
.worldforge/robotics-showcase-ci/*.json
if-no-files-found: warn
retention-days: 14