simulo 0.29.0__tar.gz → 0.30.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. {simulo-0.29.0/src/simulo.egg-info → simulo-0.30.0}/PKG-INFO +2 -2
  2. {simulo-0.29.0 → simulo-0.30.0}/pyproject.toml +2 -2
  3. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/__init__.py +10 -3
  4. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/app.py +91 -13
  5. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/cli.py +335 -60
  6. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/export_api.py +1 -1
  7. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/facades.py +31 -4
  8. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/learning.py +17 -6
  9. simulo-0.29.0/src/simulo/_client/export_bundle.py → simulo-0.30.0/src/simulo/_client/onnx_bundle.py +84 -82
  10. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/preflight_render.py +4 -0
  11. simulo-0.30.0/src/simulo/_client/preview_report.py +322 -0
  12. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/submit_api.py +1 -1
  13. simulo-0.30.0/src/simulo/_client/templates/training/play.py.tmpl +26 -0
  14. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/templates/training/task.py.tmpl +16 -0
  15. {simulo-0.29.0 → simulo-0.30.0/src/simulo.egg-info}/PKG-INFO +2 -2
  16. {simulo-0.29.0 → simulo-0.30.0}/src/simulo.egg-info/SOURCES.txt +3 -1
  17. {simulo-0.29.0 → simulo-0.30.0}/src/simulo.egg-info/requires.txt +1 -1
  18. {simulo-0.29.0 → simulo-0.30.0}/MANIFEST.in +0 -0
  19. {simulo-0.29.0 → simulo-0.30.0}/PYPI.md +0 -0
  20. {simulo-0.29.0 → simulo-0.30.0}/setup.cfg +0 -0
  21. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/__init__.py +0 -0
  22. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/_arguments.py +0 -0
  23. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/_mounts.py +0 -0
  24. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/_runner.py +0 -0
  25. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/_secure_downloads.py +0 -0
  26. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/asset.py +0 -0
  27. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/asset_api.py +0 -0
  28. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/asset_package.py +0 -0
  29. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/asset_pins.py +0 -0
  30. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/builtin_aliases.py +0 -0
  31. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/bundle.py +0 -0
  32. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/cancel_api.py +0 -0
  33. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/config.py +0 -0
  34. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/credentials.py +0 -0
  35. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/discovery.py +0 -0
  36. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/http.py +0 -0
  37. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/identity_api.py +0 -0
  38. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/install_samples.py +0 -0
  39. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/jobs_api.py +0 -0
  40. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/login.py +0 -0
  41. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/mode.py +0 -0
  42. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/outputs.py +0 -0
  43. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/packaging.py +0 -0
  44. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/policies_api.py +0 -0
  45. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/preflight_api.py +0 -0
  46. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/registry.py +0 -0
  47. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/runtime.py +0 -0
  48. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/runtime_display.py +0 -0
  49. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/stub.py +0 -0
  50. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/templates/__init__.py +0 -0
  51. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/templates/training/simuloignore.tmpl +0 -0
  52. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/templates/training/train.py.tmpl +0 -0
  53. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/view_fragment.py +0 -0
  54. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/view_session_api.py +0 -0
  55. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/_client/volume.py +0 -0
  56. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/callbacks.py +0 -0
  57. {simulo-0.29.0 → simulo-0.30.0}/src/simulo/py.typed +0 -0
  58. {simulo-0.29.0 → simulo-0.30.0}/src/simulo.egg-info/dependency_links.txt +0 -0
  59. {simulo-0.29.0 → simulo-0.30.0}/src/simulo.egg-info/entry_points.txt +0 -0
  60. {simulo-0.29.0 → simulo-0.30.0}/src/simulo.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simulo
3
- Version: 0.29.0
3
+ Version: 0.30.0
4
4
  Summary: Python SDK and CLI for cloud-native robotics simulation, reinforcement learning, and robot policy training on managed GPUs.
5
5
  Author-email: Simulo Team <team@simulo.ai>
6
6
  License: BSD-3-Clause
@@ -31,7 +31,7 @@ Classifier: Topic :: System :: Distributed Computing
31
31
  Classifier: Typing :: Typed
32
32
  Requires-Python: >=3.11
33
33
  Description-Content-Type: text/markdown
34
- Requires-Dist: simulo-interfaces<0.22,>=0.21.0
34
+ Requires-Dist: simulo-interfaces<0.23,>=0.22.0
35
35
  Requires-Dist: mcap<2,>=1.3
36
36
  Requires-Dist: defusedxml>=0.7.1
37
37
  Requires-Dist: usd-core<27,>=26.5
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "simulo"
7
- version = "0.29.0"
7
+ version = "0.30.0"
8
8
  description = "Python SDK and CLI for cloud-native robotics simulation, reinforcement learning, and robot policy training on managed GPUs."
9
9
  readme = "PYPI.md"
10
10
  requires-python = ">=3.11"
@@ -59,7 +59,7 @@ classifiers = [
59
59
  # mcap: used by `simulo recordings` to read back a downloaded recording and
60
60
  # verify it (message count) — MIT-licensed, deps only lz4+zstandard.
61
61
  dependencies = [
62
- "simulo-interfaces>=0.21.0,<0.22",
62
+ "simulo-interfaces>=0.22.0,<0.23",
63
63
  "mcap>=1.3,<2",
64
64
  # Hardened XML parsing for URDF ingestion (USD Asset Catalogs, PR-12 fix
65
65
  # loop 2, security NIT): stdlib xml.etree.ElementTree relies on
@@ -92,6 +92,7 @@ from simulo.interfaces import (
92
92
  OrganizationId,
93
93
  OutputPublicId,
94
94
  PackageId,
95
+ PolicyNetworkProtocol,
95
96
  PolicyPublicId,
96
97
  ProjectId,
97
98
  RecordingPublicId,
@@ -141,6 +142,7 @@ from simulo.interfaces.authoring import (
141
142
  GroundPlane,
142
143
  LightConfig,
143
144
  PointMarker,
145
+ PolicyCheckpoint,
144
146
  PrimitiveShape,
145
147
  RigidPhysics,
146
148
  RoughTerrain,
@@ -185,6 +187,7 @@ __all__ = [
185
187
  "JobType",
186
188
  "JobPublicId",
187
189
  "PolicyPublicId",
190
+ "PolicyNetworkProtocol",
188
191
  "RecordingPublicId",
189
192
  "OutputPublicId",
190
193
  "parse_job_public_id",
@@ -251,7 +254,8 @@ __all__ = [
251
254
  "ParallelGripperActuator",
252
255
  "Physics",
253
256
  "Player",
254
- "Policy",
257
+ "PolicyCheckpoint",
258
+ "PolicyNetwork",
255
259
  "Pose",
256
260
  "Prop",
257
261
  "RLPlayer",
@@ -273,6 +277,7 @@ __all__ = [
273
277
  "Trainer",
274
278
  "Visual",
275
279
  "World",
280
+ "preview",
276
281
  "save_output",
277
282
  ]
278
283
 
@@ -288,6 +293,7 @@ def __getattr__(name: str) -> object:
288
293
  if name in _LEARNING_NAMES:
289
294
  return _resolve_learning(name)
290
295
  removed_hints = {
296
+ "Policy": "simulo.Policy was removed. Load a checkpoint with checkpoint.load(), which returns a simulo.PolicyNetwork.",
291
297
  "Scenario": "simulo.Scenario was removed. Use a typed @app.job instead.",
292
298
  "run": "simulo.run was removed. Use `simulo run train.py` instead.",
293
299
  }
@@ -375,11 +381,12 @@ if TYPE_CHECKING:
375
381
  from simulo._client.facades import RLPlayer as RLPlayer
376
382
  from simulo._client.facades import RLTrainer as RLTrainer
377
383
  from simulo._client.facades import Robot as Robot
384
+ from simulo._client.facades import preview as preview
378
385
  from simulo.interfaces.authoring import SceneProtocol
379
386
  from simulo.interfaces.runtime import (
380
387
  ObservationComponentProtocol,
381
388
  PlayerProtocol,
382
- PolicyProtocol,
389
+ PolicyNetworkProtocol,
383
390
  RewardComponentProtocol,
384
391
  TaskProtocol,
385
392
  TerminationConditionProtocol,
@@ -389,7 +396,7 @@ if TYPE_CHECKING:
389
396
  # Subclassable names alias their Protocol so ``class X(simulo.Task)`` is checked
390
397
  # against ``TaskProtocol``.
391
398
  Task = TaskProtocol
392
- Policy = PolicyProtocol
399
+ PolicyNetwork = PolicyNetworkProtocol
393
400
  Player = PlayerProtocol
394
401
  Trainer = TrainerProtocol
395
402
  ObservationComponent = ObservationComponentProtocol
@@ -23,9 +23,11 @@ import atexit
23
23
  import contextlib
24
24
  import functools
25
25
  import hashlib
26
+ import inspect
26
27
  import io
27
28
  import json
28
29
  import os
30
+ import re
29
31
  import shlex
30
32
  import sys
31
33
  import types
@@ -157,6 +159,7 @@ def viewstream_requested(enabled: bool) -> Iterator[None]:
157
159
  # ---------------------------------------------------------------------------
158
160
 
159
161
  _INPUT_CHECKPOINT_REF: Optional[str] = None
162
+ _INPUT_CHECKPOINT_FLAG = "--from"
160
163
 
161
164
 
162
165
  class InputCheckpointRequiresCloudError(RuntimeError):
@@ -171,15 +174,18 @@ class InputCheckpointRequiresCloudError(RuntimeError):
171
174
 
172
175
 
173
176
  @contextlib.contextmanager
174
- def input_checkpoint_requested(checkpoint_ref: Optional[str]) -> Iterator[None]:
175
- """Scope ``--from``'s checkpoint reference for one ``simulo run`` submit call."""
176
- global _INPUT_CHECKPOINT_REF
177
+ def input_checkpoint_requested(checkpoint_ref: Optional[str], *, flag: str = "--from") -> Iterator[None]:
178
+ """Scope one checkpoint flag's reference for one ``simulo run`` submit call."""
179
+ global _INPUT_CHECKPOINT_REF, _INPUT_CHECKPOINT_FLAG
177
180
  previous = _INPUT_CHECKPOINT_REF
181
+ previous_flag = _INPUT_CHECKPOINT_FLAG
178
182
  _INPUT_CHECKPOINT_REF = checkpoint_ref
183
+ _INPUT_CHECKPOINT_FLAG = flag
179
184
  try:
180
185
  yield
181
186
  finally:
182
187
  _INPUT_CHECKPOINT_REF = previous
188
+ _INPUT_CHECKPOINT_FLAG = previous_flag
183
189
 
184
190
 
185
191
  # ---------------------------------------------------------------------------
@@ -283,15 +289,16 @@ class PreflightBlockedError(RuntimeError):
283
289
 
284
290
 
285
291
  def _require_no_input_checkpoint_for_local_submit(reason: str) -> None:
286
- """Refuse a local-disk submit that carries an ambient ``--from`` checkpoint."""
292
+ """Refuse a local-disk submit that carries an ambient checkpoint reference."""
287
293
  if _INPUT_CHECKPOINT_REF is None:
288
294
  return
295
+ retry = "" if _INPUT_CHECKPOINT_FLAG == "--policy" else f", or drop {_INPUT_CHECKPOINT_FLAG}"
289
296
  raise InputCheckpointRequiresCloudError(
290
- f"--from {_INPUT_CHECKPOINT_REF!r} needs a cloud control plane to resolve the checkpoint, but "
291
- f"{reason}. A local-disk submit only writes the package and cannot continue from a policy "
292
- "checkpoint, so the run would silently start fresh instead. Log in first "
297
+ f"{_INPUT_CHECKPOINT_FLAG} {_INPUT_CHECKPOINT_REF!r} needs a cloud control plane to resolve the checkpoint, but "
298
+ f"{reason}. A local-disk submit only writes the package and cannot use a policy "
299
+ "checkpoint, so the run would not honor the selected policy. Log in first "
293
300
  "(`simulo login`) or point SIMULO_API_URL/SIMULO_ENV at a control plane (and unset "
294
- "SIMULO_SUBMIT=local), or drop --from for a fresh local run."
301
+ f"SIMULO_SUBMIT=local){retry}."
295
302
  )
296
303
 
297
304
 
@@ -304,6 +311,7 @@ class JobSpec:
304
311
  qualname: str
305
312
  fn: Callable[..., object]
306
313
  job_type: JobType
314
+ policy_param: Optional[str] = None
307
315
  resources: dict[str, Any] = field(default_factory=dict)
308
316
  timeout: Optional[int] = None
309
317
  retries: int = 0
@@ -311,6 +319,26 @@ class JobSpec:
311
319
  mounts: dict[str, Mount] = field(default_factory=dict)
312
320
 
313
321
 
322
+ _POLICY_CHECKPOINT_ANNOTATION = re.compile(r"\bPolicyCheckpoint\b")
323
+
324
+
325
+ def _is_policy_checkpoint_annotation(annotation: object) -> bool:
326
+ """Whether an annotation names the runner-injected checkpoint type."""
327
+ if annotation is inspect.Parameter.empty:
328
+ return False
329
+ text = annotation if isinstance(annotation, str) else repr(annotation)
330
+ return bool(_POLICY_CHECKPOINT_ANNOTATION.search(text))
331
+
332
+
333
+ def _policy_checkpoint_declaration_error(func: Callable[..., object], job_type: JobType) -> JobDeclarationError:
334
+ label = "Play" if job_type is JobType.PLAY else "Eval"
335
+ return JobDeclarationError(
336
+ f"{label} job {func.__name__!r} in {Path(func.__code__.co_filename).name} must take one "
337
+ "simulo.PolicyCheckpoint parameter, for example `def play(policy: simulo.PolicyCheckpoint, "
338
+ "episodes: int = 3)`."
339
+ )
340
+
341
+
314
342
  def _app_source_location(module_name: str) -> tuple[Path, Path]:
315
343
  """Resolve the on-disk app file (and its directory) for a job's module.
316
344
 
@@ -361,7 +389,7 @@ def _skip_preflight_hint(blocking: "Sequence[PreflightFinding]") -> str:
361
389
  docstring rule ("a blocking finding can never become an unbypassable
362
390
  wall") is unchanged.
363
391
 
364
- ``runtime_not_available`` is the one exception, and only the ADVICE
392
+ ``runtime_not_available`` is one of two exceptions, and only the ADVICE
365
393
  changes, not the flag's behavior. The generic hint's two stated reasons
366
394
  to bypass are "preflight is wrong about your app" and "the platform is
367
395
  unreachable" — this finding is neither: it fires only when the
@@ -388,8 +416,17 @@ def _skip_preflight_hint(blocking: "Sequence[PreflightFinding]") -> str:
388
416
  the whole raise even when an ordinary, safely-bypassable finding is also
389
417
  blocking alongside it — bypassing would still re-enable that finding's
390
418
  harm too.
419
+
420
+ ``intake_paused`` comes first, ahead of ``runtime_not_available``:
421
+ while Simulo staff have paused job intake, ``--skip-preflight`` only
422
+ uploads the package before ``create_job`` refuses with the same
423
+ ``intake_paused``, so the hint says to try again later and never
424
+ names the flag.
391
425
  """
392
- from simulo._client.preflight_render import RUNTIME_NOT_AVAILABLE_CODE
426
+ from simulo._client.preflight_render import INTAKE_PAUSED_CODE, RUNTIME_NOT_AVAILABLE_CODE
427
+
428
+ if any(finding.code == INTAKE_PAUSED_CODE for finding in blocking):
429
+ return "Simulo has paused new jobs, so skipping preflight won't help until intake resumes; try again later."
393
430
 
394
431
  if any(finding.code == RUNTIME_NOT_AVAILABLE_CODE for finding in blocking):
395
432
  return (
@@ -828,6 +865,20 @@ class JobFunction:
828
865
  return self.spawn(**kwargs).job_id
829
866
 
830
867
 
868
+ def _policy_checkpoint_parameters(fn: Callable[..., object]) -> list[str]:
869
+ """Names of ``fn``'s parameters annotated with ``simulo.PolicyCheckpoint``.
870
+
871
+ Matched on the annotation's text, not its identity: the annotation may be a
872
+ string (``from __future__ import annotations``) that discovery never
873
+ evaluates, and an ``Optional[...]`` or ``| None`` spelling still names it.
874
+ """
875
+ names = []
876
+ for parameter in inspect.signature(fn).parameters.values():
877
+ if _is_policy_checkpoint_annotation(parameter.annotation):
878
+ names.append(parameter.name)
879
+ return names
880
+
881
+
831
882
  def _cli_redirect_message() -> str:
832
883
  """The ONE stderr message for a direct ``python app.py`` run — both guard paths.
833
884
 
@@ -1327,12 +1378,34 @@ class App:
1327
1378
  if system is not None:
1328
1379
  resources["system"] = system.value
1329
1380
  resources.update(extra)
1381
+ policy_params = [
1382
+ name
1383
+ for name, parameter in inspect.signature(func).parameters.items()
1384
+ if _is_policy_checkpoint_annotation(parameter.annotation)
1385
+ ]
1386
+ policy_param: Optional[str] = None
1387
+ if resolved_job_type in (JobType.PLAY, JobType.EVAL):
1388
+ if len(policy_params) != 1:
1389
+ raise _policy_checkpoint_declaration_error(func, resolved_job_type)
1390
+ policy_param = policy_params[0]
1391
+ policy_parameter = inspect.signature(func).parameters[policy_param]
1392
+ if policy_parameter.default is not inspect.Parameter.empty or policy_parameter.kind not in (
1393
+ inspect.Parameter.POSITIONAL_OR_KEYWORD,
1394
+ inspect.Parameter.KEYWORD_ONLY,
1395
+ ):
1396
+ raise _policy_checkpoint_declaration_error(func, resolved_job_type)
1397
+ elif policy_params and resolved_job_type is not JobType.PREVIEW:
1398
+ raise JobDeclarationError(
1399
+ f"{resolved_job_type.value.title()} job {func.__name__!r} in {location} cannot take a "
1400
+ "simulo.PolicyCheckpoint parameter. Only play and eval jobs receive a policy checkpoint."
1401
+ )
1330
1402
  spec = JobSpec(
1331
1403
  name=func.__name__,
1332
1404
  module=func.__module__,
1333
1405
  qualname=func.__qualname__,
1334
1406
  fn=func,
1335
1407
  job_type=resolved_job_type,
1408
+ policy_param=policy_param,
1336
1409
  resources=resources,
1337
1410
  timeout=timeout,
1338
1411
  retries=retries,
@@ -1393,11 +1466,16 @@ class App:
1393
1466
  )
1394
1467
 
1395
1468
  if spec.job_type is JobType.PREVIEW:
1396
- raise JobDeclarationError("preview isn't available yet. Use a train job for now.")
1469
+ policy_parameters = _policy_checkpoint_parameters(spec.fn)
1470
+ if policy_parameters:
1471
+ names = ", ".join(policy_parameters)
1472
+ raise JobDeclarationError(
1473
+ f"Preview job {job_name!r} in {where} has a simulo.PolicyCheckpoint parameter ({names}), "
1474
+ "but a preview job takes no policy. Remove it; a policy goes to a play or eval file "
1475
+ "with --policy."
1476
+ )
1397
1477
  if spec.job_type is JobType.EVAL:
1398
1478
  raise JobDeclarationError("evaluation isn't available yet. Use a train job for now.")
1399
- if spec.job_type is JobType.PLAY:
1400
- raise JobDeclarationError("play isn't available yet. It arrives with --policy.")
1401
1479
  if spec.job_type is JobType.ASSET_VALIDATION:
1402
1480
  raise JobDeclarationError("Asset validation runs when you publish an asset: simulo asset publish.")
1403
1481
  if spec.job_type is JobType.EXPORT: