Skip to content

Commit 04c860e

Browse files
authored
Merge pull request #12 from KraHsu/feat/cli-ux-overhaul
Feat/cli ux overhaul
2 parents 2be6d6d + 958247d commit 04c860e

8 files changed

Lines changed: 436 additions & 36 deletions

File tree

‎examples/inverted_pendulum/src/genelab_inverted_pendulum/tasks.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,23 @@ def register() -> None:
134134
InvertedPendulumTask,
135135
description="PPO inverted-pendulum balancing (single pole).",
136136
cfg_type=TaskCfg,
137+
examples=[
138+
f"genelab play {INVERTED_PENDULUM_TASK_ID} --vis",
139+
f"genelab play {INVERTED_PENDULUM_TASK_ID} --agent trained "
140+
"--checkpoint PATH/model.pt",
141+
f"genelab train {INVERTED_PENDULUM_TASK_ID} --num_envs 4096 --max_iterations 100",
142+
],
137143
)
138144
if DOUBLE_PENDULUM_TASK_ID not in TASKS:
139145
register_task(
140146
DOUBLE_PENDULUM_TASK_ID,
141147
DoubleInvertedPendulumTask,
142148
description="PPO double-inverted-pendulum balancing (two stacked poles).",
143149
cfg_type=TaskCfg,
150+
examples=[
151+
f"genelab play {DOUBLE_PENDULUM_TASK_ID} --vis",
152+
f"genelab play {DOUBLE_PENDULUM_TASK_ID} --agent trained "
153+
"--checkpoint PATH/model.pt",
154+
f"genelab train {DOUBLE_PENDULUM_TASK_ID} --num_envs 4096 --max_iterations 200",
155+
],
144156
)

‎examples/unitree/src/genelab_unitree/tasks.py‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,28 @@ def register() -> None:
125125
G1VelocityTask,
126126
description="PPO velocity tracking for Unitree G1 (flat).",
127127
cfg_type=TaskCfg,
128+
examples=[
129+
f"genelab play {VELOCITY_TASK_ID}",
130+
f"genelab play {VELOCITY_TASK_ID} --agent random",
131+
f"genelab play {VELOCITY_TASK_ID} --agent trained --checkpoint PATH/model.pt",
132+
f"genelab train {VELOCITY_TASK_ID} --num_envs 4096",
133+
f"genelab train {VELOCITY_TASK_ID} --num_envs 4096 --gpus 2",
134+
],
128135
)
129136
if TRACKING_TASK_ID not in TASKS:
130137
register_task(
131138
TRACKING_TASK_ID,
132139
G1TrackingTask,
133-
description="PPO motion imitation for Unitree G1 (flat); requires --env.commands.motion.motion_file.",
140+
description=(
141+
"PPO motion imitation for Unitree G1 on flat ground. "
142+
"Requires a motion clip; see examples."
143+
),
134144
cfg_type=TaskCfg,
145+
examples=[
146+
f"genelab play {TRACKING_TASK_ID} --env.commands.motion.motion_file PATH/clip.npy",
147+
f"genelab train {TRACKING_TASK_ID} "
148+
"--env.commands.motion.motion_file PATH/clip.npy --num_envs 4096",
149+
f"genelab train {TRACKING_TASK_ID} "
150+
"--env.commands.motion.motion_file PATH/clip.npy --num_envs 4096 --gpus 2",
151+
],
135152
)

‎src/genelab/cli/__init__.py‎

Lines changed: 80 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
from genelab.cli._interactive import pick_task_interactively
2121
from genelab.cli._render import (
2222
render_cache,
23-
render_hint,
23+
render_entry_info,
24+
render_main_help,
2425
render_registry,
2526
)
2627
from genelab.cli._scaffold import create_project_skeleton
@@ -67,23 +68,56 @@ class _RegistryKindArg(str, Enum):
6768
)
6869

6970

71+
_RUN_FLAGS_HELP: Final[str] = """\
72+
Shorthand flags rewritten into env overrides:
73+
74+
\b
75+
-v, --vis Enable the Genesis viewer (env.scene.vis=true).
76+
--gpu Use the GPU backend (env.scene.gpu=true).
77+
--steps N Run for N steps (env.scene.steps=N).
78+
--dt SECONDS Override the sim timestep (env.scene.dt=SECONDS).
79+
--a.b.c VALUE Set any dotted cfg path.
80+
81+
Runner flags (used when an RL runner is engaged):
82+
83+
\b
84+
--num_envs N Parallel environments.
85+
--agent KIND one of: zero, random, trained (play only).
86+
--checkpoint PATH Resume from a checkpoint.
87+
--seed N RNG seed.
88+
--log_dir PATH Override the log directory.
89+
--max_iterations N Cap training iterations (train only).
90+
--gpus N Distributed training across N GPUs (train only).
91+
92+
Use `genelab info TASK` to see the full overridable path list for a task.
93+
"""
94+
95+
96+
_PLAY_HELP: Final[str] = "Run a registered task.\n\n" + _RUN_FLAGS_HELP
97+
_TRAIN_HELP: Final[str] = "Train a registered task when a runner exists.\n\n" + _RUN_FLAGS_HELP
98+
99+
70100
app = typer.Typer(
71101
name="genelab",
72-
help="Run registered GeneLab tasks.",
102+
help=(
103+
"GeneLab — Genesis robot lab CLI.\n\n"
104+
"Run `genelab` with no arguments for a landing page with quickstart commands "
105+
"and a count of registered robots, envs, and tasks."
106+
),
73107
no_args_is_help=False,
74108
add_completion=False,
75109
pretty_exceptions_enable=False,
76-
rich_markup_mode=None,
110+
rich_markup_mode="rich",
77111
)
78112

79113
project_app = typer.Typer(
80114
name="project",
81-
help="Create and manage GeneLab projects.",
115+
help="Create and manage GeneLab extension projects.",
82116
no_args_is_help=True,
83117
pretty_exceptions_enable=False,
84-
rich_markup_mode=None,
118+
rich_markup_mode="rich",
85119
)
86-
app.add_typer(project_app, name="project")
120+
app.add_typer(project_app, name="project", rich_help_panel="Project")
87121

88122

89123
def _version_callback(value: bool) -> None:
@@ -109,33 +143,43 @@ def root_callback(
109143
typer.Option(
110144
"--import",
111145
metavar="MODULE",
112-
help="Import a downstream extension module before dispatch (can be repeated).",
146+
help="Import an extension module before dispatch (repeatable).",
113147
),
114148
] = None,
115149
no_entry_points: Annotated[
116150
bool,
117151
typer.Option(
118152
"--no-entry-points",
119-
help="Skip installed extensions from the genelab.extensions entry point group.",
153+
help="Skip installed entry points from the genelab.extensions group.",
120154
),
121155
] = False,
122156
) -> None:
123157
_ = version # consumed by the eager callback
124-
ctx.obj = _RootState(
158+
state = _RootState(
125159
extension_modules=list(extension_modules or []),
126160
no_entry_points=no_entry_points,
127161
)
162+
ctx.obj = state
128163
if ctx.invoked_subcommand is None:
129-
render_hint()
164+
_load_extensions(state)
165+
render_main_help()
130166

131167

132-
@app.command("cache", help="Create project-local simulation cache directories.")
168+
@app.command(
169+
"cache",
170+
help="Create project-local simulation cache directories.",
171+
rich_help_panel="Utilities",
172+
)
133173
def cache_cmd() -> None:
134174
ensure_project_cache()
135175
render_cache(CACHE_DIR)
136176

137177

138-
@app.command("list", help="List registered robots, environments, or tasks.")
178+
@app.command(
179+
"list",
180+
help="List registered robots, envs, or tasks.",
181+
rich_help_panel="Registry",
182+
)
139183
def list_cmd(
140184
ctx: typer.Context,
141185
kind: Annotated[
@@ -147,9 +191,29 @@ def list_cmd(
147191
render_registry(kind.value)
148192

149193

194+
@app.command(
195+
"info",
196+
help="Show detail for one registered task, env, or robot.",
197+
rich_help_panel="Registry",
198+
)
199+
def info_cmd(
200+
ctx: typer.Context,
201+
name: Annotated[
202+
str,
203+
typer.Argument(
204+
metavar="NAME",
205+
help="Registered task, env, or robot name.",
206+
),
207+
],
208+
) -> None:
209+
_load_extensions(_state(ctx))
210+
render_entry_info(name)
211+
212+
150213
@app.command(
151214
"play",
152-
help="Run a registered task.",
215+
help=_PLAY_HELP,
216+
rich_help_panel="Runtime",
153217
context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
154218
)
155219
def play_cmd(ctx: typer.Context) -> None:
@@ -163,7 +227,8 @@ def play_cmd(ctx: typer.Context) -> None:
163227

164228
@app.command(
165229
"train",
166-
help="Train a registered task when a runner exists.",
230+
help=_TRAIN_HELP,
231+
rich_help_panel="Runtime",
167232
context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
168233
)
169234
def train_cmd(ctx: typer.Context) -> None:
@@ -185,7 +250,7 @@ def project_new_cmd(
185250
typer.Option(
186251
"--path",
187252
"-p",
188-
help="Parent directory where the project directory is created.",
253+
help="Parent directory under which the project directory is created.",
189254
),
190255
] = Path("."),
191256
package: Annotated[

0 commit comments

Comments
 (0)