simulo 0.26.0__py3-none-any.whl
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.
- simulo/__init__.py +433 -0
- simulo/_client/__init__.py +6 -0
- simulo/_client/_entrypoint.py +313 -0
- simulo/_client/_mounts.py +25 -0
- simulo/_client/_runner.py +186 -0
- simulo/_client/_secure_downloads.py +1181 -0
- simulo/_client/app.py +1308 -0
- simulo/_client/asset.py +331 -0
- simulo/_client/asset_api.py +517 -0
- simulo/_client/asset_package.py +1103 -0
- simulo/_client/asset_pins.py +187 -0
- simulo/_client/builtin_aliases.py +107 -0
- simulo/_client/bundle.py +254 -0
- simulo/_client/cancel_api.py +104 -0
- simulo/_client/cli.py +9063 -0
- simulo/_client/config.py +186 -0
- simulo/_client/credentials.py +210 -0
- simulo/_client/discovery.py +214 -0
- simulo/_client/export_api.py +212 -0
- simulo/_client/export_bundle.py +296 -0
- simulo/_client/facades.py +581 -0
- simulo/_client/http.py +414 -0
- simulo/_client/identity_api.py +117 -0
- simulo/_client/install_samples.py +267 -0
- simulo/_client/jobs_api.py +224 -0
- simulo/_client/learning.py +393 -0
- simulo/_client/login.py +319 -0
- simulo/_client/mode.py +29 -0
- simulo/_client/outputs.py +116 -0
- simulo/_client/packaging.py +445 -0
- simulo/_client/preflight_api.py +186 -0
- simulo/_client/preflight_render.py +200 -0
- simulo/_client/registry.py +98 -0
- simulo/_client/runtime.py +185 -0
- simulo/_client/runtime_display.py +90 -0
- simulo/_client/seed_ref.py +76 -0
- simulo/_client/stub.py +41 -0
- simulo/_client/submit_api.py +1057 -0
- simulo/_client/templates/__init__.py +21 -0
- simulo/_client/templates/inference/app.py.tmpl +316 -0
- simulo/_client/templates/inference/simuloignore.tmpl +30 -0
- simulo/_client/templates/scenario/app.py.tmpl +93 -0
- simulo/_client/templates/scenario/simuloignore.tmpl +27 -0
- simulo/_client/templates/training/app.py.tmpl +235 -0
- simulo/_client/templates/training/simuloignore.tmpl +29 -0
- simulo/_client/view_fragment.py +21 -0
- simulo/_client/view_session_api.py +122 -0
- simulo/_client/volume.py +71 -0
- simulo/callbacks.py +274 -0
- simulo/py.typed +0 -0
- simulo-0.26.0.dist-info/METADATA +130 -0
- simulo-0.26.0.dist-info/RECORD +55 -0
- simulo-0.26.0.dist-info/WHEEL +5 -0
- simulo-0.26.0.dist-info/entry_points.txt +2 -0
- simulo-0.26.0.dist-info/top_level.txt +1 -0
simulo/callbacks.py
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"""Job lifecycle callbacks (``simulo.callbacks``).
|
|
2
|
+
|
|
3
|
+
Each callback is a structural ``JobCallbackProtocol`` implementation. In the thin
|
|
4
|
+
client the lifecycle hooks are intentionally no-ops — a callback DECLARES intent
|
|
5
|
+
and serialises its configuration into the package manifest via
|
|
6
|
+
:meth:`manifest_entry`; the platform honors it at execution. Today the backend
|
|
7
|
+
runner honors ``ResumableCheckpoint`` (it exports the ``SIMULO_CHECKPOINT_*`` /
|
|
8
|
+
``SIMULO_RESUME`` execution envelope that ``RLTrainer.train`` picks up for
|
|
9
|
+
periodic, resumable checkpointing) and ``DebugOnAnomaly`` (it exports the
|
|
10
|
+
``SIMULO_ANOMALY_DEBUG`` / ``SIMULO_ANOMALY_DEBUG_DIR`` execution envelope that
|
|
11
|
+
``RLTrainer`` reads when constructed without an explicit ``debug_on_anomaly``);
|
|
12
|
+
``CommitVolume`` is still recorded-only, resolved by the cloud worker in a
|
|
13
|
+
follow-up.
|
|
14
|
+
|
|
15
|
+
``DebugOnAnomaly`` here shares one field vocabulary with the runtime config
|
|
16
|
+
:class:`simulo.interfaces.runtime.DebugOnAnomaly` that ``RLTrainer`` consumes;
|
|
17
|
+
:meth:`DebugOnAnomaly.to_runtime_config` and
|
|
18
|
+
:meth:`DebugOnAnomaly.from_manifest_config` are the explicit
|
|
19
|
+
declaration → execution mapping. ``from_manifest_config`` delegates to the
|
|
20
|
+
contract's own :meth:`simulo.interfaces.runtime.DebugOnAnomaly.from_config`
|
|
21
|
+
classmethod rather than re-implementing the config → dataclass mapping.
|
|
22
|
+
Torch-free by construction: this module imports only the (torch-free)
|
|
23
|
+
``simulo-interfaces`` contract, never ``simulo-backend``.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import dataclasses
|
|
29
|
+
from typing import TYPE_CHECKING, Any, Mapping, Optional, Sequence
|
|
30
|
+
|
|
31
|
+
from simulo.interfaces.runtime.anomaly import DebugOnAnomaly as _RuntimeDebugOnAnomaly
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from simulo._client.volume import Volume
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class _CallbackBase:
|
|
38
|
+
"""Shared no-op lifecycle hooks + manifest serialisation for callbacks."""
|
|
39
|
+
|
|
40
|
+
def on_job_start(self, job_id: str) -> None: # noqa: D401 - no-op
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
def on_checkpoint(self, checkpoint_uri: str, step: int) -> None:
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
def on_anomaly(self, detail: str) -> None:
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
def on_job_end(self, job_id: str, status: str) -> None:
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
def manifest_entry(self) -> dict[str, Any]:
|
|
53
|
+
"""Serialise this callback for the package manifest.
|
|
54
|
+
|
|
55
|
+
Returns ``{"type": "<ClassName>", "config": {...}}`` — the shape the
|
|
56
|
+
backend runner reads to reconstruct the execution envelope. The config
|
|
57
|
+
dict is JSON-serialisable by construction (plain ints/floats/bools/strs
|
|
58
|
+
or ``None``).
|
|
59
|
+
"""
|
|
60
|
+
return {"type": type(self).__name__, "config": self._manifest_config()}
|
|
61
|
+
|
|
62
|
+
def _manifest_config(self) -> dict[str, Any]:
|
|
63
|
+
"""Per-class configuration payload; subclasses override."""
|
|
64
|
+
return {}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class ResumableCheckpoint(_CallbackBase):
|
|
68
|
+
"""Declare periodic, resumable checkpointing for a job.
|
|
69
|
+
|
|
70
|
+
``every`` is the checkpoint cadence in trainer iterations (the platform
|
|
71
|
+
picks a default when ``None``); ``keep_last`` additionally keeps the last N
|
|
72
|
+
numbered ``iter_<n>.pt`` copies beside ``latest.pt``.
|
|
73
|
+
|
|
74
|
+
Raises:
|
|
75
|
+
ValueError: if ``every`` or ``keep_last`` are provided and < 1 (caught at
|
|
76
|
+
authoring time, before packaging, so the error appears on the author's
|
|
77
|
+
machine rather than on the remote worker).
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def __init__(self, *, every: Optional[int] = None, keep_last: Optional[int] = None) -> None:
|
|
81
|
+
if every is not None and every < 1:
|
|
82
|
+
raise ValueError(f"ResumableCheckpoint.every must be >= 1, got {every!r}.")
|
|
83
|
+
if keep_last is not None and keep_last < 1:
|
|
84
|
+
raise ValueError(f"ResumableCheckpoint.keep_last must be >= 1, got {keep_last!r}.")
|
|
85
|
+
self.every = every
|
|
86
|
+
self.keep_last = keep_last
|
|
87
|
+
|
|
88
|
+
def _manifest_config(self) -> dict[str, Any]:
|
|
89
|
+
return {"every": self.every, "keep_last": self.keep_last}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class CommitVolume(_CallbackBase):
|
|
93
|
+
"""Commit a volume's contents at checkpoint / job-end boundaries.
|
|
94
|
+
|
|
95
|
+
Attach to a job whose body writes into ``volume`` to make those writes
|
|
96
|
+
durable at well-defined points instead of only at process exit.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
volume: The :class:`simulo.Volume` to commit.
|
|
100
|
+
commit_on_checkpoint: Also commit every time the job checkpoints.
|
|
101
|
+
commit_on_end: Commit when the job ends (success or failure).
|
|
102
|
+
|
|
103
|
+
Execution resolution: recorded-only today — the declaration rides the
|
|
104
|
+
package manifest; the cloud worker honors it in a follow-up.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def __init__(self, volume: "Volume", *, commit_on_checkpoint: bool = True, commit_on_end: bool = True) -> None:
|
|
108
|
+
self.volume = volume
|
|
109
|
+
self.commit_on_checkpoint = commit_on_checkpoint
|
|
110
|
+
self.commit_on_end = commit_on_end
|
|
111
|
+
|
|
112
|
+
def _manifest_config(self) -> dict[str, Any]:
|
|
113
|
+
return {
|
|
114
|
+
"volume": self.volume.name,
|
|
115
|
+
"commit_on_checkpoint": self.commit_on_checkpoint,
|
|
116
|
+
"commit_on_end": self.commit_on_end,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class DebugOnAnomaly(_CallbackBase):
|
|
121
|
+
"""Declare anomaly detection + MCAP debug-session capture for a job.
|
|
122
|
+
|
|
123
|
+
Mirrors the runtime config :class:`simulo.interfaces.runtime.DebugOnAnomaly`
|
|
124
|
+
field-for-field — same names, same defaults, same bounds validation (the
|
|
125
|
+
runtime config IS the validator, so an invalid declaration raises
|
|
126
|
+
``ValueError`` on the author's machine, before packaging). See that class
|
|
127
|
+
for the full field/signal semantics; in brief, each anomaly kind maps to a
|
|
128
|
+
distinct failure mode rather than a single threshold:
|
|
129
|
+
|
|
130
|
+
* non-finite loss (``check_losses``) and reward collapse
|
|
131
|
+
(``check_reward_collapse`` with ``collapse_drop_fraction`` /
|
|
132
|
+
``collapse_patience`` / ``collapse_baseline_floor``) at trainer chunk
|
|
133
|
+
boundaries;
|
|
134
|
+
* non-finite rewards, and non-finite or out-of-envelope
|
|
135
|
+
(``observation_limit``) observations, at env steps
|
|
136
|
+
(``check_observations``, every ``check_every_n_steps``).
|
|
137
|
+
|
|
138
|
+
On detection, a bounded MCAP debug session (``window_steps`` of pre-anomaly
|
|
139
|
+
evidence for ``env_indices``, at most ``max_captures`` files) is captured.
|
|
140
|
+
|
|
141
|
+
``video="<camera name>"`` additionally buffers that camera's frames for
|
|
142
|
+
the first watched env and writes them into the same debug MCAP
|
|
143
|
+
(``/debug/window/video`` + a ``foxglove.CompressedVideo`` sibling) so the
|
|
144
|
+
capture shows what the sim *looked like* before the anomaly. Two honest
|
|
145
|
+
costs, documented on the runtime config's docstring in full: the camera
|
|
146
|
+
forces the render pipeline on during training (the env must enable
|
|
147
|
+
cameras), and the frame ring buffer holds up to ``window_steps`` raw RGB
|
|
148
|
+
frames in host memory (``window_steps x H x W x 3`` bytes).
|
|
149
|
+
|
|
150
|
+
The one deliberate field difference from the runtime config: ``output_dir``
|
|
151
|
+
is NOT declarable. Where debug outputs land is decided by the execution
|
|
152
|
+
environment (exactly like checkpoint directories) and supplied at
|
|
153
|
+
resolution time via :meth:`to_runtime_config` / :meth:`from_manifest_config`.
|
|
154
|
+
|
|
155
|
+
Execution resolution: honored by the backend runner. When the selected job
|
|
156
|
+
declares this callback, the runner exports the ``SIMULO_ANOMALY_DEBUG`` /
|
|
157
|
+
``SIMULO_ANOMALY_DEBUG_DIR`` execution envelope before the job runs, and an
|
|
158
|
+
``RLTrainer`` constructed without an explicit ``debug_on_anomaly`` argument
|
|
159
|
+
arms its anomaly monitor from it — declaring the callback is enough; no
|
|
160
|
+
hand-wiring in the job body. Precedence, strongest first: an explicit
|
|
161
|
+
``RLTrainer(debug_on_anomaly=...)`` argument (including an explicit
|
|
162
|
+
``enabled=False``) > an operator ``--env`` override of the envelope > a
|
|
163
|
+
package ``runtime_env``-supplied ``SIMULO_ANOMALY_DEBUG`` (the config key
|
|
164
|
+
is user-tunable; ``SIMULO_ANOMALY_DEBUG_DIR`` is runner-reserved, so a
|
|
165
|
+
package can never choose where captures land) > this declaration >
|
|
166
|
+
nothing. The mapping helpers remain the explicit
|
|
167
|
+
declaration → execution mapping for custom executors:
|
|
168
|
+
``RLTrainer(..., debug_on_anomaly=cb.to_runtime_config())``, or from a
|
|
169
|
+
parsed manifest entry,
|
|
170
|
+
``DebugOnAnomaly.from_manifest_config(entry["config"], output_dir=...)``.
|
|
171
|
+
|
|
172
|
+
Raises:
|
|
173
|
+
ValueError: on out-of-bounds configuration (caught at authoring time,
|
|
174
|
+
before packaging, so the error appears on the author's machine
|
|
175
|
+
rather than on the remote worker).
|
|
176
|
+
"""
|
|
177
|
+
|
|
178
|
+
def __init__(
|
|
179
|
+
self,
|
|
180
|
+
*,
|
|
181
|
+
enabled: bool = True,
|
|
182
|
+
window_steps: int = 256,
|
|
183
|
+
env_indices: Sequence[int] = (0,),
|
|
184
|
+
check_observations: bool = True,
|
|
185
|
+
observation_limit: Optional[float] = 1e6,
|
|
186
|
+
check_every_n_steps: int = 1,
|
|
187
|
+
video: Optional[str] = None,
|
|
188
|
+
check_losses: bool = True,
|
|
189
|
+
check_reward_collapse: bool = True,
|
|
190
|
+
collapse_drop_fraction: float = 0.5,
|
|
191
|
+
collapse_patience: int = 2,
|
|
192
|
+
collapse_baseline_floor: float = 1e-6,
|
|
193
|
+
max_captures: int = 1,
|
|
194
|
+
) -> None:
|
|
195
|
+
# Construct the runtime config immediately: single source of validation
|
|
196
|
+
# + normalisation (env_indices -> tuple), identical to what the trainer
|
|
197
|
+
# enforces. A drift in defaults is locked out by tests.
|
|
198
|
+
self._runtime_config = _RuntimeDebugOnAnomaly(
|
|
199
|
+
enabled=enabled,
|
|
200
|
+
window_steps=window_steps,
|
|
201
|
+
env_indices=env_indices,
|
|
202
|
+
check_observations=check_observations,
|
|
203
|
+
observation_limit=observation_limit,
|
|
204
|
+
check_every_n_steps=check_every_n_steps,
|
|
205
|
+
video=video,
|
|
206
|
+
check_losses=check_losses,
|
|
207
|
+
check_reward_collapse=check_reward_collapse,
|
|
208
|
+
collapse_drop_fraction=collapse_drop_fraction,
|
|
209
|
+
collapse_patience=collapse_patience,
|
|
210
|
+
collapse_baseline_floor=collapse_baseline_floor,
|
|
211
|
+
max_captures=max_captures,
|
|
212
|
+
)
|
|
213
|
+
cfg = self._runtime_config
|
|
214
|
+
self.enabled = cfg.enabled
|
|
215
|
+
self.window_steps = cfg.window_steps
|
|
216
|
+
self.env_indices: tuple[int, ...] = tuple(cfg.env_indices)
|
|
217
|
+
self.check_observations = cfg.check_observations
|
|
218
|
+
self.observation_limit = cfg.observation_limit
|
|
219
|
+
self.check_every_n_steps = cfg.check_every_n_steps
|
|
220
|
+
self.video = cfg.video
|
|
221
|
+
self.check_losses = cfg.check_losses
|
|
222
|
+
self.check_reward_collapse = cfg.check_reward_collapse
|
|
223
|
+
self.collapse_drop_fraction = cfg.collapse_drop_fraction
|
|
224
|
+
self.collapse_patience = cfg.collapse_patience
|
|
225
|
+
self.collapse_baseline_floor = cfg.collapse_baseline_floor
|
|
226
|
+
self.max_captures = cfg.max_captures
|
|
227
|
+
|
|
228
|
+
def _manifest_config(self) -> dict[str, Any]:
|
|
229
|
+
return {
|
|
230
|
+
"enabled": self.enabled,
|
|
231
|
+
"window_steps": self.window_steps,
|
|
232
|
+
"env_indices": list(self.env_indices),
|
|
233
|
+
"check_observations": self.check_observations,
|
|
234
|
+
"observation_limit": self.observation_limit,
|
|
235
|
+
"check_every_n_steps": self.check_every_n_steps,
|
|
236
|
+
"video": self.video,
|
|
237
|
+
"check_losses": self.check_losses,
|
|
238
|
+
"check_reward_collapse": self.check_reward_collapse,
|
|
239
|
+
"collapse_drop_fraction": self.collapse_drop_fraction,
|
|
240
|
+
"collapse_patience": self.collapse_patience,
|
|
241
|
+
"collapse_baseline_floor": self.collapse_baseline_floor,
|
|
242
|
+
"max_captures": self.max_captures,
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
def to_runtime_config(self, *, output_dir: Optional[str] = None) -> _RuntimeDebugOnAnomaly:
|
|
246
|
+
"""The :class:`simulo.interfaces.runtime.DebugOnAnomaly` this declaration maps onto.
|
|
247
|
+
|
|
248
|
+
``output_dir`` is the executor-supplied output directory; ``None``
|
|
249
|
+
keeps the runtime config's default (``runs/debug``).
|
|
250
|
+
"""
|
|
251
|
+
if output_dir is None:
|
|
252
|
+
return self._runtime_config
|
|
253
|
+
return dataclasses.replace(self._runtime_config, output_dir=output_dir)
|
|
254
|
+
|
|
255
|
+
@classmethod
|
|
256
|
+
def from_manifest_config(
|
|
257
|
+
cls, config: Mapping[str, Any], *, output_dir: Optional[str] = None
|
|
258
|
+
) -> _RuntimeDebugOnAnomaly:
|
|
259
|
+
"""Construct the runtime config from a manifest ``config`` payload (executor side).
|
|
260
|
+
|
|
261
|
+
Delegates to the contract's own
|
|
262
|
+
:meth:`simulo.interfaces.runtime.DebugOnAnomaly.from_config` (the
|
|
263
|
+
canonical declaration → execution mapping, added alongside it). Reads
|
|
264
|
+
exactly the keys :meth:`_manifest_config` writes; unknown keys are
|
|
265
|
+
ignored (forward compatibility with newer clients) and value
|
|
266
|
+
validation is the runtime config's own ``__post_init__``. A
|
|
267
|
+
manifest-supplied ``output_dir`` is ignored too — the output
|
|
268
|
+
directory is executor-owned and passed only via the ``output_dir``
|
|
269
|
+
keyword.
|
|
270
|
+
"""
|
|
271
|
+
return _RuntimeDebugOnAnomaly.from_config(config, output_dir=output_dir)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
__all__ = ["ResumableCheckpoint", "CommitVolume", "DebugOnAnomaly"]
|
simulo/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: simulo
|
|
3
|
+
Version: 0.26.0
|
|
4
|
+
Summary: Simulo SDK and CLI — define robotics simulation and training apps in Python and run them on the Simulo cloud.
|
|
5
|
+
Author-email: Simulo Team <team@simulo.ai>
|
|
6
|
+
License: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://simulo.ai
|
|
8
|
+
Keywords: robotics,simulation,reinforcement-learning,simulo
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Typing :: Typed
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: simulo-interfaces<0.20,>=0.19.0
|
|
19
|
+
Requires-Dist: mcap<2,>=1.3
|
|
20
|
+
Requires-Dist: defusedxml>=0.7.1
|
|
21
|
+
Requires-Dist: usd-core<27,>=26.5
|
|
22
|
+
Provides-Extra: assets
|
|
23
|
+
Requires-Dist: usd-core<27,>=26.5; extra == "assets"
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: black==26.5.1; extra == "dev"
|
|
26
|
+
Requires-Dist: isort==8.0.1; extra == "dev"
|
|
27
|
+
Requires-Dist: ruff==0.15.21; extra == "dev"
|
|
28
|
+
Requires-Dist: mypy==2.3.0; extra == "dev"
|
|
29
|
+
Requires-Dist: types-defusedxml==0.7.0.20260504; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest>=8.2; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-timeout==2.4.0; extra == "dev"
|
|
32
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
33
|
+
Requires-Dist: twine>=5.1; extra == "dev"
|
|
34
|
+
Requires-Dist: setuptools>=68; extra == "dev"
|
|
35
|
+
Requires-Dist: wheel; extra == "dev"
|
|
36
|
+
Provides-Extra: release
|
|
37
|
+
Requires-Dist: commitizen>=3.27; extra == "release"
|
|
38
|
+
Requires-Dist: build>=1.2; extra == "release"
|
|
39
|
+
Requires-Dist: twine>=5.1; extra == "release"
|
|
40
|
+
|
|
41
|
+
# simulo
|
|
42
|
+
|
|
43
|
+
The Simulo SDK and CLI — define robotics simulation and training apps in Python,
|
|
44
|
+
submit them to the Simulo cloud, monitor jobs, and retrieve trained models.
|
|
45
|
+
|
|
46
|
+
Simulo is a cloud-first robotics simulation and training platform. Write your app
|
|
47
|
+
against a small, stable Python API; Simulo handles running it.
|
|
48
|
+
|
|
49
|
+
## Install
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install simulo
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Requires Python 3.11+.
|
|
56
|
+
|
|
57
|
+
## Quickstart
|
|
58
|
+
|
|
59
|
+
The fastest way to start: scaffold a runnable app.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
simulo create mybot
|
|
63
|
+
# Created training app in mybot/ (app.py + .simuloignore).
|
|
64
|
+
cd mybot
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`--type` picks the starter shape (default `training`): `training` (a single
|
|
68
|
+
training job), `inference` (train → evaluate → rollout, self-contained), or
|
|
69
|
+
`scenario` (a scripted scene, no learning). No network call and no login
|
|
70
|
+
required — `simulo create` only writes files to your local folder. Edit
|
|
71
|
+
`app.py` for your own robot and task, then see "Log in and submit" below to
|
|
72
|
+
run it.
|
|
73
|
+
|
|
74
|
+
To download a curated collection of runnable projects, use `simulo install samples`.
|
|
75
|
+
It creates `./simulo-samples` as a normal Git clone, so `cd simulo-samples` and
|
|
76
|
+
`cat README.md` open the sample index.
|
|
77
|
+
|
|
78
|
+
### Or write an app by hand
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
# app.py
|
|
82
|
+
import simulo
|
|
83
|
+
|
|
84
|
+
app = simulo.App("hello")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@app.job(timeout=5 * 60)
|
|
88
|
+
def hello(name: str = "world", repeat: int = 3) -> dict:
|
|
89
|
+
for i in range(repeat):
|
|
90
|
+
print(f"[hello] {i + 1}/{repeat}: hello, {name}!")
|
|
91
|
+
return {"greeting": f"hello, {name}", "repeat": repeat}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@app.entrypoint
|
|
95
|
+
def main(name: str = "world", repeat: int = 3) -> None:
|
|
96
|
+
handle = hello.spawn(name=name, repeat=repeat)
|
|
97
|
+
print(f"Submitted job: {handle.job_name}")
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Log in and submit to the cloud
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
simulo login
|
|
104
|
+
simulo run app.py --name robot --repeat 5
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Without logging in, `simulo run` packages the app locally without submitting it.
|
|
108
|
+
|
|
109
|
+
Every cloud submit is fresh — running the same command twice trains twice, never a silent
|
|
110
|
+
no-op. To continue training from an earlier job's checkpoint instead, pass
|
|
111
|
+
`--from <job-ref>[:best|:latest]`. `<job-ref>` is a full job id, matched by exact equality;
|
|
112
|
+
an unambiguous prefix (`>=4` characters, Git-style) works too, but only for the legacy UUID
|
|
113
|
+
form some jobs from before friendly identifiers still carry.
|
|
114
|
+
|
|
115
|
+
## Monitor jobs and retrieve results
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
simulo jobs # your jobs and their statuses
|
|
119
|
+
simulo logs --follow # stream the latest job's logs live
|
|
120
|
+
simulo result # the completed job's result JSON
|
|
121
|
+
simulo models # list / download trained checkpoints (sha256-verified)
|
|
122
|
+
simulo recordings # download MCAP flight recordings
|
|
123
|
+
simulo view # watch the live 3D scene (jobs submitted with --viewstream)
|
|
124
|
+
simulo cancel # cancel a queued or running job
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Commands that take a job id default to the latest job, announcing what they
|
|
128
|
+
resolved to on stderr.
|
|
129
|
+
|
|
130
|
+
Learn more at [simulo.ai](https://simulo.ai).
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
simulo/__init__.py,sha256=_VyDCuGWSWY9GVA02JUXOfW9jYUj-TFAhYb3f_0oLxU,18753
|
|
2
|
+
simulo/callbacks.py,sha256=NOY2jW0eyTpQmqLOk7DtyZnCTA3SjLzzyuWZ-9i3MgU,12754
|
|
3
|
+
simulo/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
simulo/_client/__init__.py,sha256=1gyCv0KXJYydbwbfG2pGptrpoH7gKApqXkWTzn47bec,288
|
|
5
|
+
simulo/_client/_entrypoint.py,sha256=8jUo4nz1ckYBklby4hlPI2GOcVCc4eJp0fw-LhiRBAI,14191
|
|
6
|
+
simulo/_client/_mounts.py,sha256=mWxcnP1Sj5Tlpa282F08OhLAEI82tE7ZDbdyiGNTTDo,833
|
|
7
|
+
simulo/_client/_runner.py,sha256=O8eumXDmMkNhmcarPFjG4G1kL_Q_yxdBMHCBV2q4X2c,7676
|
|
8
|
+
simulo/_client/_secure_downloads.py,sha256=wnLyuDjv2lIZ-MFP56qFhls6IzXxBlxuvE4BOohKKKI,58711
|
|
9
|
+
simulo/_client/app.py,sha256=OGxUXE_mPgAc7874poE4dbyiyPA4QlLIIHM_Rn916ds,63560
|
|
10
|
+
simulo/_client/asset.py,sha256=gj2jl9eMXB4FEzm6855j2FwcZt3zJyWnoTB1wwccMn0,15481
|
|
11
|
+
simulo/_client/asset_api.py,sha256=e-uN7Uf3y_7wxDt9DhYemo-mWcA0mLWaOVIhnK_JjRI,24056
|
|
12
|
+
simulo/_client/asset_package.py,sha256=a_y332c9UNgV1BGtRoM_J2xo3GTb_f0Yyt3y2gecKEQ,50345
|
|
13
|
+
simulo/_client/asset_pins.py,sha256=3YuW98OHHM9DRUYiVT1icJOx2CFKdGHaxrlVO4pfuQo,8622
|
|
14
|
+
simulo/_client/builtin_aliases.py,sha256=Yv1-AeE5WsOE_NhkHnPW7-AjgutAvwtLGi5JogvOKv8,5711
|
|
15
|
+
simulo/_client/bundle.py,sha256=26ovBnIpgfakl8TQSBneuNbh_vRNsRbVG5v0bnEBYd8,13294
|
|
16
|
+
simulo/_client/cancel_api.py,sha256=cgf_OLAhNTa-WnW96t3ihq7lGBW9QAKecxzcZrjUX14,5145
|
|
17
|
+
simulo/_client/cli.py,sha256=DR5EWuBVw8gVxpeeRfDYOkbI9C5E9MJQ5lagy3INVu0,429585
|
|
18
|
+
simulo/_client/config.py,sha256=hPDkCXjwBz7VbwKl-Dt64xn6jdIZFBnHtf6cADrRb1Q,8469
|
|
19
|
+
simulo/_client/credentials.py,sha256=yYnAvKz1ZN6V5PGSX52DVL3-lNLQnV8fv2cit333sro,9337
|
|
20
|
+
simulo/_client/discovery.py,sha256=5jGlDceYUfyp4Cme8rclLJTnGAJLpmxtaPC3bZAu584,9164
|
|
21
|
+
simulo/_client/export_api.py,sha256=I48Eskk42DRdXE4zxjvcmsfynuLVrlrKbOSD-DZ0gsc,9945
|
|
22
|
+
simulo/_client/export_bundle.py,sha256=gZUEasrjW_c_uhxfr6HZZzPby5-jClvSwTF4Z9JP-4Y,14571
|
|
23
|
+
simulo/_client/facades.py,sha256=_iYyDU_22DmkJy_wsqVdwDExcVFchN11LQ3z_BR1bFk,21366
|
|
24
|
+
simulo/_client/http.py,sha256=6iRI29l_y73pzQae9PcLmKBQbkcaRfMjO18qzKu-DGI,20493
|
|
25
|
+
simulo/_client/identity_api.py,sha256=kKVj5N7R7KQwzK2Urr22ki-yixRXbkxKnDh6Gb-IZEo,5571
|
|
26
|
+
simulo/_client/install_samples.py,sha256=Obm3hDerCPGJqEFnHyoXGyjKZcPmHu6vkoJwmYrq940,10416
|
|
27
|
+
simulo/_client/jobs_api.py,sha256=nhu8tsgXj3njFtTt-yDJBNy2yk_sljnw0r-gAkR9J7c,11336
|
|
28
|
+
simulo/_client/learning.py,sha256=JbXx2hNcA-wHP2T79wHDJ6GLBceytVqsc26FcTqrFfg,18880
|
|
29
|
+
simulo/_client/login.py,sha256=yex6B9TPg4LrVCwtOHswagUwB4leerO1TcAto08mDGE,13971
|
|
30
|
+
simulo/_client/mode.py,sha256=WB-Dm3IpEauYQxE4NX3w6f4FPOj5t3GC2dHrNCakyl4,1205
|
|
31
|
+
simulo/_client/outputs.py,sha256=slmw3aAakdPwr-rQsiHD43PgTyHjEOLWvRplN1jb9nw,6160
|
|
32
|
+
simulo/_client/packaging.py,sha256=XZ8pjEMazEVpWktkJlbOxoyT9EhsdQ6My4X4fC74P-g,20895
|
|
33
|
+
simulo/_client/preflight_api.py,sha256=-WcEBdrDllzUaYaxfFKZ_HYNofQVa0Yhk_-z3WfYf_o,8860
|
|
34
|
+
simulo/_client/preflight_render.py,sha256=f4H2mzE2g7xIp-2fk5pVwQl034hKbzZMq4bEVIrlnlA,10284
|
|
35
|
+
simulo/_client/registry.py,sha256=zf0ZQAX37_e5b9S61ulEE0NL6aVlhYFlPiVHJqwR_f8,4670
|
|
36
|
+
simulo/_client/runtime.py,sha256=ABOrfmLQ6sNFviG4dqSSQuyy-pqHB_2Wpiu9CBaaCB8,8762
|
|
37
|
+
simulo/_client/runtime_display.py,sha256=1dDOaesN9qKvqarN6AxInJCXN7wRxm_RQx_UXkXlins,4194
|
|
38
|
+
simulo/_client/seed_ref.py,sha256=M1Mrvm-DPG5ehn9XVrt57JsNOvt0OGi89kBsH4wRVtI,3742
|
|
39
|
+
simulo/_client/stub.py,sha256=jepEvY7Gwzpw0uQgBvSTvki7sMS2RxJddQv2ioHrBLY,1601
|
|
40
|
+
simulo/_client/submit_api.py,sha256=YYLgfourlvFnqTwHoIJBGNsLF-_xH5jj29B3Ge8tRL8,53611
|
|
41
|
+
simulo/_client/view_fragment.py,sha256=ahqQFLzJeNpMhz2hg0ex2O_qD0taWfHGgkljxyxiiHs,638
|
|
42
|
+
simulo/_client/view_session_api.py,sha256=yMgTtyntmAy9orYFOCiFFZDbuK8V_kfgZGDFCheAShI,6398
|
|
43
|
+
simulo/_client/volume.py,sha256=oYmk4mbp0Ys6ghWUqAG_ovlEERBGVM3-1n3MeS_NcnU,2763
|
|
44
|
+
simulo/_client/templates/__init__.py,sha256=je_epSb6DI14y3AByACTGjxlE-a0IAwrgADdbAxOHYw,1199
|
|
45
|
+
simulo/_client/templates/inference/app.py.tmpl,sha256=somjeBYQCN2SS-2UKnx0hA5gZGyMCuXtB8vayfxFc_w,13281
|
|
46
|
+
simulo/_client/templates/inference/simuloignore.tmpl,sha256=vXAa_SxtK3EAlRueJ7iocTU9JotNnEFdtRvq0wGXQts,751
|
|
47
|
+
simulo/_client/templates/scenario/app.py.tmpl,sha256=8VZSz6Jr_P209STv4KbSa1CxiQQlXH2-5lRVrOYK4Nc,3957
|
|
48
|
+
simulo/_client/templates/scenario/simuloignore.tmpl,sha256=OOe3i2mKeq73-eFBRTsvr6tM-qSG-7yuVQFvKUbAp8A,621
|
|
49
|
+
simulo/_client/templates/training/app.py.tmpl,sha256=y3R2KiVBMTFNd7uvBEkUgzFHa_OnPfK_6NfNo8AGULM,10109
|
|
50
|
+
simulo/_client/templates/training/simuloignore.tmpl,sha256=p1SXAnsYrfoSMDqEjm2kc28eFgW2LL8udo5kEpyO6UM,725
|
|
51
|
+
simulo-0.26.0.dist-info/METADATA,sha256=AvYklt7NNVyGB-BNwMIF1T7CGhrrG01URN4grbqTMT8,4582
|
|
52
|
+
simulo-0.26.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
53
|
+
simulo-0.26.0.dist-info/entry_points.txt,sha256=h66ahOv6BFEPtaS8r4QU_C5wpTMLWr3xJYe6QQ8LLkQ,51
|
|
54
|
+
simulo-0.26.0.dist-info/top_level.txt,sha256=qud9mL24_iIiqb0ZMUSRitgwjDv_3zxgVqlWY45swJI,7
|
|
55
|
+
simulo-0.26.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
simulo
|