simhub 0.2.0__tar.gz → 0.2.1__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.
- simhub-0.2.1/PKG-INFO +1196 -0
- simhub-0.2.1/README.sdk.md +1146 -0
- {simhub-0.2.0 → simhub-0.2.1}/pyproject.toml +23 -2
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/__init__.py +29 -12
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/base.py +57 -11
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/blender_replay.py +3 -0
- simhub-0.2.1/src/simhub/checkpoint.py +36 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/client.py +99 -7
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/contracts.py +41 -2
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/definition_runtime.py +17 -3
- simhub-0.2.1/src/simhub/deformables.py +169 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/env.py +26 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/errors.py +4 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/execution_plan.py +7 -2
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/hosted_run.py +7 -4
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/interfaces.py +3 -1
- simhub-0.2.1/src/simhub/logs.py +79 -0
- simhub-0.2.1/src/simhub/logs_cli.py +54 -0
- simhub-0.2.1/src/simhub/model_cli.py +126 -0
- simhub-0.2.1/src/simhub/model_sources.py +148 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/models.py +3 -0
- simhub-0.2.1/src/simhub/openpi_policy.py +212 -0
- simhub-0.2.1/src/simhub/openpi_transport.py +167 -0
- simhub-0.2.1/src/simhub/physics.py +92 -0
- simhub-0.2.1/src/simhub/physics_routing.py +28 -0
- simhub-0.2.1/src/simhub/preview_animation.py +174 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/project.py +19 -4
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/recording.py +32 -4
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/render_context.py +28 -3
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/render_diagnostics.py +25 -2
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/render_scene.py +8 -1
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/render_worker.py +40 -22
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/rollout.py +90 -11
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/run_cli.py +43 -3
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/runner.py +126 -49
- simhub-0.2.1/src/simhub/runner_environment.py +41 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/session.py +81 -5
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/single.py +4 -2
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/spec.py +3 -3
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/viewer.py +24 -21
- simhub-0.2.1/src/simhub/warp_checks.py +27 -0
- simhub-0.2.1/src/simhub/warp_physics.py +211 -0
- simhub-0.2.0/PKG-INFO +0 -646
- simhub-0.2.0/README.sdk.md +0 -606
- simhub-0.2.0/src/simhub/model_cli.py +0 -83
- {simhub-0.2.0 → simhub-0.2.1}/skills/README.md +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/skills/simhub-assets/SKILL.md +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/skills/simhub-blender-scenes/SKILL.md +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/skills/simhub-datasets/SKILL.md +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/skills/simhub-evaluations/SKILL.md +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/skills/simhub-hosted-batches/SKILL.md +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/skills/simhub-local-debugging/SKILL.md +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/skills/simhub-mujoco-scenes/SKILL.md +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/skills/simhub-policies/SKILL.md +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/skills/simhub-projects/SKILL.md +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/skills/simhub-projects/assets/counter.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/skills/simhub-recording/SKILL.md +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/skills/simhub-setup/SKILL.md +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/skills/simhub-tasks/SKILL.md +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/skills/simhub-typed-runs/SKILL.md +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/skills/simhub-typed-scenes/SKILL.md +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/skills/simhub-variations/SKILL.md +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/agent_workspace.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/asset_cli.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/asset_spec.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/asset_urdf.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/batch.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/blender.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/builders.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/checkout.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/checks.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/cleaning.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/codegen.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/datasets.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/emit.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/evaluation.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/experiments.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/frame_set.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/gymnasium_adapter.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/hf.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/ids.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/kinematics.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/mjcf.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/named_assets.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/native_physics.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/native_scene.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/native_visuals.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/notebook.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/objects.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/openfoam.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/particulate.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/policies.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/presentation.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/project_cli.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/py.typed +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/registry.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/rendering.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/replay.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/report.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/robots.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/run_images.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/runs_cli.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/scene_spec.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/schema.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/scorer.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/studio.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/suite.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/task.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/types.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/variation_effects.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/variation_specs.py +0 -0
- {simhub-0.2.0 → simhub-0.2.1}/src/simhub/variations.py +0 -0
simhub-0.2.1/PKG-INFO
ADDED
|
@@ -0,0 +1,1196 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: simhub
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Define an environment, a policy and a task in Python; run them on hosted GPUs
|
|
5
|
+
Project-URL: Homepage, https://github.com/General-Deployment/sim
|
|
6
|
+
License: Proprietary
|
|
7
|
+
Keywords: evaluation,mujoco,reinforcement-learning,robotics,simulation
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
|
+
Requires-Dist: httpx>=0.27
|
|
10
|
+
Requires-Dist: numpy>=1.24
|
|
11
|
+
Requires-Dist: pydantic>=2.7
|
|
12
|
+
Requires-Dist: tomli>=2.0; python_version < '3.11'
|
|
13
|
+
Requires-Dist: typing-extensions>=4.12
|
|
14
|
+
Provides-Extra: all
|
|
15
|
+
Requires-Dist: huggingface-hub>=0.26; extra == 'all'
|
|
16
|
+
Requires-Dist: imageio-ffmpeg>=0.5; extra == 'all'
|
|
17
|
+
Requires-Dist: ipython>=8.0; extra == 'all'
|
|
18
|
+
Requires-Dist: matplotlib>=3.7; extra == 'all'
|
|
19
|
+
Requires-Dist: msgpack>=1.0; extra == 'all'
|
|
20
|
+
Requires-Dist: mujoco==3.11.*; (python_version >= '3.10') and extra == 'all'
|
|
21
|
+
Requires-Dist: pandas>=2.0; extra == 'all'
|
|
22
|
+
Requires-Dist: pillow>=10.0; extra == 'all'
|
|
23
|
+
Requires-Dist: pyarrow>=15.0; extra == 'all'
|
|
24
|
+
Requires-Dist: websockets<16,>=15; extra == 'all'
|
|
25
|
+
Provides-Extra: dataset
|
|
26
|
+
Requires-Dist: pyarrow>=15.0; extra == 'dataset'
|
|
27
|
+
Provides-Extra: mujoco
|
|
28
|
+
Requires-Dist: mujoco==3.11.*; (python_version >= '3.10') and extra == 'mujoco'
|
|
29
|
+
Provides-Extra: notebook
|
|
30
|
+
Requires-Dist: ipython>=8.0; extra == 'notebook'
|
|
31
|
+
Requires-Dist: matplotlib>=3.7; extra == 'notebook'
|
|
32
|
+
Requires-Dist: pandas>=2.0; extra == 'notebook'
|
|
33
|
+
Requires-Dist: pillow>=10.0; extra == 'notebook'
|
|
34
|
+
Provides-Extra: openpi
|
|
35
|
+
Requires-Dist: msgpack>=1.0; extra == 'openpi'
|
|
36
|
+
Requires-Dist: pillow>=10.0; extra == 'openpi'
|
|
37
|
+
Requires-Dist: websockets<16,>=15; extra == 'openpi'
|
|
38
|
+
Provides-Extra: typing
|
|
39
|
+
Requires-Dist: datamodel-code-generator==0.35.0; extra == 'typing'
|
|
40
|
+
Provides-Extra: video
|
|
41
|
+
Requires-Dist: imageio-ffmpeg>=0.5; extra == 'video'
|
|
42
|
+
Requires-Dist: pillow>=10.0; extra == 'video'
|
|
43
|
+
Provides-Extra: warp
|
|
44
|
+
Requires-Dist: mujoco-warp==3.14.0; (python_version >= '3.10') and extra == 'warp'
|
|
45
|
+
Requires-Dist: mujoco==3.14.0; (python_version >= '3.10') and extra == 'warp'
|
|
46
|
+
Requires-Dist: warp-lang==1.15.0; (python_version >= '3.10') and extra == 'warp'
|
|
47
|
+
Provides-Extra: weights
|
|
48
|
+
Requires-Dist: huggingface-hub>=0.26; extra == 'weights'
|
|
49
|
+
Description-Content-Type: text/markdown
|
|
50
|
+
|
|
51
|
+
# SimHub SDK
|
|
52
|
+
|
|
53
|
+
SimHub is a Python SDK for building simulation environments, running robot
|
|
54
|
+
policies, and evaluating their results. Write your environment and controller
|
|
55
|
+
in Python, submit their source to hosted SimHub, and inspect runs, metrics,
|
|
56
|
+
recordings, and task outcomes in the
|
|
57
|
+
[SimHub console](https://simhub-console.pages.dev/).
|
|
58
|
+
|
|
59
|
+
This guide covers SDK **0.2.0**. Start with installation and the hosted example;
|
|
60
|
+
use the authoring reference when building your own project. The examples submit
|
|
61
|
+
to hosted SimHub by default, including the complete single-file starter. Local
|
|
62
|
+
execution is an explicit debugging option.
|
|
63
|
+
|
|
64
|
+
- [Install and connect](#install-and-connect)
|
|
65
|
+
- [Run your first hosted simulation](#run-your-first-hosted-simulation)
|
|
66
|
+
- [Author a project](#author-a-project)
|
|
67
|
+
- [Submit a complete single-file project](#submit-a-complete-single-file-project)
|
|
68
|
+
- [Compare policies and vary environments](#compare-policies-and-vary-environments)
|
|
69
|
+
- [Debug and recover runs](#debug-and-recover-runs)
|
|
70
|
+
- [Guidance for coding agents](#guidance-for-coding-agents)
|
|
71
|
+
|
|
72
|
+
## How SimHub fits together
|
|
73
|
+
|
|
74
|
+
An **environment** is the simulated world: it accepts actions, produces
|
|
75
|
+
observations, and reports measurements. A **policy** is the controller: it reads
|
|
76
|
+
observations and chooses the next action. A **task** defines success using the
|
|
77
|
+
environment's measurements, such as placing an object without a collision.
|
|
78
|
+
|
|
79
|
+
A **project** is the Python source and assets needed to build that world and run
|
|
80
|
+
its policies. An **episode** is one execution of an environment and policy with
|
|
81
|
+
a particular seed. Hosted episodes produce **runs**; the service groups work
|
|
82
|
+
into batches and schedules jobs for compilation, simulation, and requested
|
|
83
|
+
postprocessing. An **experiment** freezes a collection of episodes and places
|
|
84
|
+
limits on their total work.
|
|
85
|
+
|
|
86
|
+
For new hosted integrations, use `EnvironmentDefinition`, `PolicyDefinition`,
|
|
87
|
+
`TaskDefinition`, and `RunRequest`. These are validated descriptions of your
|
|
88
|
+
code and parameters. The backend constructs the actual runtime objects.
|
|
89
|
+
Decorator-based projects using `@simhub.environment` and `@simhub.policy` are
|
|
90
|
+
also supported and submit to the same hosted service.
|
|
91
|
+
|
|
92
|
+
## Physics backend
|
|
93
|
+
|
|
94
|
+
Native MuJoCo remains the default. Manifests and single-file declarations
|
|
95
|
+
without an image use `python-cpu`; typed packages without a manifest retain
|
|
96
|
+
`python-gpu` for policy/rendering resources with native physics. Existing
|
|
97
|
+
explicit runtime selections are preserved.
|
|
98
|
+
|
|
99
|
+
MuJoCo Warp is a supported opt-in backend. Select `image = "python-mujoco-warp"`
|
|
100
|
+
in `simhub.toml` and, for typed declarations, set
|
|
101
|
+
`physics=sh.MuJoCoPhysics(backend="mujoco-warp")`. It requires an allocated NVIDIA
|
|
102
|
+
GPU and a deployed runtime advertising `physics:mujoco-warp-v1`, with no silent
|
|
103
|
+
CPU fallback. `sh.MuJoCoPhysics()` and `sh.create_physics(model)` use native physics.
|
|
104
|
+
|
|
105
|
+
Construct `sh.create_physics(model, config=context.physics)` in the runtime. Use
|
|
106
|
+
its `step`, `reset`, `upload_state` and `bind_recorder` methods instead of directly
|
|
107
|
+
calling `mujoco.mj_step`. `MujocoEnvironment` subclasses honor the explicitly
|
|
108
|
+
selected hosted backend automatically.
|
|
109
|
+
|
|
110
|
+
See [the Warp runtime guide](../docs/SIMHUB_MUJOCO_WARP.md) for state synchronization,
|
|
111
|
+
cloth recording, migration and the hosted acceptance example. GPU acceleration
|
|
112
|
+
does not itself repair scene collisions or guarantee a speedup for one world.
|
|
113
|
+
|
|
114
|
+
## Install and connect
|
|
115
|
+
|
|
116
|
+
The core SDK supports Python 3.9 or newer. Use Python 3.10 or newer for local
|
|
117
|
+
MuJoCo and the repository examples in this guide. In your Python environment:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
python -m pip install simhub
|
|
121
|
+
python -c "import simhub; print(simhub.__version__)"
|
|
122
|
+
simhub --help
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The base installation contains everything needed to submit the examples in this
|
|
126
|
+
guide. Hosted workers provide the simulation engines; MuJoCo, Blender, and model
|
|
127
|
+
weights do not need to be installed on the submitting machine. Optional extras
|
|
128
|
+
support client-side analysis and explicitly requested local development:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
python -m pip install 'simhub[mujoco]' # Local MuJoCo, pinned to the supported series
|
|
132
|
+
python -m pip install 'simhub[video]' # Local video encoding and image support
|
|
133
|
+
python -m pip install 'simhub[notebook]' # Tables, plots, and notebook display
|
|
134
|
+
python -m pip install 'simhub[openpi]' # OpenPI WebSocket client
|
|
135
|
+
python -m pip install 'simhub[dataset]' # LeRobot state/action export
|
|
136
|
+
python -m pip install 'simhub[weights]' # Hugging Face checkpoint access
|
|
137
|
+
python -m pip install 'simhub[typing]' # Generate parameter classes from schemas
|
|
138
|
+
python -m pip install 'simhub[all]' # Runtime extras above, excluding typing
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The standard hosted Python runtimes include MuJoCo, NumPy, Pillow, video support,
|
|
142
|
+
and the OpenPI client transport. Projects do not need to repeat these packages
|
|
143
|
+
in their manifests. Declare only additional project-specific libraries. Blender
|
|
144
|
+
and its `bpy` module need separate installation only for explicit local use.
|
|
145
|
+
|
|
146
|
+
For hosted work, sign in to the
|
|
147
|
+
[SimHub console](https://simhub-console.pages.dev/), open **API keys**, and create
|
|
148
|
+
a key. Make it available to your process:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
export SIMHUB_API_KEY="your-api-key"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
import simhub as sh
|
|
156
|
+
|
|
157
|
+
with sh.connect() as sim:
|
|
158
|
+
print(sim)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The default API is `https://api.g-deployment.com`. To use another deployment,
|
|
162
|
+
set `SIMHUB_API_URL` or pass `sh.connect(url="https://your-api.example.com")`.
|
|
163
|
+
Explicit `url=` and `key=` arguments take precedence over environment variables.
|
|
164
|
+
Keep keys out of project source and uploaded assets.
|
|
165
|
+
|
|
166
|
+
`connect()` is hosted-only by default and prevents `project.run_local(...)`
|
|
167
|
+
through that session. No additional flag is needed for hosted execution.
|
|
168
|
+
Use `sim.run(...)` for typed requests or `sim.project(...).launch(...)` for
|
|
169
|
+
factory projects. Both upload source and submit hosted jobs whose results appear
|
|
170
|
+
in the console. The CLI's `simhub launch PROJECT` and `simhub run PROJECT`
|
|
171
|
+
commands also default to hosted submission.
|
|
172
|
+
|
|
173
|
+
For repository contributors: the published SDK and the server distribution
|
|
174
|
+
share the `simhub` import name. Use separate environments for them. Installing
|
|
175
|
+
`./simhub` directly selects the server's build configuration; see the
|
|
176
|
+
[SDK build and release instructions](https://github.com/General-Deployment/sim/blob/main/simhub/PUBLISHING.md)
|
|
177
|
+
when you need an SDK built from source.
|
|
178
|
+
|
|
179
|
+
## Run your first hosted simulation
|
|
180
|
+
|
|
181
|
+
The repository includes a complete warehouse project: a scripted robot scans
|
|
182
|
+
three shelf labels and returns to its starting position. Its classes are
|
|
183
|
+
project code, so get the example before importing them:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
git clone https://github.com/General-Deployment/sim.git
|
|
187
|
+
cd sim/examples
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
With the SDK installed and your API key configured, save the following as
|
|
191
|
+
`run_warehouse.py` in this `examples` directory:
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
from pathlib import Path
|
|
195
|
+
import simhub as sh
|
|
196
|
+
from warehouse_barcode_scan.definitions import Warehouse, BarcodeController, ScanLabels
|
|
197
|
+
|
|
198
|
+
request = sh.RunRequest(
|
|
199
|
+
environment=Warehouse(),
|
|
200
|
+
policy=BarcodeController(),
|
|
201
|
+
task=ScanLabels(),
|
|
202
|
+
seed=0,
|
|
203
|
+
limits=sh.RunLimits(max_steps=960, timeout_seconds=1800, gpus=1),
|
|
204
|
+
outputs=sh.Outputs(cameras=()),
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
with sh.connect() as sim:
|
|
208
|
+
plan = sim.plan(request, project="warehouse_barcode_scan")
|
|
209
|
+
print(plan.describe())
|
|
210
|
+
|
|
211
|
+
run = sim.run(
|
|
212
|
+
request,
|
|
213
|
+
project="warehouse_barcode_scan",
|
|
214
|
+
request_id="warehouse-first-run-1",
|
|
215
|
+
)
|
|
216
|
+
run.save(Path("warehouse-run.json"))
|
|
217
|
+
run.wait(outputs=True)
|
|
218
|
+
|
|
219
|
+
print("Run:", run.id)
|
|
220
|
+
print("Evaluation:", run.evaluation)
|
|
221
|
+
for artifact in run.artifacts:
|
|
222
|
+
print(artifact.id, artifact.path)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
python run_warehouse.py
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
This submits real hosted work. The SDK validates the request, packages the
|
|
230
|
+
source, and selects compatible hosted runtimes. The backend builds the scene,
|
|
231
|
+
executes the policy, and records the result. Your machine does not need MuJoCo,
|
|
232
|
+
Blender, a GPU, or an operator account to submit it. The example requires a
|
|
233
|
+
deployment that advertises the corresponding typed scene and runtime variation
|
|
234
|
+
capabilities; the client reports missing capabilities before submission.
|
|
235
|
+
|
|
236
|
+
`sim.plan(...)` reads source files and the hosted catalog without uploading or
|
|
237
|
+
starting jobs. It describes stages and limits; it is not a price quote or a
|
|
238
|
+
capacity reservation. `sim.run(...)` submits the work. The saved receipt lets
|
|
239
|
+
you resume monitoring after closing the terminal.
|
|
240
|
+
|
|
241
|
+
Open the [SimHub console](https://simhub-console.pages.dev/), select the same
|
|
242
|
+
organization as your API key, and open **Runs** to follow the submitted run.
|
|
243
|
+
Its run page shows execution status, task evaluation, metrics, and available
|
|
244
|
+
recordings. SDK submissions appear there automatically; no separate result
|
|
245
|
+
upload is needed. The **Scenes** view contains stored scene previews and
|
|
246
|
+
snapshots when the project produces them.
|
|
247
|
+
|
|
248
|
+
The first run above requests no extra recording cameras or cinematic film.
|
|
249
|
+
To request a film, replace its `outputs` with:
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
outputs = sh.Outputs(video=sh.VideoOutput())
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Use this value in a new `RunRequest` and a new request ID. The default cinematic
|
|
256
|
+
is 1920×1080, 60 fps, 20 seconds, and 24 samples, with overlays enabled. Film
|
|
257
|
+
delivery also requires a compatible hosted renderer and takes additional work.
|
|
258
|
+
After `run.wait(outputs=True)`, `run.video`, `run.poster`, and `run.provenance`
|
|
259
|
+
identify the delivered artifacts. Ordinary artifact URLs require authentication;
|
|
260
|
+
use the session's client to download their bytes:
|
|
261
|
+
|
|
262
|
+
```python
|
|
263
|
+
# Inside the open session, after waiting for a run that requested a film:
|
|
264
|
+
video = run.video
|
|
265
|
+
if video is not None:
|
|
266
|
+
Path("warehouse.mp4").write_bytes(sim.client.download(video.id))
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Execution and task success are separate. `run.wait()` confirms execution and
|
|
270
|
+
requested delivery; inspect `run.evaluation.outcome` and
|
|
271
|
+
`run.evaluation.checked` to determine whether the stored task passed. A policy
|
|
272
|
+
can finish normally and fail to scan the labels.
|
|
273
|
+
|
|
274
|
+
See the [warehouse source and assumptions](https://github.com/General-Deployment/sim/tree/main/examples/warehouse_barcode_scan)
|
|
275
|
+
for the scene, controller, evaluation, and cinematic hooks.
|
|
276
|
+
|
|
277
|
+
## Author a project
|
|
278
|
+
|
|
279
|
+
### Project layout and dependencies
|
|
280
|
+
|
|
281
|
+
A typed project is a Python package directory. For example:
|
|
282
|
+
|
|
283
|
+
```text
|
|
284
|
+
my_project/
|
|
285
|
+
__init__.py
|
|
286
|
+
simhub.toml
|
|
287
|
+
parameters.py # Validated environment and policy inputs
|
|
288
|
+
definitions.py # Public environment, policy, and task contracts
|
|
289
|
+
runtime.py # Executable environment and policy implementations
|
|
290
|
+
recipe.py # Optional scene construction
|
|
291
|
+
parts.py # Optional reusable scene components
|
|
292
|
+
assets/ # Meshes, textures, and other input files
|
|
293
|
+
run_project.py # Submission script, beside the package
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Run `python run_project.py` from the directory containing `my_project`, import
|
|
297
|
+
from `my_project.definitions`, and pass `project="my_project"` to `sim.run`.
|
|
298
|
+
The source root must have a valid Python package name, such as `my_project`.
|
|
299
|
+
The manifest's display name may contain hyphens.
|
|
300
|
+
|
|
301
|
+
Put `simhub.toml` **inside** the package directory:
|
|
302
|
+
|
|
303
|
+
```toml
|
|
304
|
+
name = "my-project"
|
|
305
|
+
image = "python-gpu"
|
|
306
|
+
include = ["*.py", "assets/**"]
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
- `image` selects a runtime offered by the deployment, commonly `python-cpu`
|
|
310
|
+
or `python-gpu`.
|
|
311
|
+
- `include` controls which source and asset files enter the bundle. Add patterns
|
|
312
|
+
for nonstandard inputs such as `*.blend`. Files outside the bundle are not
|
|
313
|
+
available to hosted code unless resolved through a supported asset mechanism.
|
|
314
|
+
|
|
315
|
+
Bundles are deterministic and content-addressed. Keep heavy imports and model
|
|
316
|
+
loading inside runtime construction or factories so that importing definitions
|
|
317
|
+
remains lightweight. Use paths relative to `__file__` or the supplied runtime
|
|
318
|
+
context instead of absolute paths from your laptop.
|
|
319
|
+
|
|
320
|
+
For projects that need libraries beyond the standard hosted runtime,
|
|
321
|
+
`requirements` is an optional list of additional simulation dependencies.
|
|
322
|
+
`scene_requirements` optionally supplies the complete list of additional
|
|
323
|
+
packages for scene compilation and previews; omitting it uses `requirements`,
|
|
324
|
+
while `[]` adds no scene packages. Keep policy-only libraries out of the scene
|
|
325
|
+
list. Configure these once in the project rather than reconstructing dependency
|
|
326
|
+
lists for each run. The hosted runtime owns the supported MuJoCo version.
|
|
327
|
+
|
|
328
|
+
### Typed definitions
|
|
329
|
+
|
|
330
|
+
Subclass `sh.Parameters` to declare inputs with types and Pydantic constraints.
|
|
331
|
+
For example, in `my_project/parameters.py`:
|
|
332
|
+
|
|
333
|
+
```python
|
|
334
|
+
from pydantic import Field
|
|
335
|
+
import simhub as sh
|
|
336
|
+
|
|
337
|
+
class SceneParameters(sh.Parameters):
|
|
338
|
+
target_count: int = Field(default=3, ge=1, le=10)
|
|
339
|
+
spacing_m: float = Field(default=0.5, gt=0)
|
|
340
|
+
|
|
341
|
+
class ControllerParameters(sh.Parameters):
|
|
342
|
+
speed: float = Field(default=0.2, gt=0, le=1)
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Definitions describe the runtime's interface. The following is a contract
|
|
346
|
+
example for `my_project/definitions.py`; it requires the matching runtime
|
|
347
|
+
implementations described below before it can be submitted:
|
|
348
|
+
|
|
349
|
+
```python
|
|
350
|
+
import simhub as sh
|
|
351
|
+
from .parameters import SceneParameters, ControllerParameters
|
|
352
|
+
|
|
353
|
+
STATE = sh.ArrayDefinition(name="state", shape=(6,), dtype="float32", unit="rad")
|
|
354
|
+
ACTION = sh.ArrayDefinition(name="action", shape=(6,), dtype="float32", unit="rad")
|
|
355
|
+
|
|
356
|
+
environment = sh.EnvironmentDefinition[SceneParameters](
|
|
357
|
+
name="my-environment",
|
|
358
|
+
parameters=SceneParameters(),
|
|
359
|
+
runtime=sh.SourceRef(entrypoint="my_project.runtime:MyEnvironment"),
|
|
360
|
+
observations=(STATE,),
|
|
361
|
+
action=ACTION,
|
|
362
|
+
metrics=(sh.MetricDefinition(name="targets_reached"),),
|
|
363
|
+
control_hz=30,
|
|
364
|
+
)
|
|
365
|
+
policy = sh.PolicyDefinition[ControllerParameters](
|
|
366
|
+
name="my-controller",
|
|
367
|
+
parameters=ControllerParameters(),
|
|
368
|
+
runtime=sh.SourceRef(entrypoint="my_project.runtime:MyController"),
|
|
369
|
+
observations=(STATE,),
|
|
370
|
+
action=ACTION,
|
|
371
|
+
control_hz=30,
|
|
372
|
+
)
|
|
373
|
+
task = sh.TaskDefinition(
|
|
374
|
+
name="reach-all-targets",
|
|
375
|
+
spec=sh.ConditionsSpec(conditions=(
|
|
376
|
+
sh.MetricPredicate(metric="targets_reached", op="gte", value=3),
|
|
377
|
+
)),
|
|
378
|
+
)
|
|
379
|
+
request = sh.RunRequest(environment=environment, policy=policy, task=task)
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
`RunRequest` validates that the action schemas and control rates match, the
|
|
383
|
+
policy's observations exist with the declared shapes, dtypes, and units, and
|
|
384
|
+
the task references declared metrics. Supported predicate operators are `lt`,
|
|
385
|
+
`lte`, `gt`, `gte`, `eq`, and `ne`. Use `StagesSpec` with `StagePredicate` for
|
|
386
|
+
ordered progress or `ScoreSpec` for a threshold on one metric.
|
|
387
|
+
|
|
388
|
+
Contracts reject unknown fields and nonfinite numbers and are immutable.
|
|
389
|
+
Construct a new value to change inputs. `model_dump_json()` serializes a
|
|
390
|
+
contract; `sh.export_schema(type(value))` exposes its JSON Schema. Preserve
|
|
391
|
+
concrete parameter types when loading serialized definitions or experiments.
|
|
392
|
+
|
|
393
|
+
`SourceRef` is a `package.module:ClassOrFactory` reference to code in the bundle.
|
|
394
|
+
`SourceRef.of(MyClass)` also works for importable classes; classes defined in
|
|
395
|
+
`__main__` or nested inside a function cannot be used that way. A typed
|
|
396
|
+
`EnvironmentDefinition` describes an environment; it is not the environment
|
|
397
|
+
instance that steps physics.
|
|
398
|
+
|
|
399
|
+
### Runtime contracts
|
|
400
|
+
|
|
401
|
+
For typed projects, the environment constructor or factory accepts
|
|
402
|
+
`parameters: SceneParameters` and `context: sh.RuntimeContext` as keyword
|
|
403
|
+
arguments. The policy accepts `parameters: ControllerParameters`,
|
|
404
|
+
`context: sh.RuntimeContext`, and `environment`. Annotate `parameters` with its
|
|
405
|
+
concrete `Parameters` subclass: the hosted runtime uses that annotation to
|
|
406
|
+
validate the input. Model-backed typed policies also accept `model` when a
|
|
407
|
+
model binding is declared.
|
|
408
|
+
|
|
409
|
+
An environment implements:
|
|
410
|
+
|
|
411
|
+
- `spec: sh.EnvSpec`: its action width, observation schemas, control rate,
|
|
412
|
+
episode limit, cameras, and metrics. The identifier field is `id`.
|
|
413
|
+
- `reset(seed) -> dict`: reset episode state and return initial observations.
|
|
414
|
+
- `step(action) -> (dict, sh.StepInfo)`: advance one control tick. This is a
|
|
415
|
+
two-item tuple; it is not the Gymnasium five-item return value.
|
|
416
|
+
- `observe(keys) -> dict`: provide the requested observations and render only
|
|
417
|
+
requested image keys.
|
|
418
|
+
- `score() -> dict`: return the declared metrics. Missing or nonfinite required
|
|
419
|
+
metrics prevent a clean result.
|
|
420
|
+
- `done`: a boolean property indicating that the episode has ended.
|
|
421
|
+
|
|
422
|
+
`sh.Environment` supplies `spec` from your `describe()` method, a step counter,
|
|
423
|
+
and a default `done` based on `spec.max_steps`. Your implementation resets
|
|
424
|
+
`self._steps` in `reset()` and increments it in `step()`. Initialize fields used
|
|
425
|
+
by `describe()` or `done` before calling `super().__init__()`.
|
|
426
|
+
|
|
427
|
+
A policy implements `spec: sh.PolicySpec`, `reset()`, and `act(obs) -> action`.
|
|
428
|
+
Use `sh.PolicyBase` for a stateless default `reset`, and override it to clear
|
|
429
|
+
stateful controllers between episodes. `PolicySpec.expects` names the
|
|
430
|
+
observations the loop requests. Implement `setup(context)` and `close()` when
|
|
431
|
+
resources need initialization or cleanup. Both base classes provide these hooks.
|
|
432
|
+
|
|
433
|
+
`StepInfo(progress=..., metrics=..., state=..., done=...)` describes an
|
|
434
|
+
individual step. Progress and `done` do not themselves prove task success;
|
|
435
|
+
the task evaluates the recorded measurements. Keep runtime specs consistent
|
|
436
|
+
with their typed definitions: the worker checks them during construction.
|
|
437
|
+
|
|
438
|
+
`RuntimeContext` supplies `source_root`, `output_root`, an optional compiled
|
|
439
|
+
`scene_path`, and deterministic seed streams. Use `context.seeds` or
|
|
440
|
+
`context.rng("sensor_noise", namespace="wrist")` for independent random streams.
|
|
441
|
+
Seed third-party simulators and frameworks explicitly.
|
|
442
|
+
|
|
443
|
+
For existing engines, use `sh.MujocoEnvironment` or adapt a Gymnasium environment
|
|
444
|
+
with `sh.GymnasiumAdapter`. The
|
|
445
|
+
[Gymnasium adapter guide](https://github.com/General-Deployment/sim/blob/main/docs/SDK_TYPED_PARAMETERS.md#adapting-an-existing-gymnasium-environment)
|
|
446
|
+
and [warehouse runtime](https://github.com/General-Deployment/sim/blob/main/examples/warehouse_barcode_scan/runtime.py)
|
|
447
|
+
show working integrations.
|
|
448
|
+
|
|
449
|
+
### Scene recipes, native engines, and assets
|
|
450
|
+
|
|
451
|
+
Set an environment's `recipe=sh.SourceRef(...)` when it needs a separate scene
|
|
452
|
+
compilation stage. An `EnvironmentRecipe` composes `Component` objects through
|
|
453
|
+
a `SceneBuilder`. MuJoCo and Blender components can supply native programs or
|
|
454
|
+
files while keeping their public inputs typed. The runtime consumes the compiled
|
|
455
|
+
scene through `context.scene_path`.
|
|
456
|
+
|
|
457
|
+
Start with the [native component guide](https://github.com/General-Deployment/sim/blob/main/docs/SDK_NATIVE_COMPONENTS.md)
|
|
458
|
+
and [warehouse recipe](https://github.com/General-Deployment/sim/blob/main/examples/warehouse_barcode_scan/recipe.py).
|
|
459
|
+
`ObjectSlot`, `ComponentInterface`, `SlotSelection`, and `InstanceOptions` let
|
|
460
|
+
components expose compatible joints, frames, materials, and instance settings.
|
|
461
|
+
|
|
462
|
+
For reusable robot or mesh files, upload a named asset:
|
|
463
|
+
|
|
464
|
+
```bash
|
|
465
|
+
simhub asset upload robot.urdf --env robots --name arm
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
Resolve declared assets in project code with `sh.asset("arm").path`. URDF
|
|
469
|
+
references need their meshes and other dependencies packaged too. Follow
|
|
470
|
+
[named assets](https://github.com/General-Deployment/sim/blob/main/simhub/docs/named-assets.md)
|
|
471
|
+
for aliases and version pinning, and
|
|
472
|
+
[asset metadata](https://github.com/General-Deployment/sim/blob/main/docs/SDK_ASSET_METADATA.md)
|
|
473
|
+
for links, joints, materials, and semantic roles.
|
|
474
|
+
|
|
475
|
+
Blender-authored factory projects can provide `write_mjcf(out, seed=0, **kwargs)`.
|
|
476
|
+
The `simhub.blender` helpers mark static objects, dynamic bodies, collision
|
|
477
|
+
proxies, and excluded visual geometry before export. Include `.blend` files in
|
|
478
|
+
the manifest if the scene is authored in Blender's UI. See
|
|
479
|
+
[the procedural Blender example](https://github.com/General-Deployment/sim/tree/main/examples/blender_depot)
|
|
480
|
+
and [the saved Blender scene example](https://github.com/General-Deployment/sim/tree/main/examples/blend_file_dock).
|
|
481
|
+
MuJoCo collision geometry is convex; use separate parts or proxies where a
|
|
482
|
+
single hull would fill an opening.
|
|
483
|
+
|
|
484
|
+
## Submit a complete single-file project
|
|
485
|
+
|
|
486
|
+
This small environment moves a number toward a seeded target. It demonstrates
|
|
487
|
+
the complete environment, policy, task, and hosted submission using only the
|
|
488
|
+
base SDK and your API key. Save it as `counter.py`:
|
|
489
|
+
|
|
490
|
+
```python
|
|
491
|
+
import numpy as np
|
|
492
|
+
import simhub as sh
|
|
493
|
+
|
|
494
|
+
sh.manifest("counter-demo", image="python-cpu")
|
|
495
|
+
|
|
496
|
+
class Counter(sh.Environment):
|
|
497
|
+
def __init__(self):
|
|
498
|
+
self.position = 0.0
|
|
499
|
+
self.target = 1.0
|
|
500
|
+
super().__init__()
|
|
501
|
+
|
|
502
|
+
def describe(self):
|
|
503
|
+
return sh.EnvSpec(
|
|
504
|
+
id="counter", action_dim=1, control_hz=10, max_steps=20,
|
|
505
|
+
observation={"state": sh.ArraySpec(shape=(2,))},
|
|
506
|
+
randomised=True,
|
|
507
|
+
metrics={"reached": sh.MetricSpec(required=True)},
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
def reset(self, seed=0):
|
|
511
|
+
self._steps = 0
|
|
512
|
+
self.position = 0.0
|
|
513
|
+
self.target = float(np.random.default_rng(seed).integers(3, 8))
|
|
514
|
+
return self.observe(("state",))
|
|
515
|
+
|
|
516
|
+
def observe(self, keys):
|
|
517
|
+
if "state" not in keys:
|
|
518
|
+
return {}
|
|
519
|
+
return {"state": np.array([self.position, self.target], dtype="float32")}
|
|
520
|
+
|
|
521
|
+
def step(self, action):
|
|
522
|
+
self.position += float(np.clip(action[0], 0, 1))
|
|
523
|
+
self._steps += 1
|
|
524
|
+
return self.observe(("state",)), sh.StepInfo(done=self.done)
|
|
525
|
+
|
|
526
|
+
@property
|
|
527
|
+
def done(self):
|
|
528
|
+
return self.position >= self.target or super().done
|
|
529
|
+
|
|
530
|
+
def score(self):
|
|
531
|
+
return {"reached": float(self.position >= self.target)}
|
|
532
|
+
|
|
533
|
+
class Advance(sh.PolicyBase):
|
|
534
|
+
def __init__(self, env):
|
|
535
|
+
super().__init__(sh.PolicySpec(
|
|
536
|
+
action_dim=1, control_hz=env.spec.control_hz, expects=("state",),
|
|
537
|
+
))
|
|
538
|
+
|
|
539
|
+
def act(self, obs):
|
|
540
|
+
position, target = obs["state"]
|
|
541
|
+
return np.array([min(1.0, max(0.0, target - position))], dtype="float32")
|
|
542
|
+
|
|
543
|
+
@sh.environment("counter")
|
|
544
|
+
def counter():
|
|
545
|
+
return Counter()
|
|
546
|
+
|
|
547
|
+
@sh.policy("advance")
|
|
548
|
+
def advance(env):
|
|
549
|
+
return Advance(env)
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
Save the submission script beside it as `submit_counter.py`. The simulation
|
|
553
|
+
source stays in `counter.py`; this script loads and submits it:
|
|
554
|
+
|
|
555
|
+
```python
|
|
556
|
+
import simhub as sh
|
|
557
|
+
|
|
558
|
+
with sh.connect() as sim:
|
|
559
|
+
project = sim.project("counter.py")
|
|
560
|
+
batch = project.launch(
|
|
561
|
+
env="counter", policies=["advance"], seeds=(3,),
|
|
562
|
+
task=sh.Task(reached="eq:1"), record=sh.Record(), gpu=False,
|
|
563
|
+
request_id="counter-first-run-1",
|
|
564
|
+
)
|
|
565
|
+
print("Batch:", batch.id)
|
|
566
|
+
print("Open https://simhub-console.pages.dev/ and select Runs.")
|
|
567
|
+
batch.wait()
|
|
568
|
+
for run in batch.runs:
|
|
569
|
+
print(run.id, run.state, run.outcome, run.metrics)
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
```bash
|
|
573
|
+
python submit_counter.py
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
This uploads the single-file project and runs one episode on a hosted CPU.
|
|
577
|
+
Find it in the console under the `counter-demo` environment. It should report
|
|
578
|
+
task success with `reached=1.0`; its metrics, evaluation, and result are stored
|
|
579
|
+
on SimHub. It has no camera or physics body, so it produces no video. Reuse the
|
|
580
|
+
request ID only when recovering the same submission; choose a new ID when you
|
|
581
|
+
intend to run changed code or another episode.
|
|
582
|
+
|
|
583
|
+
A larger factory project uses the same decorators in its package's
|
|
584
|
+
`__init__.py` and the same `simhub.toml` layout described above.
|
|
585
|
+
`sh.Project.load(path)` imports a factory project to discover its registrations;
|
|
586
|
+
typed submission collects source using declarations without importing its
|
|
587
|
+
runtime code.
|
|
588
|
+
|
|
589
|
+
## Compare policies and vary environments
|
|
590
|
+
|
|
591
|
+
### Hosted batches for factory projects
|
|
592
|
+
|
|
593
|
+
To compare the counter's policy against a baseline, submit a hosted batch from
|
|
594
|
+
the directory containing `counter.py`, with an API key configured:
|
|
595
|
+
|
|
596
|
+
```python
|
|
597
|
+
import simhub as sh
|
|
598
|
+
|
|
599
|
+
with sh.connect() as sim:
|
|
600
|
+
project = sim.project("counter.py")
|
|
601
|
+
batch = project.launch(
|
|
602
|
+
env="counter",
|
|
603
|
+
policies=["advance", "simhub:hold"],
|
|
604
|
+
seeds=range(8),
|
|
605
|
+
task=sh.Task(reached="eq:1"),
|
|
606
|
+
record=sh.Record(),
|
|
607
|
+
gpu=False,
|
|
608
|
+
)
|
|
609
|
+
print("Batch:", batch.id)
|
|
610
|
+
batch.wait()
|
|
611
|
+
print(batch.report().table())
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
This creates 16 episodes: two policies × eight seeds. `simhub:hold` is a built-in
|
|
615
|
+
baseline. Install the `notebook` extra for DataFrame tables and plots. Inside an
|
|
616
|
+
open session, `batch.dataframe()`, `batch.videos()`, and `batch.plot.success()`
|
|
617
|
+
help inspect recorded results; `batch.cancel()` requests cancellation. Resume
|
|
618
|
+
an existing batch with `sim.batch("batch_...")`.
|
|
619
|
+
|
|
620
|
+
For a fixed benchmark, define a suite and evaluation, then use the same project:
|
|
621
|
+
|
|
622
|
+
```python
|
|
623
|
+
suite = sh.Suite.seeds("counter/v1", range(8))
|
|
624
|
+
evaluation = sh.Eval(
|
|
625
|
+
suite=suite, task=sh.Task(reached="eq:1"),
|
|
626
|
+
primary="success", truncated_is="failure",
|
|
627
|
+
)
|
|
628
|
+
# Inside the session above:
|
|
629
|
+
batch = project.evaluate(
|
|
630
|
+
evaluation, env="counter", policies=["advance", "simhub:hold"],
|
|
631
|
+
record=sh.Record(), gpu=False,
|
|
632
|
+
)
|
|
633
|
+
report = batch.wait().report(evaluation)
|
|
634
|
+
print(report.table())
|
|
635
|
+
print(report.compare("advance", "simhub:hold"))
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
A suite freezes seeds and parameter overrides, and its content digest identifies
|
|
639
|
+
its version. Reports distinguish coverage, execution failures, checked task
|
|
640
|
+
verdicts, and runner claims. Comparisons are paired across the same suite and
|
|
641
|
+
include uncertainty. Decide explicitly how truncated episodes count.
|
|
642
|
+
|
|
643
|
+
### Typed experiments
|
|
644
|
+
|
|
645
|
+
For typed projects, use `Variations` and `ExperimentPlan` to validate and freeze
|
|
646
|
+
the entire comparison before submitting. From the `sim/examples` directory
|
|
647
|
+
used in the hosted quickstart:
|
|
648
|
+
|
|
649
|
+
```python
|
|
650
|
+
from pathlib import Path
|
|
651
|
+
import simhub as sh
|
|
652
|
+
from warehouse_barcode_scan.definitions import Warehouse, BarcodeController, ScanLabels
|
|
653
|
+
from warehouse_barcode_scan.parameters import WarehouseParameters
|
|
654
|
+
|
|
655
|
+
variations = sh.Variations.grid(
|
|
656
|
+
WarehouseParameters,
|
|
657
|
+
base=WarehouseParameters(width=320, height=240, verify_physics=False),
|
|
658
|
+
axes={"carton_color": ("natural", "blue")},
|
|
659
|
+
)
|
|
660
|
+
plan = sh.ExperimentPlan.vary(
|
|
661
|
+
Warehouse(), variations,
|
|
662
|
+
policies=(BarcodeController(),), task=ScanLabels(), seeds=(0, 1),
|
|
663
|
+
limits=sh.RunLimits(max_steps=960, timeout_seconds=300, gpus=1),
|
|
664
|
+
outputs=sh.Outputs(cameras=()),
|
|
665
|
+
budget=sh.ExperimentBudget(
|
|
666
|
+
max_runs=4, max_concurrent_jobs=2, max_gpus=1,
|
|
667
|
+
timeout_seconds=3600, max_gpu_seconds=3600,
|
|
668
|
+
),
|
|
669
|
+
)
|
|
670
|
+
print(plan.describe()) # Two colors × two replicates × one policy = four episodes
|
|
671
|
+
|
|
672
|
+
with sh.connect() as sim:
|
|
673
|
+
experiment = sim.experiment(
|
|
674
|
+
plan, project="warehouse_barcode_scan", request_id="color-comparison-1",
|
|
675
|
+
)
|
|
676
|
+
experiment.save(Path("experiment.json"))
|
|
677
|
+
experiment.wait()
|
|
678
|
+
print(experiment.report(group_by=("policy", "carton_color")))
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
`Variations.grid` takes a cross product; `zip` pairs axis entries; `cases`
|
|
682
|
+
accepts explicit parameter values; `sample` draws bounded random cases.
|
|
683
|
+
Portable recipes add serializable distributions, correlated choices, and
|
|
684
|
+
constraints. Rejected cases are recorded, and an unsuccessful bounded sample
|
|
685
|
+
does not silently submit a smaller experiment.
|
|
686
|
+
|
|
687
|
+
`RunLimits` bounds an episode's steps and stage resources. `ExperimentBudget`
|
|
688
|
+
separately bounds the number of episodes, concurrent jobs, GPUs, elapsed time
|
|
689
|
+
including queueing, and reserved GPU seconds. Compilation, simulation,
|
|
690
|
+
rendering, and retries consume the applicable budget. Tenant and fleet limits
|
|
691
|
+
still apply. A plan requires a backend that can enforce its limits.
|
|
692
|
+
|
|
693
|
+
Parameters are treated as compilation inputs by default. Reusing a compiled
|
|
694
|
+
scene across reset-only changes requires explicit `ParameterEffect`
|
|
695
|
+
declarations, runtime ABI 2, and a `VariationRuntime.apply_variation` adapter.
|
|
696
|
+
See [typed experiments](https://github.com/General-Deployment/sim/blob/main/docs/SDK_TYPED_EXPERIMENTS.md)
|
|
697
|
+
and [portable variation recipes](https://github.com/General-Deployment/sim/blob/main/docs/SDK_VARIATION_RECIPES.md)
|
|
698
|
+
for budget accounting, deterministic seed derivation, and reuse rules.
|
|
699
|
+
|
|
700
|
+
## Recordings, rendering, and datasets
|
|
701
|
+
|
|
702
|
+
Recording is explicit. Factory runs use `sh.Record(cameras=(...), video="hero")`;
|
|
703
|
+
typed requests use `sh.Outputs(cameras=(...), video=sh.VideoOutput(...))`.
|
|
704
|
+
Camera names must exist in the environment. `PolicySpec.expects` controls what
|
|
705
|
+
the policy observes, while recording options can request additional cameras.
|
|
706
|
+
Use `Outputs(cameras=())` to omit additional recording cameras; the default is
|
|
707
|
+
`("hero",)`. A policy's required image observations still have to be rendered.
|
|
708
|
+
|
|
709
|
+
`sh.Record(video="hero")` defaults to **60 fps** for environments with
|
|
710
|
+
`step_with_substeps(action, on_substep)`. `MujocoEnvironment` provides this hook
|
|
711
|
+
when using its built-in `step`; subclasses that override `step` must provide
|
|
712
|
+
their own hook. It runs the normal control step and calls `on_substep(fraction)`
|
|
713
|
+
after each physics substep, ending at `1`. The recorder renders only when an
|
|
714
|
+
output frame is due, using `render(camera)` at the current physics state.
|
|
715
|
+
Custom renderers must not reuse a frame cached for the whole control step.
|
|
716
|
+
Use `sh.Record(video="hero", fps=30)` to choose another presentation rate
|
|
717
|
+
between 1 and 60 fps. Frames are labeled with the completed control step's
|
|
718
|
+
progress and state, including its terminal status.
|
|
719
|
+
The smooth presentation is saved as `film/video.mp4` and selected as the run's
|
|
720
|
+
primary video. Policy-camera recordings retain the control rate and the exact
|
|
721
|
+
images the policy saw; trajectory sampling and policy calls are unchanged.
|
|
722
|
+
Older environments without the hook keep their original control-rate video
|
|
723
|
+
and log that limitation. Typed cinematic requests also default to 60 fps;
|
|
724
|
+
use `sh.VideoOutput(fps=30)` to choose another rate.
|
|
725
|
+
|
|
726
|
+
Depending on the environment and requested outputs, artifacts include:
|
|
727
|
+
|
|
728
|
+
```text
|
|
729
|
+
metrics.json Final measurements
|
|
730
|
+
result.json Local episode result
|
|
731
|
+
evaluation.json Metric metadata and local task evaluation
|
|
732
|
+
trajectory/meta.json Recording schema, body names, and units
|
|
733
|
+
trajectory/000000.npz Chunks of recorded state, actions, and poses
|
|
734
|
+
video/hero.mp4 Direct camera recording, when requested
|
|
735
|
+
renders/<camera>/*.png Camera frames, when a frame sink is selected
|
|
736
|
+
checkpoints/*.npz Checkpoints, when supported and enabled
|
|
737
|
+
film/video.mp4 Smooth presentation or hosted cinematic output
|
|
738
|
+
film/poster.png Hosted cinematic poster
|
|
739
|
+
film/render-manifest.json Cinematic provenance
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
Not every environment produces every file. Body trajectories require recorder
|
|
743
|
+
bindings; checkpoints require environment support. `Recorder.channel(name,
|
|
744
|
+
sampler)` adds fixed-shape finite numeric fields. Pose quaternions use **wxyz**,
|
|
745
|
+
and the trajectory metadata records the convention. Direct camera recordings
|
|
746
|
+
preserve what the policy saw; cinematic rendering replays the saved motion.
|
|
747
|
+
The hosted run's stored outcome and checked flag remain authoritative over a
|
|
748
|
+
local evaluation sidecar.
|
|
749
|
+
|
|
750
|
+
After simulation, render a different film without running the policy again:
|
|
751
|
+
|
|
752
|
+
```python
|
|
753
|
+
from pathlib import Path
|
|
754
|
+
import simhub as sh
|
|
755
|
+
|
|
756
|
+
with sh.connect() as sim:
|
|
757
|
+
run = sh.RunHandle.resume(sim, Path("warehouse-run.json"))
|
|
758
|
+
render = run.render(
|
|
759
|
+
sh.VideoOutput(width=1280, height=720, fps=24, seconds=12),
|
|
760
|
+
request_id="warehouse-film-2",
|
|
761
|
+
)
|
|
762
|
+
render.save(Path("warehouse-render.json"))
|
|
763
|
+
render.wait()
|
|
764
|
+
print(run.video.url)
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
The recording must contain the scene and trajectory inputs required by the
|
|
768
|
+
renderer. A render cannot add observations that were never recorded.
|
|
769
|
+
Presentation projects can customize cameras, materials, overlays, and
|
|
770
|
+
composition; see [hosted rendering and recovery](https://github.com/General-Deployment/sim/blob/main/docs/SDK_HOSTED_RECOVERY.md).
|
|
771
|
+
|
|
772
|
+
For dataset export, install `simhub[dataset]` and use
|
|
773
|
+
`simhub.datasets.from_trajectories(...)` for local `trajectory/` directories or
|
|
774
|
+
`simhub.datasets.lerobot(batch, ...)` for a completed batch. The current exporter
|
|
775
|
+
writes LeRobot v2.1 state/action data and requires recorded `qpos` and `action`
|
|
776
|
+
channels. Batch export includes complete runs regardless of task success and
|
|
777
|
+
does not copy camera video files. Select appropriate episodes, match the
|
|
778
|
+
recorded control rate, and validate against your training loader. See the
|
|
779
|
+
[dataset export guide](https://github.com/General-Deployment/sim/blob/main/simhub/skills/simhub-datasets/SKILL.md)
|
|
780
|
+
for the complete example and current limits.
|
|
781
|
+
|
|
782
|
+
## Learned policies and model credentials
|
|
783
|
+
|
|
784
|
+
`sh.OpenPiPolicy` manages a connection to an OpenPI server and executes returned
|
|
785
|
+
action chunks. Subclass it, declare `expects`, and implement
|
|
786
|
+
`encode_observation(obs)`. Override `decode_action(action, obs)` or
|
|
787
|
+
`decode_chunk(actions, inference_obs)` when the checkpoint's output needs
|
|
788
|
+
conversion. Match camera keys, joint ordering, gripper units, action convention,
|
|
789
|
+
and control rate to the checkpoint.
|
|
790
|
+
|
|
791
|
+
The standard hosted Python runtimes include OpenPI's client dependencies.
|
|
792
|
+
For hosted factory runs, pass the endpoint through `policy_kwargs` to your
|
|
793
|
+
factory; it must be reachable from the worker. Install `simhub[openpi]` only
|
|
794
|
+
when using the client locally. See the
|
|
795
|
+
[OpenPI policy guide](https://github.com/General-Deployment/sim/blob/main/simhub/docs/openpi-policies.md)
|
|
796
|
+
for a full adapter, authentication, timeouts, and relative actions.
|
|
797
|
+
|
|
798
|
+
For private Hugging Face checkpoints:
|
|
799
|
+
|
|
800
|
+
```bash
|
|
801
|
+
simhub model credentials set my-hf-token
|
|
802
|
+
simhub model register --help
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
The first command reads the token through a hidden prompt. Register a pinned
|
|
806
|
+
policy revision bound to that credential, then select it with
|
|
807
|
+
`project.launch(policy_version_id=...)`. Typed policies use `HuggingFaceModel`
|
|
808
|
+
with a full 40-character revision and a credential ID; token values are never
|
|
809
|
+
part of the definition. See
|
|
810
|
+
[hosted model credentials](https://github.com/General-Deployment/sim/blob/main/simhub/MODEL_CREDENTIALS.md).
|
|
811
|
+
|
|
812
|
+
### Google Cloud Storage models
|
|
813
|
+
|
|
814
|
+
GCS is a checkpoint storage source; policies still execute on SimHub's
|
|
815
|
+
Lambda/Fleet compute backend. Use SDK, API, worker, and runner images from the
|
|
816
|
+
release containing `GCSModel` and `simhub model resolve`. Older 0.2.0 builds may
|
|
817
|
+
lack these APIs; check `simhub model resolve --help`. The submission SDK needs
|
|
818
|
+
only its normal dependencies. Google libraries run in the trusted API/worker,
|
|
819
|
+
and neither `gcloud` nor `gsutil` is required to resolve or download models.
|
|
820
|
+
See the [server setup](README.md#gcs-model-source-deployment) for deployment
|
|
821
|
+
prerequisites. Legacy Modal and other backends without trusted staging reject
|
|
822
|
+
GCS submissions before execution.
|
|
823
|
+
|
|
824
|
+
Resolve a public checkpoint into a frozen manifest:
|
|
825
|
+
|
|
826
|
+
```bash
|
|
827
|
+
simhub model resolve \
|
|
828
|
+
gs://gdm-robotics-open-x-embodiment/open_x_embodiment_and_rt_x_oss/rt_1_tf_trained_for_000400120/ \
|
|
829
|
+
--recursive --layout files --out models/rt1-converged.json
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
`--recursive` requires a nonempty prefix ending in `/`. Without it, the URI
|
|
833
|
+
names exactly one object. Resolution reads metadata, not weights. The resulting
|
|
834
|
+
JSON records the bucket, object names, exact generation strings, byte counts,
|
|
835
|
+
CRC32C checksums, relative destination paths, layout, and model root. Commit
|
|
836
|
+
this manifest with your experiment. Running it always requests those generations;
|
|
837
|
+
if a version disappears, the run fails instead of switching to newer weights.
|
|
838
|
+
Use completed release prefixes: listing is not an atomic snapshot of a directory
|
|
839
|
+
that a publisher is still uploading. Long-term reproducibility also requires
|
|
840
|
+
retaining the pinned objects or mirroring them to a controlled bucket.
|
|
841
|
+
|
|
842
|
+
For private buckets, obtain a service-account JSON key with permission to read
|
|
843
|
+
the intended objects. Exact-object resolution and downloads need
|
|
844
|
+
`storage.objects.get`; prefix expansion also needs `storage.objects.list`.
|
|
845
|
+
A bucket-scoped Storage Object Viewer role supplies both. Import the key once
|
|
846
|
+
through a file or stdin; never put key contents in your source or a command-line
|
|
847
|
+
argument:
|
|
848
|
+
|
|
849
|
+
```bash
|
|
850
|
+
simhub model credentials set research-gcs \
|
|
851
|
+
--provider gcs --service-account-file /secure/research-reader.json
|
|
852
|
+
simhub model credentials list
|
|
853
|
+
# Replace gcsc_returnedid with the opaque ID returned above.
|
|
854
|
+
simhub model resolve gs://my-policy-bucket/releases/policy.zip \
|
|
855
|
+
--layout zip --root policy --credential gcsc_returnedid \
|
|
856
|
+
--out models/private-policy.json
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
The imported key is encrypted and scoped to your organization. The manifest
|
|
860
|
+
contains only the credential revision ID. The trusted worker uses it to stage
|
|
861
|
+
files; policy code receives no Google key or access token. Omitting
|
|
862
|
+
`--credential` means anonymous access, even when your laptop has a Google login.
|
|
863
|
+
The initial private-access format is a `service_account` JSON key with Google's
|
|
864
|
+
standard OAuth token endpoint. Federation, signed URLs, Requester Pays billing,
|
|
865
|
+
and custom storage endpoints are not supported.
|
|
866
|
+
|
|
867
|
+
To rotate access, import a new key revision, resolve a manifest using its ID,
|
|
868
|
+
and register a new policy revision. Revoke old access with
|
|
869
|
+
`simhub model credentials revoke gcsc_oldid`. Revocation prevents new staging
|
|
870
|
+
and is checked again after downloads; it cannot retract bytes from an already
|
|
871
|
+
running policy. Do not delete an old object generation if experiments still
|
|
872
|
+
need it.
|
|
873
|
+
|
|
874
|
+
For buckets where listing is disallowed, pass a JSON array of exact names in
|
|
875
|
+
`--objects-file`, with `gs://bucket` as the URI. Names are retained as relative
|
|
876
|
+
destinations, so `--root` can select their shared model directory:
|
|
877
|
+
|
|
878
|
+
```bash
|
|
879
|
+
simhub model resolve gs://my-policy-bucket \
|
|
880
|
+
--objects-file checkpoint-objects.json --root releases/policy \
|
|
881
|
+
--credential gcsc_returnedid --out models/private-policy.json
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
Declare the resolved manifest in an existing typed project. Here `environment`, `task`,
|
|
885
|
+
`ControllerParameters`, and `Controller` are your project definitions:
|
|
886
|
+
|
|
887
|
+
```python
|
|
888
|
+
from pathlib import Path
|
|
889
|
+
import simhub as sh
|
|
890
|
+
|
|
891
|
+
model = sh.GCSModel.model_validate_json(
|
|
892
|
+
Path("models/private-policy.json").read_text()
|
|
893
|
+
)
|
|
894
|
+
policy = sh.PolicyDefinition(
|
|
895
|
+
name="gcs-policy",
|
|
896
|
+
parameters=ControllerParameters(),
|
|
897
|
+
runtime=sh.SourceRef.of(Controller),
|
|
898
|
+
observations=environment.observations,
|
|
899
|
+
action=environment.action,
|
|
900
|
+
control_hz=environment.control_hz,
|
|
901
|
+
model=model,
|
|
902
|
+
)
|
|
903
|
+
# Use this policy in your RunRequest; session.run registers the binding.
|
|
904
|
+
request = sh.RunRequest(environment=environment, policy=policy, task=task)
|
|
905
|
+
with sh.connect() as session:
|
|
906
|
+
run = session.run(request, project=Path("my_project"), request_id="gcs-trial-001")
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
Include a manifest loaded by project code in the source bundle's `include`
|
|
910
|
+
patterns. Resolve paths relative to the module when reading inside your project,
|
|
911
|
+
for example `Path(__file__).parent / "models/private-policy.json"`. Project
|
|
912
|
+
inventory embeds the resolved source in `PolicyDefinition.model`; no separate
|
|
913
|
+
TOML model registry is needed. Current hosted model policies must declare
|
|
914
|
+
`observation/state` in addition to any image observations.
|
|
915
|
+
|
|
916
|
+
Your typed policy constructor receives `model: sh.GCSModel` and a
|
|
917
|
+
`RuntimeContext` whose `model_path` is the verified local model directory. Load
|
|
918
|
+
TensorFlow, PyTorch, JAX, or other formats from that path using your policy's
|
|
919
|
+
own loader. Decorator-based factories receive `model` as a contract dictionary
|
|
920
|
+
and `model_path` as a local path string. Both inputs are bound to the registered
|
|
921
|
+
revision and cannot be overridden through `policy_kwargs`.
|
|
922
|
+
|
|
923
|
+
For an existing decorator-based policy, register and launch explicitly:
|
|
924
|
+
|
|
925
|
+
```bash
|
|
926
|
+
simhub model register gcs-policy --contract models/private-policy.json \
|
|
927
|
+
--project-policy gcs-policy --observation-dim 8 --action-dim 7 --control-hz 3
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
```python
|
|
931
|
+
# project is your existing HostedProject; use the version ID returned above.
|
|
932
|
+
batch = project.launch(policy_version_id="pvr_returnedid")
|
|
933
|
+
```
|
|
934
|
+
|
|
935
|
+
Python clients can use `session.client.resolve_model_source(...)`,
|
|
936
|
+
`set_gcs_credential(name, service_account_dict)`, and
|
|
937
|
+
`register_model_policy(...)` for the same workflow. Existing
|
|
938
|
+
`register_hf_policy(...)`, `HuggingFaceModel`, HF dependencies, and HF credential
|
|
939
|
+
commands retain their behavior and policy digests.
|
|
940
|
+
|
|
941
|
+
`layout="files"` preserves the manifest's relative paths. `layout="zip"`
|
|
942
|
+
requires one pinned ZIP, verifies its bytes before extraction, and uses `root`
|
|
943
|
+
to select the directory inside it. The default root is `.`. Inputs are mounted
|
|
944
|
+
read-only and staged separately for each run; there is no shared download cache.
|
|
945
|
+
Limits are 1 MiB of contract JSON, 1,024 selected objects, 20 GiB downloaded,
|
|
946
|
+
20 GiB extracted, 10,000 ZIP entries, and 16 MiB of ZIP directory metadata.
|
|
947
|
+
ZIP links, traversal, encryption,
|
|
948
|
+
special files, and colliding destinations are rejected. Disk headroom and a
|
|
949
|
+
bounded staging deadline also apply.
|
|
950
|
+
|
|
951
|
+
Common setup failures:
|
|
952
|
+
|
|
953
|
+
- **Access denied:** check the credential revision, bucket permissions, and
|
|
954
|
+
whether a prefix requires list permission. Ambient Google credentials are
|
|
955
|
+
intentionally ignored.
|
|
956
|
+
- **Generation unavailable:** restore the pinned version or explicitly resolve
|
|
957
|
+
and register a new release. A retry never chooses the latest bytes.
|
|
958
|
+
- **Verification/layout failure:** check object checksums and sizes, safe file
|
|
959
|
+
paths, ZIP contents, limits, and the selected root directory.
|
|
960
|
+
- **Capability unavailable:** deploy matching Fleet API/worker/runner and node
|
|
961
|
+
configuration. Google libraries belong in the trusted services, while model
|
|
962
|
+
inference dependencies must be baked into the offline policy image.
|
|
963
|
+
|
|
964
|
+
[SIMPLER example manifests](examples/gcs_models/README.md) pin the three RT-1
|
|
965
|
+
training-stage checkpoints and RT-1-X ZIP. Keep Octo on a pinned HF revision and
|
|
966
|
+
record all model digests with ranking results. These declarations download
|
|
967
|
+
checkpoints; RT-1/Octo adapters, language encoders/tokenizers, the Google Robot
|
|
968
|
+
environment, and observation/action parity are separate requirements before
|
|
969
|
+
comparing policy ordering. Auxiliary assets must be in the declared bundle or
|
|
970
|
+
pinned inference image; GCS models currently contain one file bundle or archive.
|
|
971
|
+
|
|
972
|
+
## Debug and recover runs
|
|
973
|
+
|
|
974
|
+
Persist a request ID **before** submitting if your application must recover
|
|
975
|
+
from a lost response. Reuse the same ID and identical inputs after
|
|
976
|
+
`SubmissionUncertain`; changing the ID requests new work. Use a new ID for an
|
|
977
|
+
intentional change to source, parameters, or outputs. Save the returned receipt
|
|
978
|
+
immediately. Receipts contain IDs and the API address, not an API key.
|
|
979
|
+
|
|
980
|
+
```python
|
|
981
|
+
from pathlib import Path
|
|
982
|
+
import simhub as sh
|
|
983
|
+
|
|
984
|
+
with sh.connect() as sim:
|
|
985
|
+
run = sh.RunHandle.resume(sim, Path("warehouse-run.json"))
|
|
986
|
+
for stage in run.stages:
|
|
987
|
+
print(stage.kind, stage.state, stage.attempts, stage.job_id)
|
|
988
|
+
try:
|
|
989
|
+
run.wait(outputs=True, timeout=3600)
|
|
990
|
+
except sh.HostedExecutionError as error:
|
|
991
|
+
print(error.failure.model_dump_json(indent=2))
|
|
992
|
+
print("Inspect job:", error.job_id)
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
`run.wait(outputs=False)` waits for simulation and required evaluation while
|
|
996
|
+
allowing a requested film to remain unfinished. `outputs=True` also requires
|
|
997
|
+
requested delivery. A client wait timeout does not cancel remote work. Resume
|
|
998
|
+
with the same API origin and credentials; keep the session open while using
|
|
999
|
+
its handles. `run.events()` yields typed execution events.
|
|
1000
|
+
|
|
1001
|
+
Use `run.cancel()` or `experiment.cancel()` to request cancellation and inspect
|
|
1002
|
+
the resulting stage states. Cancellation acknowledgement alone does not prove
|
|
1003
|
+
that all remote containers have stopped. A render receipt can be resumed with
|
|
1004
|
+
`sh.RenderHandle.resume`; an experiment receipt with
|
|
1005
|
+
`sh.ExperimentHandle.resume`.
|
|
1006
|
+
|
|
1007
|
+
Fetch saved logs by job ID or by environment:
|
|
1008
|
+
|
|
1009
|
+
```bash
|
|
1010
|
+
simhub logs job_... --tail 100
|
|
1011
|
+
simhub logs --env warehouse-barcode-scan --tail 100
|
|
1012
|
+
simhub logs job_... --json > job-logs.json
|
|
1013
|
+
```
|
|
1014
|
+
|
|
1015
|
+
```python
|
|
1016
|
+
with sh.connect() as sim:
|
|
1017
|
+
logs = sim.job_logs("job_...", tail=100)
|
|
1018
|
+
for log in logs.logs:
|
|
1019
|
+
print(log.path, log.text)
|
|
1020
|
+
```
|
|
1021
|
+
|
|
1022
|
+
`sim.environment_logs(name_or_id, tail=100)` provides the environment form in
|
|
1023
|
+
Python. It selects the newest submitted job across all versions and job kinds,
|
|
1024
|
+
including queued and running jobs. It does not fall back to an older job if the
|
|
1025
|
+
newest has no saved logs. These are snapshots of retained logs and structured
|
|
1026
|
+
errors, not a live stream. Logs commonly become available after an attempt ends.
|
|
1027
|
+
`tail` trims each log after download.
|
|
1028
|
+
|
|
1029
|
+
Common starting points for a failure:
|
|
1030
|
+
|
|
1031
|
+
- **Import or missing-file errors:** check the package name, entry points,
|
|
1032
|
+
manifest `include` patterns, and runtime dependency lists.
|
|
1033
|
+
- **Schema errors:** compare the environment and policy action, observation,
|
|
1034
|
+
and control-rate declarations; check task metric names.
|
|
1035
|
+
- **Missing camera or output:** declare the camera and recorder bindings, or
|
|
1036
|
+
remove an output you do not need. Inspect the render stage separately from
|
|
1037
|
+
the simulation result.
|
|
1038
|
+
- **Unsupported capability or budget refusal:** inspect the plan and deployment
|
|
1039
|
+
catalog. Installing a local extra does not add a hosted capability.
|
|
1040
|
+
- **Authentication errors:** confirm the API address and that the key belongs
|
|
1041
|
+
to the intended deployment and organization.
|
|
1042
|
+
|
|
1043
|
+
### Job retries
|
|
1044
|
+
|
|
1045
|
+
New simulations default to three attempts. `max_attempts` accepts 1–10 when
|
|
1046
|
+
submitting a job. Failures marked retryable use exponential backoff with jitter
|
|
1047
|
+
(2.5–5 seconds initially, capped at five minutes). Invalid inputs and ambiguous
|
|
1048
|
+
container launch failures stop immediately. Deployment admission pauses wait
|
|
1049
|
+
without consuming an attempt, and expired worker leases cannot execute beyond
|
|
1050
|
+
the attempt budget.
|
|
1051
|
+
|
|
1052
|
+
The job's `summary.retry` records `next_attempt_at` and the most recent 50 failed
|
|
1053
|
+
attempts, including their timestamps and errors. The console shows this history
|
|
1054
|
+
and retry timing on run pages. The jobs menu also lists recent failed jobs,
|
|
1055
|
+
including compiles and failures before run creation, with the same recovery
|
|
1056
|
+
controls. A pending retry clears the earlier verdict and metrics from the live
|
|
1057
|
+
run; those remain available in that attempt's saved artifacts. Logs and artifacts from earlier attempts remain
|
|
1058
|
+
available. A simulation waiting to retry is pending, and cancellation during
|
|
1059
|
+
that wait stops delivery.
|
|
1060
|
+
|
|
1061
|
+
After inspecting a retryable failure, grant that job one additional attempt:
|
|
1062
|
+
|
|
1063
|
+
```python
|
|
1064
|
+
import simhub as sh
|
|
1065
|
+
|
|
1066
|
+
with sh.connect() as sim:
|
|
1067
|
+
logs = sim.job_logs("job_...")
|
|
1068
|
+
job = sim.retry_job(logs.job.id, expected_attempt=logs.job.attempts)
|
|
1069
|
+
```
|
|
1070
|
+
|
|
1071
|
+
The equivalent request is `POST /api/jobs/{job_id}/retry` with
|
|
1072
|
+
`{"expected_attempt": 3}` for a job whose last observed attempt was 3. Repeating
|
|
1073
|
+
the same request does not launch duplicate work or replenish the budget again,
|
|
1074
|
+
even if its response was lost. Stale attempts and cancelled, successful, live,
|
|
1075
|
+
or non-retryable jobs are rejected. An exhausted worker lease requires execution
|
|
1076
|
+
reconciliation first. This retries one job; cancelled downstream jobs and
|
|
1077
|
+
cancelled batches are not restarted.
|
|
1078
|
+
|
|
1079
|
+
|
|
1080
|
+
## Pull, edit, preview, and share
|
|
1081
|
+
|
|
1082
|
+
To edit an environment already stored on the service:
|
|
1083
|
+
|
|
1084
|
+
```bash
|
|
1085
|
+
simhub pull warehouse-pick ./warehouse_pick
|
|
1086
|
+
# Edit the source in ./warehouse_pick.
|
|
1087
|
+
simhub push ./warehouse_pick --message "Update the picking scene"
|
|
1088
|
+
```
|
|
1089
|
+
|
|
1090
|
+
`push` publishes a source version to hosted SimHub, where it is available in the
|
|
1091
|
+
console. To execute it as well, submit a typed request or use
|
|
1092
|
+
`sim.project(...).launch(...)`; publishing source alone does not start a run.
|
|
1093
|
+
|
|
1094
|
+
`pull` accepts an environment name or ID. It downloads the latest version by
|
|
1095
|
+
default; use `--version current`, a version number, or a version ID to select
|
|
1096
|
+
another. The destination must be new or empty. Pull verifies checksums and
|
|
1097
|
+
restores editable source and retained named assets without importing source or
|
|
1098
|
+
installing dependencies. A compiled-only version cannot be pulled as editable
|
|
1099
|
+
Python; an older source version is not silently substituted.
|
|
1100
|
+
|
|
1101
|
+
Keep `.simhub-package` and `.simhub-checkout.json` with the checkout. They retain
|
|
1102
|
+
package identity, API origin, environment, and parent version. Push refuses a
|
|
1103
|
+
stale checkout; pull into another directory and reapply edits. A concurrent
|
|
1104
|
+
push may create a branch with `current: false`. Typed checkouts retain their
|
|
1105
|
+
published definitions and parameters; submit a new typed request to change
|
|
1106
|
+
those parameter values.
|
|
1107
|
+
|
|
1108
|
+
The Python equivalents are `sim.pull("warehouse-pick", into="warehouse_pick")`
|
|
1109
|
+
and `sim.project("warehouse_pick").push("Update the picking scene")`.
|
|
1110
|
+
|
|
1111
|
+
For a factory project with an environment named `warehouse-pick`, publish an
|
|
1112
|
+
initial scene preview without stepping its policy:
|
|
1113
|
+
|
|
1114
|
+
```bash
|
|
1115
|
+
simhub launch ./warehouse_pick --preview --env warehouse-pick --seed 7
|
|
1116
|
+
```
|
|
1117
|
+
|
|
1118
|
+
In Python, use `sim.project(path).preview(env="warehouse-pick", seed=7)`.
|
|
1119
|
+
Preview generation is hosted compilation and requires a compatible GLB compiler.
|
|
1120
|
+
It builds and resets the environment and can incur compute charges. The console
|
|
1121
|
+
shows stored previews and run snapshots without starting another compile merely
|
|
1122
|
+
because you open them.
|
|
1123
|
+
|
|
1124
|
+
Run pages are organization-scoped. When you want to give someone access without
|
|
1125
|
+
an account, explicitly create a revocable, expiring public share:
|
|
1126
|
+
|
|
1127
|
+
```python
|
|
1128
|
+
# Inside the session, with a completed run handle:
|
|
1129
|
+
print(run.share(expires_in_days=7).url)
|
|
1130
|
+
```
|
|
1131
|
+
|
|
1132
|
+
Treat that URL as access to the scoped results. Never put an organization API
|
|
1133
|
+
key in a shared URL.
|
|
1134
|
+
|
|
1135
|
+
## Guidance for coding agents
|
|
1136
|
+
|
|
1137
|
+
Default to hosted submission and console-visible results. Use local simulation
|
|
1138
|
+
only when the user explicitly requests local debugging. Do not replace a hosted
|
|
1139
|
+
run with a local rollout when credentials or hosted capabilities are missing;
|
|
1140
|
+
report the missing prerequisite.
|
|
1141
|
+
|
|
1142
|
+
Use this sequence when implementing an integration:
|
|
1143
|
+
|
|
1144
|
+
1. Check the installed SDK version and choose typed hosted definitions for a
|
|
1145
|
+
new hosted project, or preserve an existing factory project's API.
|
|
1146
|
+
2. Define parameter models, observation/action shapes and units, control rate,
|
|
1147
|
+
required metrics, and task predicates before implementing the runtime.
|
|
1148
|
+
3. Keep every referenced module and asset in the source bundle. Keep runtime
|
|
1149
|
+
construction out of definition imports. Use the hosted runtime's bundled
|
|
1150
|
+
dependencies and declare only additional project-specific libraries.
|
|
1151
|
+
4. Validate definitions without starting a simulator, inspect `sim.plan(...)`
|
|
1152
|
+
for typed requests, and set explicit output and resource limits. Submit a
|
|
1153
|
+
small hosted run before expanding to a batch.
|
|
1154
|
+
5. Persist a stable request ID, submit, and save the receipt. Resume existing
|
|
1155
|
+
work after a timeout or uncertain response instead of creating duplicates.
|
|
1156
|
+
6. Direct the user to the run in the SimHub console. Report execution state,
|
|
1157
|
+
checked task outcome, metrics, and requested artifact delivery separately.
|
|
1158
|
+
Inspect failed stages and saved logs before retrying.
|
|
1159
|
+
7. Freeze cases and seeds for comparisons. Use experiment budgets for typed
|
|
1160
|
+
sweeps, and record the source version, plan, and receipt with results.
|
|
1161
|
+
|
|
1162
|
+
The SDK includes **15 portable agent skills** covering setup, typed runs,
|
|
1163
|
+
scene composition, variations, assets, policies, tasks, debugging, evaluation,
|
|
1164
|
+
recording, Blender, and datasets. Installing the Python package does not
|
|
1165
|
+
activate skills in an agent. Copy complete skill directories from the wheel or
|
|
1166
|
+
repository into your agent's skill folder and reload the agent session. Follow
|
|
1167
|
+
[installation instructions](https://github.com/General-Deployment/sim/blob/main/simhub/skills/README.md#install-for-your-agent)
|
|
1168
|
+
for the copy commands. Start with `$simhub-typed-runs` for hosted work or
|
|
1169
|
+
`$simhub-projects` for factory authoring.
|
|
1170
|
+
|
|
1171
|
+
For exact signatures and additional examples:
|
|
1172
|
+
|
|
1173
|
+
- [Typed contracts](https://github.com/General-Deployment/sim/blob/main/simhub/src/simhub/contracts.py)
|
|
1174
|
+
and [runtime base classes](https://github.com/General-Deployment/sim/blob/main/simhub/src/simhub/base.py).
|
|
1175
|
+
- [Hosted plans, receipts, cancellation, and rerendering](https://github.com/General-Deployment/sim/blob/main/docs/SDK_HOSTED_RECOVERY.md).
|
|
1176
|
+
- [Parameter schema generation](https://github.com/General-Deployment/sim/blob/main/docs/SDK_TYPED_PARAMETERS.md)
|
|
1177
|
+
from an immutable published version.
|
|
1178
|
+
- [Agent skill index](https://github.com/General-Deployment/sim/blob/main/simhub/skills/README.md)
|
|
1179
|
+
with focused workflows and implementation limits.
|
|
1180
|
+
- [Repository examples](https://github.com/General-Deployment/sim/tree/main/examples),
|
|
1181
|
+
including robotics, Blender sets, and the specialized
|
|
1182
|
+
[single-file ceiling simulation](https://github.com/General-Deployment/sim/blob/main/examples/asbestos_ceiling.py).
|
|
1183
|
+
|
|
1184
|
+
## Optional local debugging
|
|
1185
|
+
|
|
1186
|
+
Local execution is available when explicitly needed and requires the project's
|
|
1187
|
+
engine dependencies on your machine. For factory projects, `simhub launch
|
|
1188
|
+
PROJECT --check` runs scene checks and `simhub launch PROJECT --episode` runs a
|
|
1189
|
+
local episode. In Python, `sh.check(...)` checks a scene and the top-level
|
|
1190
|
+
`sh.run(...)` executes registered factories locally. These operations do not
|
|
1191
|
+
publish runs to the console; use the hosted submission APIs above for that.
|
|
1192
|
+
To enable `project.run_local(...)` through a session, explicitly opt in with
|
|
1193
|
+
`sh.connect(hosted_only=False)`. The default connection rejects local execution,
|
|
1194
|
+
including the deprecated `project.run(...)` alias.
|
|
1195
|
+
The [local debugging guide](https://github.com/General-Deployment/sim/blob/main/simhub/skills/simhub-local-debugging/SKILL.md)
|
|
1196
|
+
documents setup, registration, camera checks, and local output files.
|