simulo 0.6.0__tar.gz → 0.7.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.
- simulo-0.7.0/PKG-INFO +190 -0
- simulo-0.7.0/README.md +158 -0
- simulo-0.7.0/pyproject.toml +107 -0
- simulo-0.7.0/src/simulo/__init__.py +154 -0
- simulo-0.7.0/src/simulo/_client/__init__.py +6 -0
- simulo-0.7.0/src/simulo/_client/_entrypoint.py +104 -0
- simulo-0.7.0/src/simulo/_client/_mounts.py +25 -0
- simulo-0.7.0/src/simulo/_client/_runner.py +171 -0
- simulo-0.7.0/src/simulo/_client/app.py +440 -0
- simulo-0.7.0/src/simulo/_client/asset.py +119 -0
- simulo-0.7.0/src/simulo/_client/bundle.py +157 -0
- simulo-0.7.0/src/simulo/_client/cli.py +1076 -0
- simulo-0.7.0/src/simulo/_client/config.py +136 -0
- simulo-0.7.0/src/simulo/_client/credentials.py +175 -0
- simulo-0.7.0/src/simulo/_client/discovery.py +214 -0
- simulo-0.7.0/src/simulo/_client/http.py +191 -0
- simulo-0.7.0/src/simulo/_client/jobs_api.py +160 -0
- simulo-0.7.0/src/simulo/_client/learning.py +189 -0
- simulo-0.7.0/src/simulo/_client/login.py +282 -0
- simulo-0.7.0/src/simulo/_client/manifest.py +56 -0
- simulo-0.7.0/src/simulo/_client/mode.py +29 -0
- simulo-0.7.0/src/simulo/_client/packaging.py +283 -0
- simulo-0.7.0/src/simulo/_client/registry.py +85 -0
- simulo-0.7.0/src/simulo/_client/runtime.py +189 -0
- simulo-0.7.0/src/simulo/_client/stub.py +41 -0
- simulo-0.7.0/src/simulo/_client/submit_api.py +291 -0
- simulo-0.7.0/src/simulo/_client/volume.py +52 -0
- simulo-0.7.0/src/simulo/callbacks.py +249 -0
- simulo-0.7.0/src/simulo.egg-info/PKG-INFO +190 -0
- simulo-0.7.0/src/simulo.egg-info/SOURCES.txt +72 -0
- simulo-0.7.0/src/simulo.egg-info/entry_points.txt +2 -0
- simulo-0.7.0/src/simulo.egg-info/requires.txt +15 -0
- simulo-0.7.0/src/simulo.egg-info/top_level.txt +1 -0
- simulo-0.7.0/tests/test_callbacks_manifest.py +182 -0
- simulo-0.7.0/tests/test_cartpole_e2e.py +203 -0
- simulo-0.7.0/tests/test_cartpole_eval_e2e.py +280 -0
- simulo-0.7.0/tests/test_cartpole_eval_lean_submit.py +166 -0
- simulo-0.7.0/tests/test_cartpole_lean_submit.py +92 -0
- simulo-0.7.0/tests/test_cartpole_resume_e2e.py +244 -0
- simulo-0.7.0/tests/test_cli.py +252 -0
- simulo-0.7.0/tests/test_cli_auth_commands.py +114 -0
- simulo-0.7.0/tests/test_cli_env_preset_error.py +85 -0
- simulo-0.7.0/tests/test_cli_models.py +794 -0
- simulo-0.7.0/tests/test_cli_recordings.py +462 -0
- simulo-0.7.0/tests/test_cli_run_cloud.py +152 -0
- simulo-0.7.0/tests/test_cloud_submit_e2e.py +253 -0
- simulo-0.7.0/tests/test_config.py +121 -0
- simulo-0.7.0/tests/test_credentials.py +313 -0
- simulo-0.7.0/tests/test_discovery.py +77 -0
- simulo-0.7.0/tests/test_discovery_transitive.py +83 -0
- simulo-0.7.0/tests/test_fake_control_plane.py +155 -0
- simulo-0.7.0/tests/test_hello_submit.py +106 -0
- simulo-0.7.0/tests/test_http.py +136 -0
- simulo-0.7.0/tests/test_jobs_api.py +271 -0
- simulo-0.7.0/tests/test_jobs_cli.py +487 -0
- simulo-0.7.0/tests/test_learning_surface.py +450 -0
- simulo-0.7.0/tests/test_learning_typing.py +83 -0
- simulo-0.7.0/tests/test_local_stream_e2e.py +365 -0
- simulo-0.7.0/tests/test_login.py +360 -0
- simulo-0.7.0/tests/test_manifest.py +37 -0
- simulo-0.7.0/tests/test_manifest_pip_dependencies.py +112 -0
- simulo-0.7.0/tests/test_namespace_coexistence_thin.py +24 -0
- simulo-0.7.0/tests/test_no_stray_namespace_init.py +32 -0
- simulo-0.7.0/tests/test_packaging.py +229 -0
- simulo-0.7.0/tests/test_process_retry_e2e.py +150 -0
- simulo-0.7.0/tests/test_protocol_conformance.py +53 -0
- simulo-0.7.0/tests/test_spawn_cloud.py +247 -0
- simulo-0.7.0/tests/test_submit_api.py +279 -0
- simulo-0.7.0/tests/test_submit_end_to_end.py +250 -0
- simulo-0.7.0/tests/test_submit_handle.py +142 -0
- simulo-0.7.0/tests/test_torch_free_import.py +62 -0
- simulo-0.6.0/LICENSE +0 -29
- simulo-0.6.0/PKG-INFO +0 -81
- simulo-0.6.0/README.md +0 -38
- simulo-0.6.0/cli/src/simulo_cli/__init__.py +0 -1
- simulo-0.6.0/cli/src/simulo_cli/__main__.py +0 -6
- simulo-0.6.0/cli/src/simulo_cli/api/__init__.py +0 -1
- simulo-0.6.0/cli/src/simulo_cli/api/client.py +0 -98
- simulo-0.6.0/cli/src/simulo_cli/auth/__init__.py +0 -1
- simulo-0.6.0/cli/src/simulo_cli/auth/credentials.py +0 -57
- simulo-0.6.0/cli/src/simulo_cli/cli.py +0 -33
- simulo-0.6.0/cli/src/simulo_cli/commands/__init__.py +0 -1
- simulo-0.6.0/cli/src/simulo_cli/commands/auth.py +0 -272
- simulo-0.6.0/cli/src/simulo_cli/commands/org.py +0 -29
- simulo-0.6.0/cli/src/simulo_cli/config.py +0 -138
- simulo-0.6.0/pyproject.toml +0 -185
- simulo-0.6.0/sdk/src/simulo/__init__.py +0 -162
- simulo-0.6.0/sdk/src/simulo/backend/__init__.py +0 -1
- simulo-0.6.0/sdk/src/simulo/backend/isaaclab/__init__.py +0 -5
- simulo-0.6.0/sdk/src/simulo/backend/isaaclab/asset_registry.py +0 -365
- simulo-0.6.0/sdk/src/simulo/backend/isaaclab/backend.py +0 -227
- simulo-0.6.0/sdk/src/simulo/backend/isaaclab/scene_builder.py +0 -2237
- simulo-0.6.0/sdk/src/simulo/backend/isaaclab/stream_adapter.py +0 -182
- simulo-0.6.0/sdk/src/simulo/core/__init__.py +0 -88
- simulo-0.6.0/sdk/src/simulo/core/actuator.py +0 -314
- simulo-0.6.0/sdk/src/simulo/core/asset.py +0 -63
- simulo-0.6.0/sdk/src/simulo/core/controller.py +0 -1012
- simulo-0.6.0/sdk/src/simulo/core/entity.py +0 -180
- simulo-0.6.0/sdk/src/simulo/core/env.py +0 -553
- simulo-0.6.0/sdk/src/simulo/core/light.py +0 -84
- simulo-0.6.0/sdk/src/simulo/core/materials.py +0 -61
- simulo-0.6.0/sdk/src/simulo/core/math_utils.py +0 -74
- simulo-0.6.0/sdk/src/simulo/core/observations.py +0 -481
- simulo-0.6.0/sdk/src/simulo/core/physics.py +0 -154
- simulo-0.6.0/sdk/src/simulo/core/player.py +0 -604
- simulo-0.6.0/sdk/src/simulo/core/policy.py +0 -245
- simulo-0.6.0/sdk/src/simulo/core/pose.py +0 -99
- simulo-0.6.0/sdk/src/simulo/core/primitives.py +0 -132
- simulo-0.6.0/sdk/src/simulo/core/rewards.py +0 -798
- simulo-0.6.0/sdk/src/simulo/core/robot.py +0 -1041
- simulo-0.6.0/sdk/src/simulo/core/scene.py +0 -462
- simulo-0.6.0/sdk/src/simulo/core/sensor.py +0 -923
- simulo-0.6.0/sdk/src/simulo/core/task.py +0 -215
- simulo-0.6.0/sdk/src/simulo/core/terminations.py +0 -326
- simulo-0.6.0/sdk/src/simulo/core/terrain.py +0 -380
- simulo-0.6.0/sdk/src/simulo/core/trainer.py +0 -437
- simulo-0.6.0/sdk/src/simulo/core/visual.py +0 -302
- simulo-0.6.0/sdk/src/simulo/core/world.py +0 -138
- simulo-0.6.0/sdk/src/simulo/py.typed +0 -0
- simulo-0.6.0/sdk/src/simulo/scenario.py +0 -196
- simulo-0.6.0/sdk/src/simulo/streaming/__init__.py +0 -31
- simulo-0.6.0/sdk/src/simulo/streaming/adapter.py +0 -209
- simulo-0.6.0/sdk/src/simulo/streaming/config.py +0 -90
- simulo-0.6.0/sdk/src/simulo/streaming/types.py +0 -103
- simulo-0.6.0/simulo.egg-info/PKG-INFO +0 -81
- simulo-0.6.0/simulo.egg-info/SOURCES.txt +0 -59
- simulo-0.6.0/simulo.egg-info/entry_points.txt +0 -2
- simulo-0.6.0/simulo.egg-info/requires.txt +0 -23
- simulo-0.6.0/simulo.egg-info/top_level.txt +0 -2
- {simulo-0.6.0 → simulo-0.7.0}/setup.cfg +0 -0
- {simulo-0.6.0/cli/src/simulo_cli → simulo-0.7.0/src/simulo}/py.typed +0 -0
- {simulo-0.6.0 → simulo-0.7.0/src}/simulo.egg-info/dependency_links.txt +0 -0
simulo-0.7.0/PKG-INFO
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: simulo
|
|
3
|
+
Version: 0.7.0
|
|
4
|
+
Summary: Simulo thin client — the Modal-inspired, torch-free user-facing SDK. Provides simulo.App / Runtime / Volume / Asset, @app.job, the source packager, and the `simulo` CLI. Depends only on simulo-interfaces.
|
|
5
|
+
Author-email: Simulo Team <team@simulo.ai>
|
|
6
|
+
License: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://simulo.ai
|
|
8
|
+
Project-URL: Source, https://github.com/simulo-org/simulo-platform
|
|
9
|
+
Keywords: robotics,simulation,reinforcement-learning,thin-client,simulo
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Typing :: Typed
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: simulo-interfaces<0.2,>=0.1
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: black>=24.0; extra == "dev"
|
|
22
|
+
Requires-Dist: isort>=5.13; extra == "dev"
|
|
23
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
24
|
+
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest>=8.2; extra == "dev"
|
|
26
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
27
|
+
Requires-Dist: twine>=5.1; extra == "dev"
|
|
28
|
+
Provides-Extra: release
|
|
29
|
+
Requires-Dist: commitizen>=3.27; extra == "release"
|
|
30
|
+
Requires-Dist: build>=1.2; extra == "release"
|
|
31
|
+
Requires-Dist: twine>=5.1; extra == "release"
|
|
32
|
+
|
|
33
|
+
# simulo
|
|
34
|
+
|
|
35
|
+
The Simulo **thin client** — the Modal-inspired, user-facing SDK you install with
|
|
36
|
+
`pip install simulo`.
|
|
37
|
+
|
|
38
|
+
This distribution is **platform-only and torch-free**. It provides the cloud/packaging
|
|
39
|
+
surface and depends only on [`simulo-interfaces`](../simulo-interfaces):
|
|
40
|
+
|
|
41
|
+
- `simulo.App`, `simulo.Runtime`, `simulo.Volume`, `simulo.Asset`
|
|
42
|
+
- `@app.job` and `@app.local_entrypoint`
|
|
43
|
+
- `simulo.callbacks` (`ResumableCheckpoint`, `CommitVolume`, `DebugOnAnomaly`)
|
|
44
|
+
- the source **packager** and the `simulo run` CLI (submit)
|
|
45
|
+
|
|
46
|
+
Heavy imports (`torch`, Isaac, …) never live in this package. They are written inside the
|
|
47
|
+
user's `runtime.imports()` guard and resolved only during **execution** — by the
|
|
48
|
+
`simulo-backend` runner today (the local-simulation stand-in for the cloud worker), and by
|
|
49
|
+
the cloud worker eventually.
|
|
50
|
+
|
|
51
|
+
## Submit vs. execute
|
|
52
|
+
|
|
53
|
+
Submit and execute are cleanly separated:
|
|
54
|
+
|
|
55
|
+
| Step | Who | What it does |
|
|
56
|
+
|------|-----|--------------|
|
|
57
|
+
| **Submit** | the user (`simulo run app.py`) | runs `@app.local_entrypoint` in discovery mode (torch-free) → `spawn()`/`submit()` **write the package** (manifest + `source/`) to disk — and, in cloud mode, upload it + create a job on the control plane. NEVER trains, NEVER invokes the backend. |
|
|
58
|
+
| **Execute** | `simulo-backend run-package` (worker/system launcher; the **sole executor**) | imports the packaged source in execution mode and calls the `@app.job` — the real training. Invoked directly for the local simulation, and by `simulo-backend worker` in the cloud. |
|
|
59
|
+
|
|
60
|
+
## Offline vs. cloud mode
|
|
61
|
+
|
|
62
|
+
The SAME `simulo run` serves both; the presence of a cloud target selects the mode:
|
|
63
|
+
|
|
64
|
+
| Mode | When | What `spawn()` does |
|
|
65
|
+
|------|------|---------------------|
|
|
66
|
+
| **Offline (local disk)** | no credentials, no `SIMULO_API_URL`/`SIMULO_ENV` — or `SIMULO_SUBMIT=local` forces it | writes the package to disk; you execute it yourself with `simulo-backend run-package` |
|
|
67
|
+
| **Cloud** | logged in (`simulo login`), or `SIMULO_API_URL`/`SIMULO_ENV` names a target | ALSO tars + uploads the package (`POST /v1/packages` + archive PUT) and creates a job (`POST /v1/jobs`); `simulo run` then follows the job's logs to completion by default (`--detach` to skip), and `JobHandle.get()` polls the platform for the real result |
|
|
68
|
+
|
|
69
|
+
Cloud quickstart (staging):
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
SIMULO_ENV=staging simulo login # OAuth PKCE via browser loopback -> ~/.simulo/credentials (0600)
|
|
73
|
+
simulo run examples/hello/app.py # uploads, creates the job, follows its logs
|
|
74
|
+
simulo result # the job's result JSON
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
A worker must be polling the control plane to execute cloud jobs — on a GPU box:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
SIMULO_API_URL=https://api.staging.simulo.ai SIMULO_WORKER_TOKEN=<token> \
|
|
81
|
+
simulo-backend worker # claim -> download -> run-package -> stream logs -> complete
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The full demo script (staging AND the zero-AWS local stack) is
|
|
85
|
+
`context/verification-steps/cloud-submit-demo-runbook.md`; the automated full-stack
|
|
86
|
+
proof is `tests/integration/` at the repo root (markers `cp_integration` / `cloud_e2e`).
|
|
87
|
+
|
|
88
|
+
This mirrors Modal: the runtime imports the module and calls the function; it does **not**
|
|
89
|
+
run `python app.py`. The job body the author wrote runs unchanged (source-preserving).
|
|
90
|
+
|
|
91
|
+
## Namespace
|
|
92
|
+
|
|
93
|
+
`simulo` is a **hybrid namespace**. The thin client is the only distribution that ships
|
|
94
|
+
`simulo/__init__.py`; it calls `pkgutil.extend_path` so the PEP 420 namespace portions
|
|
95
|
+
shipped by `simulo-backend` (`simulo.core`, `simulo.backend`, `simulo.recording`) and
|
|
96
|
+
`simulo-interfaces` (`simulo.interfaces`) coexist under the same top-level `simulo`.
|
|
97
|
+
|
|
98
|
+
## Modes
|
|
99
|
+
|
|
100
|
+
A single mode-aware package, driven by `SIMULO_MODE`:
|
|
101
|
+
|
|
102
|
+
| Mode | `runtime.imports()` |
|
|
103
|
+
|------|---------------------|
|
|
104
|
+
| `discovery` (default) | installs a stub import finder, records deferred top-level imports, runs no job body — the torch-free **submit** path |
|
|
105
|
+
| `execution` | `contextlib.nullcontext()` — real imports; set explicitly by the `simulo-backend` runner when it **executes** a package |
|
|
106
|
+
|
|
107
|
+
Discovery is the default so any bare `import` of a user app stays torch-free; `simulo
|
|
108
|
+
run` submits on that default. Execution is only ever set by the sole executor
|
|
109
|
+
(`simulo-backend run-package`).
|
|
110
|
+
|
|
111
|
+
## One user command — submit
|
|
112
|
+
|
|
113
|
+
`simulo run` is the canonical — and only — submit command:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
simulo run app.py --num-envs 4096 --max-iterations 200
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
It runs the `@app.local_entrypoint` in discovery mode (torch-free). Its `spawn()` /
|
|
120
|
+
`submit()` calls **write the package** — a content-addressed `<digest>/source/` tree plus
|
|
121
|
+
`<digest>/simulo.manifest.json` — and print where it landed. The manifest records the
|
|
122
|
+
entrypoint, runtime image + env, jobs (resources/timeout/retries/resume), volumes, the
|
|
123
|
+
submitted `args`, and the deferred `remote_imports` (e.g. `["torch"]`). `spawn()` returns
|
|
124
|
+
a `JobHandle` exposing `.package_path` / `.job_id`; it does **not** return a result —
|
|
125
|
+
submit never trains.
|
|
126
|
+
|
|
127
|
+
`simulo run` imports the file as a non-`__main__` module and invokes the registered
|
|
128
|
+
entrypoint explicitly (argparse is built from its signature; `--num-envs` maps to the
|
|
129
|
+
`num_envs` parameter). A direct `python app.py` does **not** submit:
|
|
130
|
+
`@app.local_entrypoint` detects the `__main__` run, prints the exact equivalent
|
|
131
|
+
`simulo run` command to stderr, and exits 2 — so a script wrapping the `python` form
|
|
132
|
+
fails loudly instead of silently not submitting.
|
|
133
|
+
|
|
134
|
+
## Execute (the sole executor)
|
|
135
|
+
|
|
136
|
+
Submit does not train. Executing a package is a separate step owned entirely by the
|
|
137
|
+
`simulo-backend` runner — a worker/system launcher surfaced as a CLI today only for the
|
|
138
|
+
local simulation:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
simulo-backend run-package <package> --job train
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
It imports the same packaged source in execution mode and calls the `@app.job` body —
|
|
145
|
+
resolving the real `torch` / Isaac imports there. (Eventually the cloud worker invokes it
|
|
146
|
+
and `JobHandle.get()` streams the result back; today `get()` raises a friendly
|
|
147
|
+
`JobResultUnavailable` pointing at the package + the execute command.) Executing locally
|
|
148
|
+
therefore needs the GPU runtime installed (`pip install simulo-backend`). See
|
|
149
|
+
[`examples/cartpole`](examples/cartpole/) for a full, GPU-proven submit → execute walkthrough.
|
|
150
|
+
|
|
151
|
+
## Observe — `simulo jobs` / `simulo logs` / `simulo result`
|
|
152
|
+
|
|
153
|
+
Once a package executes, the thin client observes it **over HTTP only** — it never reads
|
|
154
|
+
the executor's run store from disk. Submit/observe and execute behave as if on **two
|
|
155
|
+
separate machines**: eventually a cloud event triggers execution and the client streams
|
|
156
|
+
from the real control plane; locally, *you* are that cloud event, and a deliberately-thin
|
|
157
|
+
stdlib server stands in for the control plane. The wire contract (job-record schema, the
|
|
158
|
+
four `GET /v1/jobs*` endpoints, byte-offset log semantics) is the durable piece — it
|
|
159
|
+
lives in `simulo.interfaces.platform.runs` and survives the move to the real cloud.
|
|
160
|
+
|
|
161
|
+
The two-terminal story on one box:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
# Terminal B — the "pretend cloud" (needs simulo-backend installed):
|
|
165
|
+
simulo-backend serve # local stand-in for the cloud jobs API (127.0.0.1)
|
|
166
|
+
simulo-backend run-package <package> --job train # you are the cloud event; prints SIMULO_JOB_ID=<id> first
|
|
167
|
+
|
|
168
|
+
# Terminal A — the user's machine (torch-free thin client, HTTP only):
|
|
169
|
+
simulo run app.py --num-envs 64 # submit: writes the package (never executes)
|
|
170
|
+
simulo logs <job-id> --follow # stream live training output; exits 0/1 with the job
|
|
171
|
+
simulo result <job-id> # the job's result JSON (404->error until success)
|
|
172
|
+
simulo jobs # table of runs: id, job, status, started, exit
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Notes:
|
|
176
|
+
|
|
177
|
+
- The **job id is printed by `run-package` at execution** (first line,
|
|
178
|
+
`SIMULO_JOB_ID=...`), not at submit — a run only exists once execution starts.
|
|
179
|
+
- `simulo logs <id> --follow` on a job that does not exist *yet* waits for it to appear,
|
|
180
|
+
so you can arm the tail in terminal A before triggering `run-package` in terminal B.
|
|
181
|
+
- Reconnecting is just re-reading: log chunks are addressed by **byte offset**, so a new
|
|
182
|
+
`simulo logs <id>` replays the full stream from 0.
|
|
183
|
+
- `SIMULO_API_URL` points the client at a different API (default
|
|
184
|
+
`http://127.0.0.1:8765`); `SIMULO_API_TOKEN`, when set, is sent as a bearer token.
|
|
185
|
+
With credentials saved by `simulo login`, their token + recorded `api_base_url`
|
|
186
|
+
are used instead. Bearer over plaintext `http://` is refused for any
|
|
187
|
+
non-loopback host (the https-when-token guard).
|
|
188
|
+
- `simulo recordings <job-id> [name|--all] [-o PATH]` downloads a job's MCAP
|
|
189
|
+
recording(s) via a presigned URL (`GET /v1/jobs/{id}/recordings`) — open them in
|
|
190
|
+
Lichtblick or read them with the `mcap` library.
|
simulo-0.7.0/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# simulo
|
|
2
|
+
|
|
3
|
+
The Simulo **thin client** — the Modal-inspired, user-facing SDK you install with
|
|
4
|
+
`pip install simulo`.
|
|
5
|
+
|
|
6
|
+
This distribution is **platform-only and torch-free**. It provides the cloud/packaging
|
|
7
|
+
surface and depends only on [`simulo-interfaces`](../simulo-interfaces):
|
|
8
|
+
|
|
9
|
+
- `simulo.App`, `simulo.Runtime`, `simulo.Volume`, `simulo.Asset`
|
|
10
|
+
- `@app.job` and `@app.local_entrypoint`
|
|
11
|
+
- `simulo.callbacks` (`ResumableCheckpoint`, `CommitVolume`, `DebugOnAnomaly`)
|
|
12
|
+
- the source **packager** and the `simulo run` CLI (submit)
|
|
13
|
+
|
|
14
|
+
Heavy imports (`torch`, Isaac, …) never live in this package. They are written inside the
|
|
15
|
+
user's `runtime.imports()` guard and resolved only during **execution** — by the
|
|
16
|
+
`simulo-backend` runner today (the local-simulation stand-in for the cloud worker), and by
|
|
17
|
+
the cloud worker eventually.
|
|
18
|
+
|
|
19
|
+
## Submit vs. execute
|
|
20
|
+
|
|
21
|
+
Submit and execute are cleanly separated:
|
|
22
|
+
|
|
23
|
+
| Step | Who | What it does |
|
|
24
|
+
|------|-----|--------------|
|
|
25
|
+
| **Submit** | the user (`simulo run app.py`) | runs `@app.local_entrypoint` in discovery mode (torch-free) → `spawn()`/`submit()` **write the package** (manifest + `source/`) to disk — and, in cloud mode, upload it + create a job on the control plane. NEVER trains, NEVER invokes the backend. |
|
|
26
|
+
| **Execute** | `simulo-backend run-package` (worker/system launcher; the **sole executor**) | imports the packaged source in execution mode and calls the `@app.job` — the real training. Invoked directly for the local simulation, and by `simulo-backend worker` in the cloud. |
|
|
27
|
+
|
|
28
|
+
## Offline vs. cloud mode
|
|
29
|
+
|
|
30
|
+
The SAME `simulo run` serves both; the presence of a cloud target selects the mode:
|
|
31
|
+
|
|
32
|
+
| Mode | When | What `spawn()` does |
|
|
33
|
+
|------|------|---------------------|
|
|
34
|
+
| **Offline (local disk)** | no credentials, no `SIMULO_API_URL`/`SIMULO_ENV` — or `SIMULO_SUBMIT=local` forces it | writes the package to disk; you execute it yourself with `simulo-backend run-package` |
|
|
35
|
+
| **Cloud** | logged in (`simulo login`), or `SIMULO_API_URL`/`SIMULO_ENV` names a target | ALSO tars + uploads the package (`POST /v1/packages` + archive PUT) and creates a job (`POST /v1/jobs`); `simulo run` then follows the job's logs to completion by default (`--detach` to skip), and `JobHandle.get()` polls the platform for the real result |
|
|
36
|
+
|
|
37
|
+
Cloud quickstart (staging):
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
SIMULO_ENV=staging simulo login # OAuth PKCE via browser loopback -> ~/.simulo/credentials (0600)
|
|
41
|
+
simulo run examples/hello/app.py # uploads, creates the job, follows its logs
|
|
42
|
+
simulo result # the job's result JSON
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
A worker must be polling the control plane to execute cloud jobs — on a GPU box:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
SIMULO_API_URL=https://api.staging.simulo.ai SIMULO_WORKER_TOKEN=<token> \
|
|
49
|
+
simulo-backend worker # claim -> download -> run-package -> stream logs -> complete
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The full demo script (staging AND the zero-AWS local stack) is
|
|
53
|
+
`context/verification-steps/cloud-submit-demo-runbook.md`; the automated full-stack
|
|
54
|
+
proof is `tests/integration/` at the repo root (markers `cp_integration` / `cloud_e2e`).
|
|
55
|
+
|
|
56
|
+
This mirrors Modal: the runtime imports the module and calls the function; it does **not**
|
|
57
|
+
run `python app.py`. The job body the author wrote runs unchanged (source-preserving).
|
|
58
|
+
|
|
59
|
+
## Namespace
|
|
60
|
+
|
|
61
|
+
`simulo` is a **hybrid namespace**. The thin client is the only distribution that ships
|
|
62
|
+
`simulo/__init__.py`; it calls `pkgutil.extend_path` so the PEP 420 namespace portions
|
|
63
|
+
shipped by `simulo-backend` (`simulo.core`, `simulo.backend`, `simulo.recording`) and
|
|
64
|
+
`simulo-interfaces` (`simulo.interfaces`) coexist under the same top-level `simulo`.
|
|
65
|
+
|
|
66
|
+
## Modes
|
|
67
|
+
|
|
68
|
+
A single mode-aware package, driven by `SIMULO_MODE`:
|
|
69
|
+
|
|
70
|
+
| Mode | `runtime.imports()` |
|
|
71
|
+
|------|---------------------|
|
|
72
|
+
| `discovery` (default) | installs a stub import finder, records deferred top-level imports, runs no job body — the torch-free **submit** path |
|
|
73
|
+
| `execution` | `contextlib.nullcontext()` — real imports; set explicitly by the `simulo-backend` runner when it **executes** a package |
|
|
74
|
+
|
|
75
|
+
Discovery is the default so any bare `import` of a user app stays torch-free; `simulo
|
|
76
|
+
run` submits on that default. Execution is only ever set by the sole executor
|
|
77
|
+
(`simulo-backend run-package`).
|
|
78
|
+
|
|
79
|
+
## One user command — submit
|
|
80
|
+
|
|
81
|
+
`simulo run` is the canonical — and only — submit command:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
simulo run app.py --num-envs 4096 --max-iterations 200
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
It runs the `@app.local_entrypoint` in discovery mode (torch-free). Its `spawn()` /
|
|
88
|
+
`submit()` calls **write the package** — a content-addressed `<digest>/source/` tree plus
|
|
89
|
+
`<digest>/simulo.manifest.json` — and print where it landed. The manifest records the
|
|
90
|
+
entrypoint, runtime image + env, jobs (resources/timeout/retries/resume), volumes, the
|
|
91
|
+
submitted `args`, and the deferred `remote_imports` (e.g. `["torch"]`). `spawn()` returns
|
|
92
|
+
a `JobHandle` exposing `.package_path` / `.job_id`; it does **not** return a result —
|
|
93
|
+
submit never trains.
|
|
94
|
+
|
|
95
|
+
`simulo run` imports the file as a non-`__main__` module and invokes the registered
|
|
96
|
+
entrypoint explicitly (argparse is built from its signature; `--num-envs` maps to the
|
|
97
|
+
`num_envs` parameter). A direct `python app.py` does **not** submit:
|
|
98
|
+
`@app.local_entrypoint` detects the `__main__` run, prints the exact equivalent
|
|
99
|
+
`simulo run` command to stderr, and exits 2 — so a script wrapping the `python` form
|
|
100
|
+
fails loudly instead of silently not submitting.
|
|
101
|
+
|
|
102
|
+
## Execute (the sole executor)
|
|
103
|
+
|
|
104
|
+
Submit does not train. Executing a package is a separate step owned entirely by the
|
|
105
|
+
`simulo-backend` runner — a worker/system launcher surfaced as a CLI today only for the
|
|
106
|
+
local simulation:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
simulo-backend run-package <package> --job train
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
It imports the same packaged source in execution mode and calls the `@app.job` body —
|
|
113
|
+
resolving the real `torch` / Isaac imports there. (Eventually the cloud worker invokes it
|
|
114
|
+
and `JobHandle.get()` streams the result back; today `get()` raises a friendly
|
|
115
|
+
`JobResultUnavailable` pointing at the package + the execute command.) Executing locally
|
|
116
|
+
therefore needs the GPU runtime installed (`pip install simulo-backend`). See
|
|
117
|
+
[`examples/cartpole`](examples/cartpole/) for a full, GPU-proven submit → execute walkthrough.
|
|
118
|
+
|
|
119
|
+
## Observe — `simulo jobs` / `simulo logs` / `simulo result`
|
|
120
|
+
|
|
121
|
+
Once a package executes, the thin client observes it **over HTTP only** — it never reads
|
|
122
|
+
the executor's run store from disk. Submit/observe and execute behave as if on **two
|
|
123
|
+
separate machines**: eventually a cloud event triggers execution and the client streams
|
|
124
|
+
from the real control plane; locally, *you* are that cloud event, and a deliberately-thin
|
|
125
|
+
stdlib server stands in for the control plane. The wire contract (job-record schema, the
|
|
126
|
+
four `GET /v1/jobs*` endpoints, byte-offset log semantics) is the durable piece — it
|
|
127
|
+
lives in `simulo.interfaces.platform.runs` and survives the move to the real cloud.
|
|
128
|
+
|
|
129
|
+
The two-terminal story on one box:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Terminal B — the "pretend cloud" (needs simulo-backend installed):
|
|
133
|
+
simulo-backend serve # local stand-in for the cloud jobs API (127.0.0.1)
|
|
134
|
+
simulo-backend run-package <package> --job train # you are the cloud event; prints SIMULO_JOB_ID=<id> first
|
|
135
|
+
|
|
136
|
+
# Terminal A — the user's machine (torch-free thin client, HTTP only):
|
|
137
|
+
simulo run app.py --num-envs 64 # submit: writes the package (never executes)
|
|
138
|
+
simulo logs <job-id> --follow # stream live training output; exits 0/1 with the job
|
|
139
|
+
simulo result <job-id> # the job's result JSON (404->error until success)
|
|
140
|
+
simulo jobs # table of runs: id, job, status, started, exit
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Notes:
|
|
144
|
+
|
|
145
|
+
- The **job id is printed by `run-package` at execution** (first line,
|
|
146
|
+
`SIMULO_JOB_ID=...`), not at submit — a run only exists once execution starts.
|
|
147
|
+
- `simulo logs <id> --follow` on a job that does not exist *yet* waits for it to appear,
|
|
148
|
+
so you can arm the tail in terminal A before triggering `run-package` in terminal B.
|
|
149
|
+
- Reconnecting is just re-reading: log chunks are addressed by **byte offset**, so a new
|
|
150
|
+
`simulo logs <id>` replays the full stream from 0.
|
|
151
|
+
- `SIMULO_API_URL` points the client at a different API (default
|
|
152
|
+
`http://127.0.0.1:8765`); `SIMULO_API_TOKEN`, when set, is sent as a bearer token.
|
|
153
|
+
With credentials saved by `simulo login`, their token + recorded `api_base_url`
|
|
154
|
+
are used instead. Bearer over plaintext `http://` is refused for any
|
|
155
|
+
non-loopback host (the https-when-token guard).
|
|
156
|
+
- `simulo recordings <job-id> [name|--all] [-o PATH]` downloads a job's MCAP
|
|
157
|
+
recording(s) via a presigned URL (`GET /v1/jobs/{id}/recordings`) — open them in
|
|
158
|
+
Lichtblick or read them with the `mcap` library.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "simulo"
|
|
7
|
+
version = "0.7.0"
|
|
8
|
+
description = "Simulo thin client — the Modal-inspired, torch-free user-facing SDK. Provides simulo.App / Runtime / Volume / Asset, @app.job, the source packager, and the `simulo` CLI. Depends only on simulo-interfaces."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = { text = "BSD-3-Clause" }
|
|
12
|
+
authors = [{ name = "Simulo Team", email = "team@simulo.ai" }]
|
|
13
|
+
keywords = ["robotics", "simulation", "reinforcement-learning", "thin-client", "simulo"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: BSD License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Typing :: Typed",
|
|
22
|
+
]
|
|
23
|
+
# Torch-free by construction. The thin client orchestrates packaging + discovery
|
|
24
|
+
# and depends ONLY on the contract layer; NO torch, NO simulo-backend. Heavy
|
|
25
|
+
# imports live inside the user's `runtime.imports()` guard (resolved in the
|
|
26
|
+
# cloud / by the backend runner), never in this distribution.
|
|
27
|
+
dependencies = ["simulo-interfaces>=0.1,<0.2"]
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
dev = ["black>=24.0", "isort>=5.13", "ruff>=0.5", "mypy>=1.10", "pytest>=8.2", "build>=1.2", "twine>=5.1"]
|
|
31
|
+
# Mirrors simulo-backend's [release] extra. `dev` above carries build/twine
|
|
32
|
+
# but deliberately NOT commitizen — the release skill (.claude/skills/release/
|
|
33
|
+
# SKILL.md) installs THIS extra specifically so `cz` is available without
|
|
34
|
+
# also pulling in a second copy of build/twine.
|
|
35
|
+
release = ["commitizen>=3.27", "build>=1.2", "twine>=5.1"]
|
|
36
|
+
|
|
37
|
+
[project.scripts]
|
|
38
|
+
simulo = "simulo._client.cli:main"
|
|
39
|
+
|
|
40
|
+
[project.urls]
|
|
41
|
+
Homepage = "https://simulo.ai"
|
|
42
|
+
Source = "https://github.com/simulo-org/simulo-platform"
|
|
43
|
+
|
|
44
|
+
# The thin client is the ONE distribution that ships `src/simulo/__init__.py`.
|
|
45
|
+
# That __init__ calls pkgutil.extend_path so the PEP 420 namespace portions
|
|
46
|
+
# shipped by simulo-backend (simulo.core/backend/recording) and simulo-interfaces
|
|
47
|
+
# (simulo.interfaces) union into the same `simulo` package. `namespaces = true`
|
|
48
|
+
# keeps setuptools from choking on the shared top-level name.
|
|
49
|
+
[tool.setuptools.packages.find]
|
|
50
|
+
where = ["src"]
|
|
51
|
+
namespaces = true
|
|
52
|
+
|
|
53
|
+
[tool.setuptools.package-data]
|
|
54
|
+
"simulo" = ["py.typed"]
|
|
55
|
+
|
|
56
|
+
[tool.mypy]
|
|
57
|
+
python_version = "3.11"
|
|
58
|
+
mypy_path = ["src"]
|
|
59
|
+
namespace_packages = true
|
|
60
|
+
explicit_package_bases = true
|
|
61
|
+
strict = true
|
|
62
|
+
|
|
63
|
+
[tool.pytest.ini_options]
|
|
64
|
+
testpaths = ["tests"]
|
|
65
|
+
addopts = "--import-mode=importlib"
|
|
66
|
+
markers = [
|
|
67
|
+
"isaac: requires NVIDIA Isaac Sim / IsaacLab (skipped on dev machines and in PR CI)",
|
|
68
|
+
"gpu: requires a CUDA GPU (skipped on dev machines and in PR CI)",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
[tool.isort]
|
|
72
|
+
profile = "black"
|
|
73
|
+
line_length = 120
|
|
74
|
+
known_first_party = ["simulo"]
|
|
75
|
+
|
|
76
|
+
[tool.ruff]
|
|
77
|
+
line-length = 120
|
|
78
|
+
src = ["src"]
|
|
79
|
+
|
|
80
|
+
[tool.black]
|
|
81
|
+
line-length = 120
|
|
82
|
+
target-version = ["py311"]
|
|
83
|
+
|
|
84
|
+
# --- Commitizen ------------------------------------------------------------
|
|
85
|
+
# Path B release flow (see `.claude/skills/release/SKILL.md`): `cz bump` here
|
|
86
|
+
# creates the bump commit + tag locally; `.github/workflows/release.yml`
|
|
87
|
+
# publishes to PyPI on tag push. version_provider = "pep621" reads/writes
|
|
88
|
+
# [project].version directly; there is no in-tree __version__ mirror to keep
|
|
89
|
+
# in sync (version_files = []).
|
|
90
|
+
#
|
|
91
|
+
# The FIRST release (0.7.0) cannot come from `cz bump` — 0.1.0 -> 0.7.0 is not
|
|
92
|
+
# a conventional-commit increment, and cz's first-run changelog would sweep
|
|
93
|
+
# in repo-wide history that predates this distribution's split from
|
|
94
|
+
# simulo-backend. It is a hand-written bump commit per the release skill's
|
|
95
|
+
# First-release appendix. 0.7.0 (not 0.2.0) is deliberate: it must exceed the
|
|
96
|
+
# legacy thick-client `simulo` package's last version (0.6.0) already
|
|
97
|
+
# published to PyPI under this same project name, so `pip install simulo`
|
|
98
|
+
# resolves to the new thin client, never the old thick client.
|
|
99
|
+
[tool.commitizen]
|
|
100
|
+
name = "cz_conventional_commits"
|
|
101
|
+
version_provider = "pep621"
|
|
102
|
+
version_files = []
|
|
103
|
+
tag_format = "simulo-v$version"
|
|
104
|
+
update_changelog_on_bump = true
|
|
105
|
+
changelog_incremental = true
|
|
106
|
+
major_version_zero = true
|
|
107
|
+
bump_message = "chore(release): bump simulo to v$new_version"
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""Simulo thin client — the user-facing, torch-free SDK surface.
|
|
2
|
+
|
|
3
|
+
This is the ONE Simulo distribution that ships ``simulo/__init__.py``. The
|
|
4
|
+
``extend_path`` call below unions every ``simulo/`` directory on ``sys.path`` into
|
|
5
|
+
this package's ``__path__`` so the PEP 420 namespace portions shipped by sibling
|
|
6
|
+
distributions (``simulo.core`` / ``simulo.backend`` / ``simulo.recording`` from
|
|
7
|
+
``simulo-backend``; ``simulo.interfaces`` from ``simulo-interfaces``) resolve
|
|
8
|
+
alongside the clean top-level ``simulo.App`` re-exported here.
|
|
9
|
+
|
|
10
|
+
It MUST run before the re-exports below, because importing ``simulo._client.app``
|
|
11
|
+
pulls in ``simulo.interfaces`` (a sibling distribution).
|
|
12
|
+
|
|
13
|
+
Two kinds of name live on ``simulo``:
|
|
14
|
+
|
|
15
|
+
* **Platform** names — ``App / Runtime / Volume / Asset / callbacks`` — are **real,
|
|
16
|
+
eager** attributes bound below. They are always torch-free.
|
|
17
|
+
* **Learning** names — ``Task / Scene / Robot / LearningEnv / RLTrainer / …`` — are
|
|
18
|
+
resolved **lazily and mode-aware** through the PEP 562 ``__getattr__`` below,
|
|
19
|
+
which delegates to :mod:`simulo._client.learning`. At submit (discovery mode)
|
|
20
|
+
they resolve to lean stand-ins so the user app imports without the heavy runtime;
|
|
21
|
+
only the backend runner (execution mode) resolves them to ``simulo.core``. Because
|
|
22
|
+
the platform names are real attributes, ``__getattr__`` fires *only* for the
|
|
23
|
+
learning names.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
import pkgutil as _pkgutil
|
|
27
|
+
from typing import TYPE_CHECKING
|
|
28
|
+
|
|
29
|
+
__path__ = _pkgutil.extend_path(__path__, __name__)
|
|
30
|
+
|
|
31
|
+
from simulo import callbacks
|
|
32
|
+
from simulo._client.app import App
|
|
33
|
+
from simulo._client.asset import Asset
|
|
34
|
+
from simulo._client.learning import LEARNING_NAMES as _LEARNING_NAMES
|
|
35
|
+
from simulo._client.learning import resolve as _resolve_learning
|
|
36
|
+
from simulo._client.runtime import Runtime
|
|
37
|
+
from simulo._client.volume import Volume
|
|
38
|
+
|
|
39
|
+
# Public surface = eager platform exports (real attributes; ``__getattr__`` never
|
|
40
|
+
# fires for these) + the lazily-resolved learning names. Kept a static literal so
|
|
41
|
+
# tooling recognises the re-exports; a test guards it against ``LEARNING_NAMES`` drift.
|
|
42
|
+
__all__ = [
|
|
43
|
+
# platform (eager, real)
|
|
44
|
+
"App",
|
|
45
|
+
"Runtime",
|
|
46
|
+
"Volume",
|
|
47
|
+
"Asset",
|
|
48
|
+
"callbacks",
|
|
49
|
+
# learning (lazy, mode-aware)
|
|
50
|
+
"Entity",
|
|
51
|
+
"LearningEnv",
|
|
52
|
+
"Light",
|
|
53
|
+
"Material",
|
|
54
|
+
"ObservationComponent",
|
|
55
|
+
"Physics",
|
|
56
|
+
"Player",
|
|
57
|
+
"Policy",
|
|
58
|
+
"Pose",
|
|
59
|
+
"RLPlayer",
|
|
60
|
+
"RLTrainer",
|
|
61
|
+
"RecordConfig",
|
|
62
|
+
"RewardComponent",
|
|
63
|
+
"Robot",
|
|
64
|
+
"Scenario",
|
|
65
|
+
"Scene",
|
|
66
|
+
"Task",
|
|
67
|
+
"Terrain",
|
|
68
|
+
"TerminationCondition",
|
|
69
|
+
"Trainer",
|
|
70
|
+
"Visual",
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def __getattr__(name: str) -> object:
|
|
75
|
+
"""PEP 562 hook: lazily resolve the mode-aware learning names.
|
|
76
|
+
|
|
77
|
+
Fires only for attributes missing from the module dict — i.e. never for the
|
|
78
|
+
real platform exports above. Delegates the learning names to
|
|
79
|
+
:func:`simulo._client.learning.resolve` (discovery stand-in vs. real
|
|
80
|
+
``simulo.core`` class, by mode); everything else is a genuine ``AttributeError``.
|
|
81
|
+
"""
|
|
82
|
+
if name in _LEARNING_NAMES:
|
|
83
|
+
return _resolve_learning(name)
|
|
84
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def __dir__() -> list[str]:
|
|
88
|
+
"""Expose only the public surface to ``dir(simulo)``.
|
|
89
|
+
|
|
90
|
+
That is ``__all__`` (eager platform exports + learning names) unioned with
|
|
91
|
+
:data:`LEARNING_NAMES`. Private module globals (``_pkgutil``,
|
|
92
|
+
``_resolve_learning``, ``_LEARNING_NAMES``, the ``TYPE_CHECKING`` import, …)
|
|
93
|
+
are deliberately filtered out so tab-completion shows the user-facing API only.
|
|
94
|
+
"""
|
|
95
|
+
return sorted(set(__all__) | set(_LEARNING_NAMES))
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if TYPE_CHECKING:
|
|
99
|
+
# Static aliases so a type checker sees the learning names with precise types
|
|
100
|
+
# (PEP 562 ``__getattr__`` would otherwise type them as ``Any``). The rule that
|
|
101
|
+
# this block MUST follow — and that the mypy positive test in
|
|
102
|
+
# ``tests/test_learning_typing.py`` locks — is:
|
|
103
|
+
#
|
|
104
|
+
# * **Subclassable** names (used as ``class X(simulo.Task)``) alias their
|
|
105
|
+
# ``interfaces.runtime`` Protocol, so the subclass is checked against the
|
|
106
|
+
# contract (e.g. ``TaskProtocol``).
|
|
107
|
+
# * **Instantiated / value-config** names (used as ``simulo.LearningEnv(task=...)``,
|
|
108
|
+
# ``simulo.RLTrainer(env=...)``, ``simulo.Scene(...)``) alias ``Any``. Their
|
|
109
|
+
# concrete type is the heavy ``simulo.core`` class, available only on the
|
|
110
|
+
# worker — aliasing them to a Protocol would make mypy reject instantiation
|
|
111
|
+
# ("Cannot instantiate protocol class").
|
|
112
|
+
from typing import Any
|
|
113
|
+
|
|
114
|
+
from simulo.interfaces.runtime import (
|
|
115
|
+
ObservationComponentProtocol,
|
|
116
|
+
PlayerProtocol,
|
|
117
|
+
PolicyProtocol,
|
|
118
|
+
RewardComponentProtocol,
|
|
119
|
+
ScenarioProtocol,
|
|
120
|
+
TaskProtocol,
|
|
121
|
+
TerminationConditionProtocol,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Subclassable names alias their Protocol so ``class X(simulo.Task)`` is checked
|
|
125
|
+
# against ``TaskProtocol``.
|
|
126
|
+
Task = TaskProtocol
|
|
127
|
+
Scenario = ScenarioProtocol
|
|
128
|
+
Policy = PolicyProtocol
|
|
129
|
+
Player = PlayerProtocol
|
|
130
|
+
ObservationComponent = ObservationComponentProtocol
|
|
131
|
+
RewardComponent = RewardComponentProtocol
|
|
132
|
+
TerminationCondition = TerminationConditionProtocol
|
|
133
|
+
# Instantiated / value-config names: concrete type is the heavy ``simulo.core``
|
|
134
|
+
# (or ``simulo.recording``, for ``RecordConfig``) class, available only on the
|
|
135
|
+
# worker; alias ``Any`` so mypy accepts ``simulo.Name(...)``.
|
|
136
|
+
# ``LearningEnv`` is here — ``simulo.LearningEnv(task=...)`` is instantiated, so
|
|
137
|
+
# it must NOT alias ``LearningEnvProtocol`` (mypy would reject the call).
|
|
138
|
+
# ``RLPlayer``/``RecordConfig`` are instantiated too (``simulo.RLPlayer(env=...)``,
|
|
139
|
+
# ``simulo.RecordConfig(output_path=...)``), so they alias ``Any`` — only the
|
|
140
|
+
# subclassable ``Player`` base aliases ``PlayerProtocol``.
|
|
141
|
+
LearningEnv = Any
|
|
142
|
+
Scene = Any
|
|
143
|
+
Robot = Any
|
|
144
|
+
RLTrainer = Any
|
|
145
|
+
Trainer = Any
|
|
146
|
+
RLPlayer = Any
|
|
147
|
+
RecordConfig = Any
|
|
148
|
+
Pose = Any
|
|
149
|
+
Terrain = Any
|
|
150
|
+
Light = Any
|
|
151
|
+
Entity = Any
|
|
152
|
+
Material = Any
|
|
153
|
+
Physics = Any
|
|
154
|
+
Visual = Any
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"""Internal implementation package for the Simulo thin client.
|
|
2
|
+
|
|
3
|
+
Nothing here is part of the public API. Users import from the top-level
|
|
4
|
+
``simulo`` namespace (``simulo.App`` / ``simulo.Runtime`` / ``simulo.callbacks``);
|
|
5
|
+
these modules are the private machinery behind those re-exports.
|
|
6
|
+
"""
|