simrig 0.2.2__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. {simrig-0.2.2 → simrig-0.3.0}/PKG-INFO +36 -6
  2. simrig-0.2.2/simrig.egg-info/PKG-INFO → simrig-0.3.0/README.md +28 -30
  3. {simrig-0.2.2 → simrig-0.3.0}/pyproject.toml +7 -5
  4. {simrig-0.2.2 → simrig-0.3.0}/simrig/_version.py +1 -1
  5. {simrig-0.2.2 → simrig-0.3.0}/simrig/cli.py +231 -0
  6. simrig-0.3.0/simrig/lambda_cloud.py +434 -0
  7. {simrig-0.2.2 → simrig-0.3.0}/simrig/playground_backend.py +21 -1
  8. {simrig-0.2.2 → simrig-0.3.0}/simrig/preview.py +8 -0
  9. simrig-0.3.0/simrig/runtime.py +134 -0
  10. simrig-0.2.2/README.md → simrig-0.3.0/simrig.egg-info/PKG-INFO +60 -0
  11. {simrig-0.2.2 → simrig-0.3.0}/simrig.egg-info/SOURCES.txt +4 -0
  12. simrig-0.3.0/simrig.egg-info/requires.txt +21 -0
  13. {simrig-0.2.2 → simrig-0.3.0}/tests/test_cli.py +58 -0
  14. simrig-0.3.0/tests/test_lambda_cloud.py +241 -0
  15. simrig-0.3.0/tests/test_runtime.py +78 -0
  16. simrig-0.2.2/simrig.egg-info/requires.txt +0 -19
  17. {simrig-0.2.2 → simrig-0.3.0}/LICENSE +0 -0
  18. {simrig-0.2.2 → simrig-0.3.0}/setup.cfg +0 -0
  19. {simrig-0.2.2 → simrig-0.3.0}/simrig/__init__.py +0 -0
  20. {simrig-0.2.2 → simrig-0.3.0}/simrig/browser_render.py +0 -0
  21. {simrig-0.2.2 → simrig-0.3.0}/simrig/browser_shell.py +0 -0
  22. {simrig-0.2.2 → simrig-0.3.0}/simrig/core.py +0 -0
  23. {simrig-0.2.2 → simrig-0.3.0}/simrig/custom_env.py +0 -0
  24. {simrig-0.2.2 → simrig-0.3.0}/simrig/huggingface.py +0 -0
  25. {simrig-0.2.2 → simrig-0.3.0}/simrig/io.py +0 -0
  26. {simrig-0.2.2 → simrig-0.3.0}/simrig/live_view.py +0 -0
  27. {simrig-0.2.2 → simrig-0.3.0}/simrig/model_view.py +0 -0
  28. {simrig-0.2.2 → simrig-0.3.0}/simrig/mujoco_backend.py +0 -0
  29. {simrig-0.2.2 → simrig-0.3.0}/simrig/paths.py +0 -0
  30. {simrig-0.2.2 → simrig-0.3.0}/simrig/presets.py +0 -0
  31. {simrig-0.2.2 → simrig-0.3.0}/simrig/rendering.py +0 -0
  32. {simrig-0.2.2 → simrig-0.3.0}/simrig/scaffold.py +0 -0
  33. {simrig-0.2.2 → simrig-0.3.0}/simrig/three_scene.py +0 -0
  34. {simrig-0.2.2 → simrig-0.3.0}/simrig/validate_env.py +0 -0
  35. {simrig-0.2.2 → simrig-0.3.0}/simrig.egg-info/dependency_links.txt +0 -0
  36. {simrig-0.2.2 → simrig-0.3.0}/simrig.egg-info/entry_points.txt +0 -0
  37. {simrig-0.2.2 → simrig-0.3.0}/simrig.egg-info/top_level.txt +0 -0
  38. {simrig-0.2.2 → simrig-0.3.0}/tests/test_core.py +0 -0
  39. {simrig-0.2.2 → simrig-0.3.0}/tests/test_custom_env.py +0 -0
  40. {simrig-0.2.2 → simrig-0.3.0}/tests/test_demo_reach.py +0 -0
  41. {simrig-0.2.2 → simrig-0.3.0}/tests/test_huggingface.py +0 -0
  42. {simrig-0.2.2 → simrig-0.3.0}/tests/test_jax_compat.py +0 -0
  43. {simrig-0.2.2 → simrig-0.3.0}/tests/test_live_view.py +0 -0
  44. {simrig-0.2.2 → simrig-0.3.0}/tests/test_model_view.py +0 -0
  45. {simrig-0.2.2 → simrig-0.3.0}/tests/test_mujoco_integration.py +0 -0
  46. {simrig-0.2.2 → simrig-0.3.0}/tests/test_paths.py +0 -0
  47. {simrig-0.2.2 → simrig-0.3.0}/tests/test_playground_backend.py +0 -0
  48. {simrig-0.2.2 → simrig-0.3.0}/tests/test_presets.py +0 -0
  49. {simrig-0.2.2 → simrig-0.3.0}/tests/test_rendering.py +0 -0
  50. {simrig-0.2.2 → simrig-0.3.0}/tests/test_scaffold.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simrig
3
- Version: 0.2.2
3
+ Version: 0.3.0
4
4
  Summary: Agent- and human-friendly workflows for MuJoCo Playground training and evaluation.
5
5
  Author: SimRig contributors
6
6
  License-Expression: MIT
@@ -16,11 +16,13 @@ Requires-Dist: mujoco; extra == "mujoco"
16
16
  Provides-Extra: hf
17
17
  Requires-Dist: huggingface_hub; extra == "hf"
18
18
  Provides-Extra: playground
19
- Requires-Dist: gymnasium[mujoco]; extra == "playground"
20
- Requires-Dist: mujoco; extra == "playground"
21
- Requires-Dist: jax; extra == "playground"
22
- Requires-Dist: brax; extra == "playground"
23
- Requires-Dist: playground; extra == "playground"
19
+ Requires-Dist: gymnasium[mujoco]==1.3.0; extra == "playground"
20
+ Requires-Dist: mujoco==3.10.0; extra == "playground"
21
+ Requires-Dist: mujoco-mjx==3.10.0; extra == "playground"
22
+ Requires-Dist: jax==0.10.2; extra == "playground"
23
+ Requires-Dist: brax==0.14.2; extra == "playground"
24
+ Requires-Dist: playground==0.2.0; extra == "playground"
25
+ Requires-Dist: warp-lang==1.15.0; extra == "playground"
24
26
  Provides-Extra: dev
25
27
  Requires-Dist: build; extra == "dev"
26
28
  Requires-Dist: numpy; extra == "dev"
@@ -216,10 +218,38 @@ transforms update the browser scene. The camera follows the robot without
216
218
  streaming rendered image frames. Use `--render-mode mujoco` for the older local
217
219
  image-stream preview or `--render-mode topdown` for the schematic fallback.
218
220
 
221
+ ### Train on a Lambda Cloud GPU
222
+
223
+ After launching a Lambda On-Demand instance with an SSH key, SimRig can connect,
224
+ sync this checkout, verify JAX GPU visibility, train, monitor a detached run,
225
+ and download its artifacts:
226
+
227
+ ```bash
228
+ simrig cloud lambda connect INSTANCE_IP --identity ~/Downloads/lambda-key.pem
229
+ simrig cloud lambda prepare INSTANCE_IP --identity ~/Downloads/lambda-key.pem
230
+ simrig cloud lambda smoke INSTANCE_IP Go1JoystickFlatTerrain \
231
+ --identity ~/Downloads/lambda-key.pem
232
+ simrig cloud lambda train INSTANCE_IP Go1JoystickFlatTerrain \
233
+ --identity ~/Downloads/lambda-key.pem \
234
+ --preset smoke
235
+ ```
236
+
237
+ Only after the environment and PPO smoke gates pass, start a detached large
238
+ run with `--preset cloud --detach`. SimRig operates on an instance you already
239
+ provisioned; it never launches or terminates billable Lambda resources. See the
240
+ complete [Lambda Cloud GPU guide](docs/lambda-cloud.md), including persistent
241
+ storage, monitoring, artifact download, and shutdown reminders.
242
+
243
+ Lambda preparation requires Python 3.11+ and installs a pinned Playground
244
+ training stack. Every run records its Python and package versions; checkpoint
245
+ eval, demo, and preview reject a different recorded runtime unless
246
+ `--allow-runtime-mismatch` is explicitly selected for qualitative review.
247
+
219
248
  ## Documentation
220
249
 
221
250
  - [Examples](examples/README.md)
222
251
  - [Agent workflow](docs/agent_workflow.md)
252
+ - [Lambda Cloud GPU training](docs/lambda-cloud.md)
223
253
  - [Agent skill installation](docs/skill-installation.md)
224
254
  - [Contributing](CONTRIBUTING.md)
225
255
  - [SimRig skill source](skills/simrig/SKILL.md)
@@ -1,33 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: simrig
3
- Version: 0.2.2
4
- Summary: Agent- and human-friendly workflows for MuJoCo Playground training and evaluation.
5
- Author: SimRig contributors
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/Su1eym4n/simrig
8
- Project-URL: Repository, https://github.com/Su1eym4n/simrig
9
- Project-URL: Issues, https://github.com/Su1eym4n/simrig/issues
10
- Project-URL: Changelog, https://github.com/Su1eym4n/simrig/blob/main/CHANGELOG.md
11
- Requires-Python: >=3.10
12
- Description-Content-Type: text/markdown
13
- License-File: LICENSE
14
- Provides-Extra: mujoco
15
- Requires-Dist: mujoco; extra == "mujoco"
16
- Provides-Extra: hf
17
- Requires-Dist: huggingface_hub; extra == "hf"
18
- Provides-Extra: playground
19
- Requires-Dist: gymnasium[mujoco]; extra == "playground"
20
- Requires-Dist: mujoco; extra == "playground"
21
- Requires-Dist: jax; extra == "playground"
22
- Requires-Dist: brax; extra == "playground"
23
- Requires-Dist: playground; extra == "playground"
24
- Provides-Extra: dev
25
- Requires-Dist: build; extra == "dev"
26
- Requires-Dist: numpy; extra == "dev"
27
- Requires-Dist: pytest; extra == "dev"
28
- Requires-Dist: twine; extra == "dev"
29
- Dynamic: license-file
30
-
31
1
  # SimRig
32
2
 
33
3
  Turn MuJoCo robots into trained policies with agent-guided task design, PPO
@@ -216,10 +186,38 @@ transforms update the browser scene. The camera follows the robot without
216
186
  streaming rendered image frames. Use `--render-mode mujoco` for the older local
217
187
  image-stream preview or `--render-mode topdown` for the schematic fallback.
218
188
 
189
+ ### Train on a Lambda Cloud GPU
190
+
191
+ After launching a Lambda On-Demand instance with an SSH key, SimRig can connect,
192
+ sync this checkout, verify JAX GPU visibility, train, monitor a detached run,
193
+ and download its artifacts:
194
+
195
+ ```bash
196
+ simrig cloud lambda connect INSTANCE_IP --identity ~/Downloads/lambda-key.pem
197
+ simrig cloud lambda prepare INSTANCE_IP --identity ~/Downloads/lambda-key.pem
198
+ simrig cloud lambda smoke INSTANCE_IP Go1JoystickFlatTerrain \
199
+ --identity ~/Downloads/lambda-key.pem
200
+ simrig cloud lambda train INSTANCE_IP Go1JoystickFlatTerrain \
201
+ --identity ~/Downloads/lambda-key.pem \
202
+ --preset smoke
203
+ ```
204
+
205
+ Only after the environment and PPO smoke gates pass, start a detached large
206
+ run with `--preset cloud --detach`. SimRig operates on an instance you already
207
+ provisioned; it never launches or terminates billable Lambda resources. See the
208
+ complete [Lambda Cloud GPU guide](docs/lambda-cloud.md), including persistent
209
+ storage, monitoring, artifact download, and shutdown reminders.
210
+
211
+ Lambda preparation requires Python 3.11+ and installs a pinned Playground
212
+ training stack. Every run records its Python and package versions; checkpoint
213
+ eval, demo, and preview reject a different recorded runtime unless
214
+ `--allow-runtime-mismatch` is explicitly selected for qualitative review.
215
+
219
216
  ## Documentation
220
217
 
221
218
  - [Examples](examples/README.md)
222
219
  - [Agent workflow](docs/agent_workflow.md)
220
+ - [Lambda Cloud GPU training](docs/lambda-cloud.md)
223
221
  - [Agent skill installation](docs/skill-installation.md)
224
222
  - [Contributing](CONTRIBUTING.md)
225
223
  - [SimRig skill source](skills/simrig/SKILL.md)
@@ -23,11 +23,13 @@ Changelog = "https://github.com/Su1eym4n/simrig/blob/main/CHANGELOG.md"
23
23
  mujoco = ["mujoco"]
24
24
  hf = ["huggingface_hub"]
25
25
  playground = [
26
- "gymnasium[mujoco]",
27
- "mujoco",
28
- "jax",
29
- "brax",
30
- "playground",
26
+ "gymnasium[mujoco]==1.3.0",
27
+ "mujoco==3.10.0",
28
+ "mujoco-mjx==3.10.0",
29
+ "jax==0.10.2",
30
+ "brax==0.14.2",
31
+ "playground==0.2.0",
32
+ "warp-lang==1.15.0",
31
33
  ]
32
34
  dev = ["build", "numpy", "pytest", "twine"]
33
35
 
@@ -1,3 +1,3 @@
1
1
  """Single source of truth for the SimRig package version."""
2
2
 
3
- __version__ = "0.2.2"
3
+ __version__ = "0.3.0"
@@ -12,6 +12,16 @@ from simrig._version import __version__
12
12
  from simrig.core import report_markdown, to_dict
13
13
  from simrig.huggingface import resolve_policy_checkpoint
14
14
  from simrig.io import save_json, save_report_pair, slugify
15
+ from simrig.lambda_cloud import (
16
+ LambdaSSHConfig,
17
+ check_lambda,
18
+ connect_lambda,
19
+ fetch_lambda,
20
+ prepare_lambda,
21
+ smoke_lambda,
22
+ status_lambda,
23
+ train_lambda,
24
+ )
15
25
  from simrig.mujoco_backend import inspect_model, list_models
16
26
  from simrig.paths import ensure_project_dirs
17
27
  from simrig.playground_backend import (
@@ -128,6 +138,11 @@ def build_parser() -> argparse.ArgumentParser:
128
138
  eval_parser.add_argument("--small-network", action=argparse.BooleanOptionalAction, default=None)
129
139
  eval_parser.add_argument("--hf-revision", help="Revision for hf:// policy checkpoints.")
130
140
  eval_parser.add_argument("--hf-token", help="Hugging Face token for private policy repos.")
141
+ eval_parser.add_argument(
142
+ "--allow-runtime-mismatch",
143
+ action="store_true",
144
+ help="Allow an explicitly qualitative rollout when recorded runtime versions differ.",
145
+ )
131
146
  eval_parser.add_argument("--json", action="store_true")
132
147
  eval_parser.set_defaults(func=_cmd_eval)
133
148
 
@@ -142,6 +157,11 @@ def build_parser() -> argparse.ArgumentParser:
142
157
  demo_parser.add_argument("--command", type=float, nargs="+")
143
158
  demo_parser.add_argument("--speed", type=float, default=1.0)
144
159
  demo_parser.add_argument("--camera-distance", type=float)
160
+ demo_parser.add_argument(
161
+ "--allow-runtime-mismatch",
162
+ action="store_true",
163
+ help="Allow an explicitly qualitative demo when recorded runtime versions differ.",
164
+ )
145
165
  demo_parser.add_argument("--json", action="store_true")
146
166
  demo_parser.set_defaults(func=_cmd_demo)
147
167
 
@@ -161,6 +181,11 @@ def build_parser() -> argparse.ArgumentParser:
161
181
  preview_parser.add_argument("--command", type=float, nargs="+")
162
182
  preview_parser.add_argument("--camera")
163
183
  preview_parser.add_argument("--paused", action="store_true", help="Start the browser preview paused.")
184
+ preview_parser.add_argument(
185
+ "--allow-runtime-mismatch",
186
+ action="store_true",
187
+ help="Allow an explicitly qualitative preview when recorded runtime versions differ.",
188
+ )
164
189
  preview_parser.add_argument(
165
190
  "--render-mode",
166
191
  choices=("threejs", "mujoco", "topdown"),
@@ -215,6 +240,119 @@ def build_parser() -> argparse.ArgumentParser:
215
240
  validate_env_parser.add_argument("--json", action="store_true")
216
241
  validate_env_parser.set_defaults(func=_cmd_validate_env)
217
242
 
243
+ cloud_parser = sub.add_parser(
244
+ "cloud",
245
+ help="Connect to and train on an already-provisioned cloud GPU.",
246
+ )
247
+ cloud_providers = cloud_parser.add_subparsers(dest="cloud_provider", required=True)
248
+ lambda_parser = cloud_providers.add_parser(
249
+ "lambda",
250
+ help="Use a Lambda On-Demand Cloud instance over SSH.",
251
+ )
252
+ lambda_actions = lambda_parser.add_subparsers(dest="cloud_action", required=True)
253
+
254
+ lambda_connect = lambda_actions.add_parser(
255
+ "connect",
256
+ help="Open an interactive SSH connection to the instance.",
257
+ )
258
+ _add_lambda_connection_args(lambda_connect)
259
+ lambda_connect.add_argument(
260
+ "--tunnel-port",
261
+ type=int,
262
+ help="Forward this localhost port to the same port on the instance.",
263
+ )
264
+ lambda_connect.set_defaults(func=_cmd_lambda_connect)
265
+
266
+ lambda_check = lambda_actions.add_parser(
267
+ "check",
268
+ help="Check SSH plus NVIDIA and JAX device visibility.",
269
+ )
270
+ _add_lambda_connection_args(lambda_check)
271
+ lambda_check.set_defaults(func=_cmd_lambda_check)
272
+
273
+ lambda_prepare = lambda_actions.add_parser(
274
+ "prepare",
275
+ help="Sync this checkout and prepare a GPU-enabled remote virtualenv.",
276
+ )
277
+ _add_lambda_connection_args(lambda_prepare)
278
+ lambda_prepare.add_argument("--project", type=Path, default=Path("."))
279
+ lambda_prepare.add_argument("--remote-dir")
280
+ lambda_prepare.add_argument(
281
+ "--jax-cuda",
282
+ choices=("preinstalled", "cuda12", "cuda13"),
283
+ default="preinstalled",
284
+ help="Use Lambda's preinstalled JAX or install a pip CUDA wheel.",
285
+ )
286
+ lambda_prepare.add_argument(
287
+ "--python",
288
+ dest="python_command",
289
+ default="python3",
290
+ help="Remote Python 3.11+ executable used to create the training virtualenv.",
291
+ )
292
+ lambda_prepare.set_defaults(func=_cmd_lambda_prepare)
293
+
294
+ lambda_smoke = lambda_actions.add_parser(
295
+ "smoke",
296
+ help="Run the reset/step environment smoke gate on the remote GPU.",
297
+ )
298
+ _add_lambda_connection_args(lambda_smoke)
299
+ lambda_smoke.add_argument(
300
+ "env_name",
301
+ help="Playground env name or synced custom *.py env module path.",
302
+ )
303
+ lambda_smoke.add_argument("--remote-dir")
304
+ lambda_smoke.add_argument("--steps", type=int, default=10)
305
+ lambda_smoke.set_defaults(func=_cmd_lambda_smoke)
306
+
307
+ lambda_train = lambda_actions.add_parser(
308
+ "train",
309
+ help="Run training on a prepared Lambda instance (smoke preset by default).",
310
+ )
311
+ _add_lambda_connection_args(lambda_train)
312
+ lambda_train.add_argument(
313
+ "env_name",
314
+ help="Playground env name or synced custom *.py env module path.",
315
+ )
316
+ lambda_train.add_argument(
317
+ "--preset",
318
+ choices=("smoke", "local", "cloud"),
319
+ default="smoke",
320
+ )
321
+ lambda_train.add_argument("--remote-dir")
322
+ lambda_train.add_argument(
323
+ "--output",
324
+ help="Remote run directory, relative to --remote-dir unless absolute.",
325
+ )
326
+ lambda_train.add_argument(
327
+ "--detach",
328
+ action="store_true",
329
+ help="Keep training after SSH disconnects and write train.log/train.pid.",
330
+ )
331
+ lambda_train.add_argument("--timesteps", type=int)
332
+ lambda_train.add_argument("--num-envs", type=int)
333
+ lambda_train.add_argument("--batch-size", type=int)
334
+ lambda_train.set_defaults(func=_cmd_lambda_train)
335
+
336
+ lambda_status = lambda_actions.add_parser(
337
+ "status",
338
+ help="Show detached process state and recent training log lines.",
339
+ )
340
+ _add_lambda_connection_args(lambda_status)
341
+ lambda_status.add_argument("output", help="Remote output returned by cloud lambda train.")
342
+ lambda_status.add_argument("--remote-dir")
343
+ lambda_status.add_argument("--lines", type=int, default=30)
344
+ lambda_status.set_defaults(func=_cmd_lambda_status)
345
+
346
+ lambda_fetch = lambda_actions.add_parser(
347
+ "fetch",
348
+ help="Download a remote run directory into local runs/.",
349
+ )
350
+ _add_lambda_connection_args(lambda_fetch)
351
+ lambda_fetch.add_argument("output", help="Remote output returned by cloud lambda train.")
352
+ lambda_fetch.add_argument("--remote-dir")
353
+ lambda_fetch.add_argument("--local-output", type=Path)
354
+ lambda_fetch.set_defaults(func=_cmd_lambda_fetch)
355
+
218
356
  return parser
219
357
 
220
358
 
@@ -292,6 +430,7 @@ def _cmd_eval(args: argparse.Namespace) -> None:
292
430
  small_network=args.small_network,
293
431
  seed=args.seed,
294
432
  command=command,
433
+ allow_runtime_mismatch=args.allow_runtime_mismatch,
295
434
  )
296
435
  save_json(Path("reports") / f"{slugify(args.env_name)}_eval.json", result)
297
436
  _print(result, as_json=args.json)
@@ -313,6 +452,7 @@ def _cmd_demo(args: argparse.Namespace) -> None:
313
452
  command=command,
314
453
  speed=args.speed,
315
454
  camera_distance=args.camera_distance,
455
+ allow_runtime_mismatch=args.allow_runtime_mismatch,
316
456
  )
317
457
  _print(result, as_json=args.json)
318
458
 
@@ -357,6 +497,7 @@ def _cmd_preview(args: argparse.Namespace) -> None:
357
497
  render_mode=args.render_mode,
358
498
  paused=args.paused,
359
499
  fps=args.fps,
500
+ allow_runtime_mismatch=args.allow_runtime_mismatch,
360
501
  )
361
502
 
362
503
 
@@ -374,6 +515,96 @@ def _cmd_validate_env(args: argparse.Namespace) -> int:
374
515
  return 0 if result.passed else 1
375
516
 
376
517
 
518
+ def _add_lambda_connection_args(parser: argparse.ArgumentParser) -> None:
519
+ parser.add_argument("host", help="Public IP address or DNS name from Lambda Cloud.")
520
+ parser.add_argument(
521
+ "--identity",
522
+ "-i",
523
+ type=Path,
524
+ help="Path to the SSH private key selected when the instance was launched.",
525
+ )
526
+ parser.add_argument("--user", default="ubuntu")
527
+ parser.add_argument("--port", type=int, default=22)
528
+
529
+
530
+ def _lambda_config(args: argparse.Namespace) -> LambdaSSHConfig:
531
+ return LambdaSSHConfig(
532
+ host=args.host,
533
+ user=args.user,
534
+ identity=args.identity,
535
+ port=args.port,
536
+ )
537
+
538
+
539
+ def _cmd_lambda_connect(args: argparse.Namespace) -> int:
540
+ return connect_lambda(_lambda_config(args), tunnel_port=args.tunnel_port)
541
+
542
+
543
+ def _cmd_lambda_check(args: argparse.Namespace) -> int:
544
+ return check_lambda(_lambda_config(args))
545
+
546
+
547
+ def _cmd_lambda_prepare(args: argparse.Namespace) -> None:
548
+ prepare_lambda(
549
+ _lambda_config(args),
550
+ project_dir=args.project,
551
+ remote_dir=args.remote_dir,
552
+ jax_cuda=args.jax_cuda,
553
+ python_command=args.python_command,
554
+ )
555
+ print(f"prepared Lambda project: {args.remote_dir or f'/home/{args.user}/simrig'}")
556
+
557
+
558
+ def _cmd_lambda_train(args: argparse.Namespace) -> int:
559
+ result = train_lambda(
560
+ _lambda_config(args),
561
+ args.env_name,
562
+ preset_name=args.preset,
563
+ remote_dir=args.remote_dir,
564
+ output=args.output,
565
+ detach=args.detach,
566
+ timesteps=args.timesteps,
567
+ num_envs=args.num_envs,
568
+ batch_size=args.batch_size,
569
+ )
570
+ if result.returncode == 0:
571
+ mode = "detached run" if result.detached else "run"
572
+ print(f"Lambda {mode}: {result.output_dir}")
573
+ return result.returncode
574
+
575
+
576
+ def _cmd_lambda_smoke(args: argparse.Namespace) -> int:
577
+ return smoke_lambda(
578
+ _lambda_config(args),
579
+ args.env_name,
580
+ remote_dir=args.remote_dir,
581
+ steps=args.steps,
582
+ )
583
+
584
+
585
+ def _cmd_lambda_status(args: argparse.Namespace) -> int:
586
+ return status_lambda(
587
+ _lambda_config(args),
588
+ args.output,
589
+ remote_dir=args.remote_dir,
590
+ lines=args.lines,
591
+ )
592
+
593
+
594
+ def _cmd_lambda_fetch(args: argparse.Namespace) -> None:
595
+ destination = fetch_lambda(
596
+ _lambda_config(args),
597
+ args.output,
598
+ remote_dir=args.remote_dir,
599
+ local_output=args.local_output,
600
+ )
601
+ print(f"downloaded run: {destination}")
602
+ print(
603
+ "After verifying these artifacts or persistent storage, terminate the "
604
+ "Lambda instance in the cloud console to stop compute charges."
605
+ )
606
+
607
+
377
608
  def _training_overrides(args: argparse.Namespace) -> dict[str, Any]:
378
609
  overrides: dict[str, Any] = {}
379
610
  for cli_name, key in (