Skip to content

Commit b8a43a0

Browse files
committed
ci(pages): expand GitHub Pages into a demo landing + live benchmark
tools/pages/build_site.py copies the README GIFs, generates the GPU-free adapter benchmark via the CLI, and writes a self-contained index.html landing (hero + GIF gallery + highlights + links). pages.yml now builds this site, so the public URL is a real showcase, not just a bench table.
1 parent a6f8db6 commit b8a43a0

2 files changed

Lines changed: 121 additions & 6 deletions

File tree

.github/workflows/pages.yml

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,8 @@ jobs:
2626
with:
2727
python-version: "3.12"
2828
- run: python -m pip install --upgrade pip numpy
29-
- name: Generate benchmark dashboard
30-
working-directory: world_model_py
31-
run: |
32-
mkdir -p ../_site
33-
python -m world_model_py.cli bench-compare \
34-
--adapters dummy,remote --runs 300 --out ../_site/index.html
29+
- name: Build demo landing + benchmark dashboard
30+
run: python tools/pages/build_site.py --repo-root . --out _site --runs 300
3531
- uses: actions/upload-pages-artifact@v3
3632
with:
3733
path: _site

tools/pages/build_site.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
"""Build the GitHub Pages site: a demo landing (GIFs) + the live benchmark.
2+
3+
python3 tools/pages/build_site.py --repo-root . --out _site
4+
5+
Copies docs/*.gif, generates the adapter benchmark dashboard via the CLI
6+
(GPU-free dummy vs remote-over-HTTP), and writes a self-contained index.html.
7+
Used by .github/workflows/pages.yml.
8+
"""
9+
import argparse
10+
import glob
11+
import html
12+
import os
13+
import shutil
14+
import subprocess
15+
import sys
16+
17+
REPO_URL = "https://github.com/rsasaki0109/worldmodels_ros2"
18+
19+
CAPTIONS = {
20+
"imagination.gif": "Imagined future occupancy + risk from one ROS 2 call (dummy).",
21+
"jepa_compare.gif": "Same camera stream, two real World Models: I-JEPA vs V-JEPA 2 surprise.",
22+
"nav2_scoring.gif": "Nav2 candidate paths ranked by model-based risk; safest highlighted.",
23+
"ijepa_surprise.gif": "Real I-JEPA surprise spiking on scene changes (GPU).",
24+
}
25+
HERO = "imagination.gif"
26+
ORDER = ["jepa_compare.gif", "nav2_scoring.gif", "ijepa_surprise.gif"]
27+
28+
29+
def _card(gif: str) -> str:
30+
cap = html.escape(CAPTIONS.get(gif, gif))
31+
return (f'<figure><img src="{gif}" alt="{cap}" loading="lazy">'
32+
f'<figcaption>{cap}</figcaption></figure>')
33+
34+
35+
def build_index(gifs: list) -> str:
36+
hero = _card(HERO) if HERO in gifs else ""
37+
gallery = "\n".join(_card(g) for g in ORDER if g in gifs)
38+
extras = "\n".join(_card(g) for g in sorted(gifs)
39+
if g != HERO and g not in ORDER)
40+
return f"""<!doctype html>
41+
<html lang="en"><head><meta charset="utf-8">
42+
<meta name="viewport" content="width=device-width, initial-scale=1">
43+
<title>world_model_ros2 — the ROS 2 layer for World Models</title>
44+
<style>
45+
:root{{color-scheme:dark}}
46+
body{{font-family:system-ui,sans-serif;margin:0;background:#15151a;color:#e9e9ee}}
47+
.wrap{{max-width:980px;margin:0 auto;padding:2rem 1.2rem 4rem}}
48+
h1{{font-size:1.8rem;margin:.2rem 0}}
49+
a{{color:#5ad1ff}}
50+
.tag{{color:#a6a6b3;font-size:1.05rem}}
51+
.btns{{margin:1.2rem 0 2rem}}
52+
.btn{{display:inline-block;background:#3b82f6;color:#fff;padding:.55rem 1rem;border-radius:8px;text-decoration:none;margin-right:.6rem;font-weight:600}}
53+
.btn.alt{{background:#2a2a33;color:#e9e9ee}}
54+
ul.hi{{line-height:1.7}}
55+
figure{{margin:0 0 1.6rem}}
56+
img{{width:100%;border:1px solid #2a2a33;border-radius:10px;display:block}}
57+
figcaption{{color:#a6a6b3;font-size:.92rem;margin-top:.4rem}}
58+
.grid{{display:grid;grid-template-columns:1fr 1fr;gap:1.4rem}}
59+
@media(max-width:720px){{.grid{{grid-template-columns:1fr}}}}
60+
footer{{color:#6f6f7a;margin-top:2rem;font-size:.9rem}}
61+
</style></head><body><div class="wrap">
62+
<h1>world_model_ros2</h1>
63+
<p class="tag">Run existing World Models from ROS 2 — runtime, adapters,
64+
benchmark, visualization. <em>The ROS 2 layer for World Models, not another
65+
foundation model.</em></p>
66+
<div class="btns">
67+
<a class="btn" href="{REPO_URL}">View on GitHub</a>
68+
<a class="btn alt" href="bench.html">Live benchmark &rarr;</a>
69+
</div>
70+
71+
<ul class="hi">
72+
<li><b>Two real model backends, one contract</b> — I-JEPA (image) &amp; V-JEPA&nbsp;2 (video).</li>
73+
<li><b>Local &harr; remote split</b> over a shared JSON wire (Cosmos/DreamZero-ready).</li>
74+
<li><b>Compiled Nav2 costmap layer</b> — predicted occupancy into the live costmap.</li>
75+
<li><b>rosbag2 &rarr; LeRobot</b> dataset export, GPU-free.</li>
76+
<li><b>RViz + Foxglove</b> imagination viewer.</li>
77+
</ul>
78+
79+
{hero}
80+
<div class="grid">
81+
{gallery}
82+
{extras}
83+
</div>
84+
85+
<footer>Auto-generated from the repo on every push · all GIFs are real
86+
pipeline / real model output · <a href="{REPO_URL}">source</a></footer>
87+
</div></body></html>"""
88+
89+
90+
def main():
91+
ap = argparse.ArgumentParser()
92+
ap.add_argument("--repo-root", default=".")
93+
ap.add_argument("--out", default="_site")
94+
ap.add_argument("--runs", type=int, default=300)
95+
args = ap.parse_args()
96+
97+
repo = os.path.abspath(args.repo_root)
98+
out = os.path.abspath(args.out)
99+
os.makedirs(out, exist_ok=True)
100+
101+
gifs = []
102+
for path in sorted(glob.glob(os.path.join(repo, "docs", "*.gif"))):
103+
shutil.copy(path, out)
104+
gifs.append(os.path.basename(path))
105+
106+
subprocess.run(
107+
[sys.executable, "-m", "world_model_py.cli", "bench-compare",
108+
"--adapters", "dummy,remote", "--runs", str(args.runs),
109+
"--out", os.path.join(out, "bench.html")],
110+
cwd=os.path.join(repo, "world_model_py"), check=True,
111+
)
112+
113+
with open(os.path.join(out, "index.html"), "w", encoding="utf-8") as fh:
114+
fh.write(build_index(gifs))
115+
print(f"built site at {out} ({len(gifs)} gifs + bench.html + index.html)")
116+
117+
118+
if __name__ == "__main__":
119+
main()

0 commit comments

Comments
 (0)