eclise 0.1.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.
- eclise-0.1.0/CHANGELOG.md +25 -0
- eclise-0.1.0/LICENSE +21 -0
- eclise-0.1.0/MANIFEST.in +6 -0
- eclise-0.1.0/PKG-INFO +183 -0
- eclise-0.1.0/README.md +117 -0
- eclise-0.1.0/data/clips/src/eclise/clips/__init__.py +97 -0
- eclise-0.1.0/data/clips/src/eclise/clips/codec.py +85 -0
- eclise-0.1.0/data/clips/src/eclise/clips/dataset.py +237 -0
- eclise-0.1.0/data/clips/src/eclise/clips/labels.py +198 -0
- eclise-0.1.0/data/clips/src/eclise/clips/readers/__init__.py +24 -0
- eclise-0.1.0/data/clips/src/eclise/clips/readers/phyworld_hdf5.py +489 -0
- eclise-0.1.0/data/clips/src/eclise/clips/schema.py +302 -0
- eclise-0.1.0/data/clips/src/eclise/clips/shards.py +487 -0
- eclise-0.1.0/data/datasets/src/eclise/datasets/__init__.py +17 -0
- eclise-0.1.0/data/datasets/src/eclise/datasets/_resources/imagenet_classes.json +5250 -0
- eclise-0.1.0/data/datasets/src/eclise/datasets/imagenet.py +131 -0
- eclise-0.1.0/data/datasets/src/eclise/datasets/labels.py +65 -0
- eclise-0.1.0/data/openvid/src/eclise/openvid/__init__.py +58 -0
- eclise-0.1.0/data/openvid/src/eclise/openvid/config.py +183 -0
- eclise-0.1.0/data/openvid/src/eclise/openvid/hub.py +178 -0
- eclise-0.1.0/data/openvid/src/eclise/openvid/ingest.py +526 -0
- eclise-0.1.0/data/openvid/src/eclise/openvid/metadata.py +342 -0
- eclise-0.1.0/data/openvid/src/eclise/openvid/split.py +33 -0
- eclise-0.1.0/data/openvid/src/eclise/openvid/video.py +291 -0
- eclise-0.1.0/data/phyworld/src/eclise/phyworld/__init__.py +66 -0
- eclise-0.1.0/data/phyworld/src/eclise/phyworld/analysis/__init__.py +41 -0
- eclise-0.1.0/data/phyworld/src/eclise/phyworld/analysis/metrics.py +201 -0
- eclise-0.1.0/data/phyworld/src/eclise/phyworld/analysis/state_recovery.py +275 -0
- eclise-0.1.0/data/phyworld/src/eclise/phyworld/analysis/verify.py +213 -0
- eclise-0.1.0/data/phyworld/src/eclise/phyworld/combinatorial_driver.py +302 -0
- eclise-0.1.0/data/phyworld/src/eclise/phyworld/config.py +353 -0
- eclise-0.1.0/data/phyworld/src/eclise/phyworld/generate.py +447 -0
- eclise-0.1.0/data/phyworld/src/eclise/phyworld/grids.py +368 -0
- eclise-0.1.0/data/phyworld/src/eclise/phyworld/render.py +477 -0
- eclise-0.1.0/data/phyworld/src/eclise/phyworld/scenes/__init__.py +51 -0
- eclise-0.1.0/data/phyworld/src/eclise/phyworld/scenes/base.py +177 -0
- eclise-0.1.0/data/phyworld/src/eclise/phyworld/scenes/collision.py +192 -0
- eclise-0.1.0/data/phyworld/src/eclise/phyworld/scenes/combinatorial.py +386 -0
- eclise-0.1.0/data/phyworld/src/eclise/phyworld/scenes/parabola.py +92 -0
- eclise-0.1.0/data/phyworld/src/eclise/phyworld/scenes/phyre_compat.py +400 -0
- eclise-0.1.0/data/phyworld/src/eclise/phyworld/scenes/uniform_motion.py +207 -0
- eclise-0.1.0/eclise.egg-info/PKG-INFO +183 -0
- eclise-0.1.0/eclise.egg-info/SOURCES.txt +114 -0
- eclise-0.1.0/eclise.egg-info/dependency_links.txt +1 -0
- eclise-0.1.0/eclise.egg-info/requires.txt +52 -0
- eclise-0.1.0/eclise.egg-info/top_level.txt +1 -0
- eclise-0.1.0/launch/src/eclise/launch/__init__.py +24 -0
- eclise-0.1.0/launch/src/eclise/launch/__main__.py +125 -0
- eclise-0.1.0/launch/src/eclise/launch/_version.py +3 -0
- eclise-0.1.0/launch/src/eclise/launch/errors.py +5 -0
- eclise-0.1.0/launch/src/eclise/launch/launcher.py +216 -0
- eclise-0.1.0/launch/src/eclise/launch/pixi.py +181 -0
- eclise-0.1.0/launch/src/eclise/launch/registry.py +69 -0
- eclise-0.1.0/launch/src/eclise/launch/root.py +153 -0
- eclise-0.1.0/models/robotics/libero/src/eclise/libero/__init__.py +27 -0
- eclise-0.1.0/models/robotics/libero/src/eclise/libero/evaluation/__init__.py +11 -0
- eclise-0.1.0/models/robotics/libero/src/eclise/libero/evaluation/config.py +63 -0
- eclise-0.1.0/models/robotics/libero/src/eclise/libero/evaluation/metrics.py +36 -0
- eclise-0.1.0/models/robotics/libero/src/eclise/libero/evaluation/runner.py +174 -0
- eclise-0.1.0/models/robotics/libero/src/eclise/libero/harness/__init__.py +18 -0
- eclise-0.1.0/models/robotics/libero/src/eclise/libero/harness/env.py +88 -0
- eclise-0.1.0/models/robotics/libero/src/eclise/libero/harness/libero_env.py +107 -0
- eclise-0.1.0/models/robotics/libero/src/eclise/libero/harness/rollout.py +14 -0
- eclise-0.1.0/models/robotics/libero/src/eclise/libero/observation.py +32 -0
- eclise-0.1.0/models/robotics/libero/src/eclise/libero/serve.py +230 -0
- eclise-0.1.0/models/robotics/octo/src/eclise/octo/__init__.py +14 -0
- eclise-0.1.0/models/robotics/octo/src/eclise/octo/obs_adapter.py +190 -0
- eclise-0.1.0/models/robotics/octo/src/eclise/octo/policy.py +133 -0
- eclise-0.1.0/models/robotics/octo/src/eclise/octo/serve.py +76 -0
- eclise-0.1.0/models/robotics/octo/src/eclise/octo/training/__init__.py +39 -0
- eclise-0.1.0/models/robotics/octo/src/eclise/octo/training/config.py +76 -0
- eclise-0.1.0/models/robotics/octo/src/eclise/octo/training/data.py +507 -0
- eclise-0.1.0/models/robotics/octo/src/eclise/octo/training/step.py +47 -0
- eclise-0.1.0/models/robotics/octo/src/eclise/octo/training/train_state.py +102 -0
- eclise-0.1.0/models/robotics/octo/src/eclise/octo/training/transforms.py +53 -0
- eclise-0.1.0/models/robotics/pi0/src/eclise/pi0/__init__.py +17 -0
- eclise-0.1.0/models/robotics/serving/src/eclise/vla/__init__.py +74 -0
- eclise-0.1.0/models/robotics/serving/src/eclise/vla/_ws.py +23 -0
- eclise-0.1.0/models/robotics/serving/src/eclise/vla/env.py +70 -0
- eclise-0.1.0/models/robotics/serving/src/eclise/vla/env_client.py +104 -0
- eclise-0.1.0/models/robotics/serving/src/eclise/vla/env_server.py +134 -0
- eclise-0.1.0/models/robotics/serving/src/eclise/vla/msgpack_numpy.py +58 -0
- eclise-0.1.0/models/robotics/serving/src/eclise/vla/observation.py +78 -0
- eclise-0.1.0/models/robotics/serving/src/eclise/vla/policy.py +60 -0
- eclise-0.1.0/models/robotics/serving/src/eclise/vla/policy_client.py +95 -0
- eclise-0.1.0/models/robotics/serving/src/eclise/vla/rollout.py +181 -0
- eclise-0.1.0/models/robotics/serving/src/eclise/vla/serve_dummy.py +30 -0
- eclise-0.1.0/models/robotics/serving/src/eclise/vla/server.py +93 -0
- eclise-0.1.0/models/robotics/smolvla/src/eclise/smolvla/__init__.py +1 -0
- eclise-0.1.0/models/robotics/smolvla/src/eclise/smolvla/obs_adapter.py +89 -0
- eclise-0.1.0/models/robotics/smolvla/src/eclise/smolvla/policy.py +118 -0
- eclise-0.1.0/models/robotics/smolvla/src/eclise/smolvla/serve.py +67 -0
- eclise-0.1.0/pixi.lock +10377 -0
- eclise-0.1.0/pixi.toml +229 -0
- eclise-0.1.0/pyproject.toml +100 -0
- eclise-0.1.0/setup.cfg +4 -0
- eclise-0.1.0/setup.py +59 -0
- eclise-0.1.0/utils/src/eclise/utils/__init__.py +103 -0
- eclise-0.1.0/utils/src/eclise/utils/are.py +409 -0
- eclise-0.1.0/utils/src/eclise/utils/arrays.py +93 -0
- eclise-0.1.0/utils/src/eclise/utils/dicts.py +85 -0
- eclise-0.1.0/utils/src/eclise/utils/ensemble.py +137 -0
- eclise-0.1.0/utils/src/eclise/utils/fast_conv_scan.py +171 -0
- eclise-0.1.0/utils/src/eclise/utils/io.py +40 -0
- eclise-0.1.0/utils/src/eclise/utils/labeled_array.py +213 -0
- eclise-0.1.0/utils/src/eclise/utils/linalg.py +285 -0
- eclise-0.1.0/utils/src/eclise/utils/memory.py +61 -0
- eclise-0.1.0/utils/src/eclise/utils/modules.py +243 -0
- eclise-0.1.0/utils/src/eclise/utils/ode.py +77 -0
- eclise-0.1.0/utils/src/eclise/utils/optim.py +34 -0
- eclise-0.1.0/utils/src/eclise/utils/plotting.py +50 -0
- eclise-0.1.0/utils/src/eclise/utils/recursive.py +50 -0
- eclise-0.1.0/utils/src/eclise/utils/scan.py +67 -0
- eclise-0.1.0/utils/src/eclise/utils/settings.py +137 -0
- eclise-0.1.0/utils/src/eclise/utils/timing.py +96 -0
- eclise-0.1.0/utils/src/eclise/utils/types.py +19 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Contract changes in `eclise.clips` and `eclise.vla` are documented here: they
|
|
4
|
+
no longer have independent versions.
|
|
5
|
+
|
|
6
|
+
## 0.1.0
|
|
7
|
+
|
|
8
|
+
Single distribution: one `eclise` wheel, tagged `eclise-v<version>`. Heavy
|
|
9
|
+
stacks live in pixi features, not wheel extras. `eclise.launch` obtains env
|
|
10
|
+
definitions by cloning this repo at the wheel's own version tag.
|
|
11
|
+
|
|
12
|
+
### Legacy distributions
|
|
13
|
+
|
|
14
|
+
The per-package distributions are gone, with no compatibility shims. Only
|
|
15
|
+
three were ever published; all are deleted from PyPI:
|
|
16
|
+
|
|
17
|
+
| Deleted from PyPI | Replacement | Import |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `eclise-clips` (0.1.0) | `eclise[clips]` | `eclise.clips` |
|
|
20
|
+
| `eclise-phyworld` (0.2.0) | run from checkout: `pixi run -e phyworld generate` | `eclise.phyworld` |
|
|
21
|
+
| `ecliseutils` (0.1.0–0.1.3) | `eclise[utils]` | `eclise.utils` |
|
|
22
|
+
|
|
23
|
+
`eclise-utils`, `eclise-datasets`, `eclise-vla`, and `eclise-openvid` were
|
|
24
|
+
never published. Downstream projects must switch their requirement to the
|
|
25
|
+
`eclise[<extra>]` form; the `ecliseutils` import name has no successor.
|
eclise-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Wentinn Liao
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
eclise-0.1.0/MANIFEST.in
ADDED
eclise-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: eclise
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Shared research infrastructure: clip/VLA contracts, research utilities, and stdlib launchers for isolated robotics/data environments.
|
|
5
|
+
Author: Wentinn Liao
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/JophiArcana/eclise
|
|
8
|
+
Project-URL: Repository, https://github.com/JophiArcana/eclise
|
|
9
|
+
Project-URL: Issues, https://github.com/JophiArcana/eclise/issues
|
|
10
|
+
Keywords: research-utilities,robotics,vla,datasets,clips
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Provides-Extra: utils
|
|
25
|
+
Requires-Dist: numpy; extra == "utils"
|
|
26
|
+
Requires-Dist: torch; extra == "utils"
|
|
27
|
+
Requires-Dist: tensordict; extra == "utils"
|
|
28
|
+
Requires-Dist: einops; extra == "utils"
|
|
29
|
+
Requires-Dist: scipy; extra == "utils"
|
|
30
|
+
Requires-Dist: matplotlib; extra == "utils"
|
|
31
|
+
Requires-Dist: tqdm; extra == "utils"
|
|
32
|
+
Requires-Dist: pandas; extra == "utils"
|
|
33
|
+
Provides-Extra: ode
|
|
34
|
+
Requires-Dist: torchdiffeq; extra == "ode"
|
|
35
|
+
Provides-Extra: datasets
|
|
36
|
+
Requires-Dist: datasets<5.0,>=3.0; extra == "datasets"
|
|
37
|
+
Requires-Dist: huggingface_hub<1.0,>=0.34.0; extra == "datasets"
|
|
38
|
+
Requires-Dist: Pillow~=12.2.0; extra == "datasets"
|
|
39
|
+
Requires-Dist: numpy~=2.4.0; extra == "datasets"
|
|
40
|
+
Provides-Extra: clips
|
|
41
|
+
Requires-Dist: numpy>=1.24; extra == "clips"
|
|
42
|
+
Requires-Dist: h5py>=3.10; extra == "clips"
|
|
43
|
+
Requires-Dist: imageio[ffmpeg]>=2.36; extra == "clips"
|
|
44
|
+
Requires-Dist: av>=13.1; extra == "clips"
|
|
45
|
+
Provides-Extra: vla
|
|
46
|
+
Requires-Dist: numpy; extra == "vla"
|
|
47
|
+
Requires-Dist: websockets>=14; extra == "vla"
|
|
48
|
+
Requires-Dist: msgpack>=1.0.5; extra == "vla"
|
|
49
|
+
Provides-Extra: octo
|
|
50
|
+
Requires-Dist: eclise[vla]; extra == "octo"
|
|
51
|
+
Provides-Extra: smolvla
|
|
52
|
+
Requires-Dist: eclise[vla]; extra == "smolvla"
|
|
53
|
+
Provides-Extra: pi0
|
|
54
|
+
Requires-Dist: eclise[vla]; extra == "pi0"
|
|
55
|
+
Provides-Extra: libero
|
|
56
|
+
Requires-Dist: eclise[vla]; extra == "libero"
|
|
57
|
+
Provides-Extra: all
|
|
58
|
+
Requires-Dist: eclise[utils]; extra == "all"
|
|
59
|
+
Requires-Dist: eclise[datasets]; extra == "all"
|
|
60
|
+
Requires-Dist: eclise[clips]; extra == "all"
|
|
61
|
+
Requires-Dist: eclise[vla]; extra == "all"
|
|
62
|
+
Provides-Extra: dev
|
|
63
|
+
Requires-Dist: eclise[all]; extra == "dev"
|
|
64
|
+
Requires-Dist: pytest~=8.4.0; extra == "dev"
|
|
65
|
+
Dynamic: license-file
|
|
66
|
+
|
|
67
|
+
# eclise
|
|
68
|
+
|
|
69
|
+
Shared research infrastructure: one `eclise` wheel under the PEP 420 namespace
|
|
70
|
+
`eclise.*`. The folder taxonomy is organizational only — imports are always
|
|
71
|
+
flat `eclise.<name>`. Heavy stacks live in pixi environments, not in wheel
|
|
72
|
+
metadata.
|
|
73
|
+
|
|
74
|
+
## Extras (the published wheel)
|
|
75
|
+
|
|
76
|
+
| Extra | Import | Purpose |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| `utils` | `eclise.utils` | Research utilities (tensor/module batching, linalg, ARE/ODE, labeled arrays, plotting) |
|
|
79
|
+
| `ode` | `eclise.utils.ode` | `torchdiffeq` for `batch_odeint` |
|
|
80
|
+
| `datasets` | `eclise.datasets` | Adapters for consuming external datasets (ImageNet-1k val sampling, label tables) |
|
|
81
|
+
| `clips` | `eclise.clips` | First-party clip-shard data contract (mp4-in-HDF5 + per-frame state labels) |
|
|
82
|
+
| `vla` | `eclise.vla` | Sim-/model-agnostic VLA obs/action schema + websocket env/policy serving |
|
|
83
|
+
| `octo` / `smolvla` / `pi0` / `libero` | (client aliases) | `eclise[vla]` — talking to a server, not installing its stack |
|
|
84
|
+
| `all` | | utils + datasets + clips + vla |
|
|
85
|
+
| `dev` | | all + pytest |
|
|
86
|
+
|
|
87
|
+
`eclise.octo` / `smolvla` / `libero` / `phyworld` / `openvid` / `pi0` / `launch`
|
|
88
|
+
ship as inert source in the same wheel. Running a server or generator needs
|
|
89
|
+
the matching pixi environment (or `python -m eclise.launch`).
|
|
90
|
+
|
|
91
|
+
`models/vision/` is deliberately absent: a vision-model contract will be
|
|
92
|
+
created only once it has at least two real consumers.
|
|
93
|
+
|
|
94
|
+
## Pixi environments (from a checkout)
|
|
95
|
+
|
|
96
|
+
| Environment | Python | Kind | Purpose |
|
|
97
|
+
|---|---|---|---|
|
|
98
|
+
| `dev` | 3.12 | library | `eclise[dev]` + contract/launcher tests |
|
|
99
|
+
| `libero` | 3.10 | socket (env-server, port 9000) | LIBERO + robosuite/MuJoCo harness |
|
|
100
|
+
| `octo` | 3.10 | socket (policy, port 8000) | Octo JAX 0.4.20 policy server |
|
|
101
|
+
| `smolvla` | 3.12 | socket (policy, port 8000) | SmolVLA (LeRobot) policy server |
|
|
102
|
+
| `pi0` | 3.11 | socket (policy, port 8000) | Tooling for upstream openpi (`uv` / git-lfs / gsutil) |
|
|
103
|
+
| `phyworld` | 3.10 | batch | Box2D families → `eclise.clips` shards |
|
|
104
|
+
| `phyworld-phyre` | 3.9 | batch | PHYRE combinatorial family (`phyre==0.2.2`) |
|
|
105
|
+
| `openvid` | 3.10 | batch | OpenVid-1M mp4 → 256px fixed-FPS shards |
|
|
106
|
+
|
|
107
|
+
## Installing
|
|
108
|
+
|
|
109
|
+
Client-side (numpy-1.x-safe `eclise.vla` only — no torch/jax):
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
pip install "eclise[octo,libero]"
|
|
113
|
+
python -m eclise.launch start octo --setup
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`--setup` clones this repo at `eclise-v<wheel version>` (unless
|
|
117
|
+
`ECLISE_ROOT` already points at a checkout), installs pixi if needed, and
|
|
118
|
+
runs `pixi install -e <env>`. `start` returns a host/port; the caller wires
|
|
119
|
+
`WebsocketPolicyClient` / `WebsocketEnvClient`.
|
|
120
|
+
|
|
121
|
+
From a checkout:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
pixi install -e dev
|
|
125
|
+
pixi run -e dev test
|
|
126
|
+
pixi run -e libero setup
|
|
127
|
+
pixi run -e libero serve --port 9000
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Developing
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
pixi run -e dev test
|
|
134
|
+
pixi run -e libero serve
|
|
135
|
+
pixi run -e phyworld generate --family collision --grid in_dist_l0 --experiment smoke --limit 32
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Editable extras without pixi: `pip install -e ".[dev]"`. Releases are tagged
|
|
139
|
+
`eclise-v0.1.0`.
|
|
140
|
+
|
|
141
|
+
## Design rules
|
|
142
|
+
|
|
143
|
+
- **One distribution, environments at the pixi boundary.** Incompatible
|
|
144
|
+
stacks (JAX vs torch, numpy 1.x vs 2.x) do not share an environment.
|
|
145
|
+
Wheel extras stay light; pixi features own the heavy pins.
|
|
146
|
+
- **Imports are flat, folders carry taxonomy.** Moving a package between
|
|
147
|
+
folders is never a breaking change for consumers.
|
|
148
|
+
- **`eclise.clips` and `eclise.vla` are contracts.** Writers and readers may
|
|
149
|
+
live in different repos/envs; document schema changes in `CHANGELOG.md`.
|
|
150
|
+
`eclise[vla]` must remain numpy 1.x-compatible.
|
|
151
|
+
|
|
152
|
+
## Package archetypes
|
|
153
|
+
|
|
154
|
+
Every package is one of three kinds. New packages pick one and follow its
|
|
155
|
+
rules rather than inventing a fourth layout.
|
|
156
|
+
|
|
157
|
+
**Library extra** — published wheel, installed into a consumer's env, no
|
|
158
|
+
server. `utils`, `datasets`, `clips`, `vla`.
|
|
159
|
+
|
|
160
|
+
**Interactive socket service** — pixi env isolates an incompatible stack
|
|
161
|
+
behind an `eclise.vla` websocket. `octo`, `smolvla`, `pi0`, `libero`.
|
|
162
|
+
|
|
163
|
+
- Env: `pixi install -e <name>` (and `pixi run -e <name> setup` when the
|
|
164
|
+
feature defines a `setup` task).
|
|
165
|
+
- Server logic lives in the package as `eclise.<name>.serve` (pi0 wraps
|
|
166
|
+
upstream openpi instead).
|
|
167
|
+
- Launch: `python -m eclise.launch start <name>` → `pixi run -e <env> serve`.
|
|
168
|
+
|
|
169
|
+
**Batch producer** — pixi env; decoupled via the on-disk `eclise.clips`
|
|
170
|
+
shard contract (no socket). `phyworld`, `openvid`.
|
|
171
|
+
|
|
172
|
+
- Env: `pixi install -e <name>` + `pixi run -e <name> setup` (scratch
|
|
173
|
+
`data/` / `outputs/` symlinks).
|
|
174
|
+
- Job entrypoint: `scripts/<verb>.py` (+ `.sbatch` via `pixi run -e <env>`).
|
|
175
|
+
- Producer verify: `scripts/verify_shards.py`.
|
|
176
|
+
|
|
177
|
+
| Package | Archetype |
|
|
178
|
+
|---|---|
|
|
179
|
+
| `utils`, `datasets`, `clips`, `vla` | library extra |
|
|
180
|
+
| `octo`, `smolvla`, `libero` | interactive socket service (`eclise.<name>.serve`) |
|
|
181
|
+
| `pi0` | interactive socket service (`setup_pi0.sh` + `serve_pi0.sh` wrap openpi) |
|
|
182
|
+
| `phyworld` | batch producer (`phyworld-phyre` is the PHYRE secondary env) |
|
|
183
|
+
| `openvid` | batch producer |
|
eclise-0.1.0/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# eclise
|
|
2
|
+
|
|
3
|
+
Shared research infrastructure: one `eclise` wheel under the PEP 420 namespace
|
|
4
|
+
`eclise.*`. The folder taxonomy is organizational only — imports are always
|
|
5
|
+
flat `eclise.<name>`. Heavy stacks live in pixi environments, not in wheel
|
|
6
|
+
metadata.
|
|
7
|
+
|
|
8
|
+
## Extras (the published wheel)
|
|
9
|
+
|
|
10
|
+
| Extra | Import | Purpose |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| `utils` | `eclise.utils` | Research utilities (tensor/module batching, linalg, ARE/ODE, labeled arrays, plotting) |
|
|
13
|
+
| `ode` | `eclise.utils.ode` | `torchdiffeq` for `batch_odeint` |
|
|
14
|
+
| `datasets` | `eclise.datasets` | Adapters for consuming external datasets (ImageNet-1k val sampling, label tables) |
|
|
15
|
+
| `clips` | `eclise.clips` | First-party clip-shard data contract (mp4-in-HDF5 + per-frame state labels) |
|
|
16
|
+
| `vla` | `eclise.vla` | Sim-/model-agnostic VLA obs/action schema + websocket env/policy serving |
|
|
17
|
+
| `octo` / `smolvla` / `pi0` / `libero` | (client aliases) | `eclise[vla]` — talking to a server, not installing its stack |
|
|
18
|
+
| `all` | | utils + datasets + clips + vla |
|
|
19
|
+
| `dev` | | all + pytest |
|
|
20
|
+
|
|
21
|
+
`eclise.octo` / `smolvla` / `libero` / `phyworld` / `openvid` / `pi0` / `launch`
|
|
22
|
+
ship as inert source in the same wheel. Running a server or generator needs
|
|
23
|
+
the matching pixi environment (or `python -m eclise.launch`).
|
|
24
|
+
|
|
25
|
+
`models/vision/` is deliberately absent: a vision-model contract will be
|
|
26
|
+
created only once it has at least two real consumers.
|
|
27
|
+
|
|
28
|
+
## Pixi environments (from a checkout)
|
|
29
|
+
|
|
30
|
+
| Environment | Python | Kind | Purpose |
|
|
31
|
+
|---|---|---|---|
|
|
32
|
+
| `dev` | 3.12 | library | `eclise[dev]` + contract/launcher tests |
|
|
33
|
+
| `libero` | 3.10 | socket (env-server, port 9000) | LIBERO + robosuite/MuJoCo harness |
|
|
34
|
+
| `octo` | 3.10 | socket (policy, port 8000) | Octo JAX 0.4.20 policy server |
|
|
35
|
+
| `smolvla` | 3.12 | socket (policy, port 8000) | SmolVLA (LeRobot) policy server |
|
|
36
|
+
| `pi0` | 3.11 | socket (policy, port 8000) | Tooling for upstream openpi (`uv` / git-lfs / gsutil) |
|
|
37
|
+
| `phyworld` | 3.10 | batch | Box2D families → `eclise.clips` shards |
|
|
38
|
+
| `phyworld-phyre` | 3.9 | batch | PHYRE combinatorial family (`phyre==0.2.2`) |
|
|
39
|
+
| `openvid` | 3.10 | batch | OpenVid-1M mp4 → 256px fixed-FPS shards |
|
|
40
|
+
|
|
41
|
+
## Installing
|
|
42
|
+
|
|
43
|
+
Client-side (numpy-1.x-safe `eclise.vla` only — no torch/jax):
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install "eclise[octo,libero]"
|
|
47
|
+
python -m eclise.launch start octo --setup
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`--setup` clones this repo at `eclise-v<wheel version>` (unless
|
|
51
|
+
`ECLISE_ROOT` already points at a checkout), installs pixi if needed, and
|
|
52
|
+
runs `pixi install -e <env>`. `start` returns a host/port; the caller wires
|
|
53
|
+
`WebsocketPolicyClient` / `WebsocketEnvClient`.
|
|
54
|
+
|
|
55
|
+
From a checkout:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pixi install -e dev
|
|
59
|
+
pixi run -e dev test
|
|
60
|
+
pixi run -e libero setup
|
|
61
|
+
pixi run -e libero serve --port 9000
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Developing
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pixi run -e dev test
|
|
68
|
+
pixi run -e libero serve
|
|
69
|
+
pixi run -e phyworld generate --family collision --grid in_dist_l0 --experiment smoke --limit 32
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Editable extras without pixi: `pip install -e ".[dev]"`. Releases are tagged
|
|
73
|
+
`eclise-v0.1.0`.
|
|
74
|
+
|
|
75
|
+
## Design rules
|
|
76
|
+
|
|
77
|
+
- **One distribution, environments at the pixi boundary.** Incompatible
|
|
78
|
+
stacks (JAX vs torch, numpy 1.x vs 2.x) do not share an environment.
|
|
79
|
+
Wheel extras stay light; pixi features own the heavy pins.
|
|
80
|
+
- **Imports are flat, folders carry taxonomy.** Moving a package between
|
|
81
|
+
folders is never a breaking change for consumers.
|
|
82
|
+
- **`eclise.clips` and `eclise.vla` are contracts.** Writers and readers may
|
|
83
|
+
live in different repos/envs; document schema changes in `CHANGELOG.md`.
|
|
84
|
+
`eclise[vla]` must remain numpy 1.x-compatible.
|
|
85
|
+
|
|
86
|
+
## Package archetypes
|
|
87
|
+
|
|
88
|
+
Every package is one of three kinds. New packages pick one and follow its
|
|
89
|
+
rules rather than inventing a fourth layout.
|
|
90
|
+
|
|
91
|
+
**Library extra** — published wheel, installed into a consumer's env, no
|
|
92
|
+
server. `utils`, `datasets`, `clips`, `vla`.
|
|
93
|
+
|
|
94
|
+
**Interactive socket service** — pixi env isolates an incompatible stack
|
|
95
|
+
behind an `eclise.vla` websocket. `octo`, `smolvla`, `pi0`, `libero`.
|
|
96
|
+
|
|
97
|
+
- Env: `pixi install -e <name>` (and `pixi run -e <name> setup` when the
|
|
98
|
+
feature defines a `setup` task).
|
|
99
|
+
- Server logic lives in the package as `eclise.<name>.serve` (pi0 wraps
|
|
100
|
+
upstream openpi instead).
|
|
101
|
+
- Launch: `python -m eclise.launch start <name>` → `pixi run -e <env> serve`.
|
|
102
|
+
|
|
103
|
+
**Batch producer** — pixi env; decoupled via the on-disk `eclise.clips`
|
|
104
|
+
shard contract (no socket). `phyworld`, `openvid`.
|
|
105
|
+
|
|
106
|
+
- Env: `pixi install -e <name>` + `pixi run -e <name> setup` (scratch
|
|
107
|
+
`data/` / `outputs/` symlinks).
|
|
108
|
+
- Job entrypoint: `scripts/<verb>.py` (+ `.sbatch` via `pixi run -e <env>`).
|
|
109
|
+
- Producer verify: `scripts/verify_shards.py`.
|
|
110
|
+
|
|
111
|
+
| Package | Archetype |
|
|
112
|
+
|---|---|
|
|
113
|
+
| `utils`, `datasets`, `clips`, `vla` | library extra |
|
|
114
|
+
| `octo`, `smolvla`, `libero` | interactive socket service (`eclise.<name>.serve`) |
|
|
115
|
+
| `pi0` | interactive socket service (`setup_pi0.sh` + `serve_pi0.sh` wrap openpi) |
|
|
116
|
+
| `phyworld` | batch producer (`phyworld-phyre` is the PHYRE secondary env) |
|
|
117
|
+
| `openvid` | batch producer |
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""A shared on-disk contract for video clips with per-frame state labels.
|
|
2
|
+
|
|
3
|
+
Sits between anything that *produces* clips (a simulator, a download of a
|
|
4
|
+
published dataset, eventually a real-video ingest) and anything that *consumes*
|
|
5
|
+
them (a training project). Producers and consumers live in separate conda envs
|
|
6
|
+
and never import each other; they agree on this package instead.
|
|
7
|
+
|
|
8
|
+
from eclise.clips import ClipDataset
|
|
9
|
+
|
|
10
|
+
data = ClipDataset("data/phyworld/data/collision_30K")
|
|
11
|
+
item = data[0]
|
|
12
|
+
item["pixels"] # [3, T, H, W] float32 in [-1, 1]
|
|
13
|
+
item["positions"] # [T, n_objects, 2] world units, y up
|
|
14
|
+
item["velocity"] # [2] px/frame, y down (image convention)
|
|
15
|
+
data.source_fps # 1 / frame_dt -- authoritative, not the mp4 header
|
|
16
|
+
|
|
17
|
+
Upstream phyworld's published HDF5 carries no metadata, so it needs its family
|
|
18
|
+
named:
|
|
19
|
+
|
|
20
|
+
from eclise.clips.readers import phyworld_hdf5
|
|
21
|
+
|
|
22
|
+
shards = phyworld_hdf5.open_shard_set("collision_30K.hdf5", family="collision")
|
|
23
|
+
data = ClipDataset(shards, velocity_object=0)
|
|
24
|
+
|
|
25
|
+
Writing is the mirror image: declare a
|
|
26
|
+
:class:`~eclise.clips.schema.ShardMeta`, then append clips.
|
|
27
|
+
|
|
28
|
+
from eclise.clips import ShardMeta, ShardWriter, position_spec
|
|
29
|
+
|
|
30
|
+
meta = ShardMeta(
|
|
31
|
+
family="uniform_motion", num_clips=0, clip_len=32,
|
|
32
|
+
image_size=(256, 256), frame_dt=0.1, world_scale=10.0,
|
|
33
|
+
labels={"positions": position_spec()},
|
|
34
|
+
)
|
|
35
|
+
with ShardWriter("shard_000.hdf5", meta) as w:
|
|
36
|
+
w.append(frames, positions=positions)
|
|
37
|
+
|
|
38
|
+
Torch is needed only for :class:`~eclise.clips.dataset.ClipDataset`; the schema,
|
|
39
|
+
shard, and reader layers are pure numpy.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
from __future__ import annotations
|
|
43
|
+
|
|
44
|
+
from .codec import decode_mp4, encode_mp4
|
|
45
|
+
from .labels import (
|
|
46
|
+
collision_frame,
|
|
47
|
+
positions_to_pixels,
|
|
48
|
+
velocities_per_frame,
|
|
49
|
+
velocity_from_state,
|
|
50
|
+
)
|
|
51
|
+
from .schema import (
|
|
52
|
+
LABEL_GROUP,
|
|
53
|
+
META_ATTR,
|
|
54
|
+
SCHEMA_VERSION,
|
|
55
|
+
VIDEO_DATASET,
|
|
56
|
+
LabelSpec,
|
|
57
|
+
SchemaError,
|
|
58
|
+
ShardMeta,
|
|
59
|
+
object_attrs_spec,
|
|
60
|
+
params_spec,
|
|
61
|
+
position_spec,
|
|
62
|
+
velocity_field_spec,
|
|
63
|
+
)
|
|
64
|
+
from .shards import ShardReader, ShardSet, ShardWriter, write_shard
|
|
65
|
+
|
|
66
|
+
__all__ = [
|
|
67
|
+
"LABEL_GROUP",
|
|
68
|
+
"META_ATTR",
|
|
69
|
+
"SCHEMA_VERSION",
|
|
70
|
+
"VIDEO_DATASET",
|
|
71
|
+
"LabelSpec",
|
|
72
|
+
"SchemaError",
|
|
73
|
+
"ShardMeta",
|
|
74
|
+
"ShardReader",
|
|
75
|
+
"ShardSet",
|
|
76
|
+
"ShardWriter",
|
|
77
|
+
"collision_frame",
|
|
78
|
+
"decode_mp4",
|
|
79
|
+
"encode_mp4",
|
|
80
|
+
"object_attrs_spec",
|
|
81
|
+
"params_spec",
|
|
82
|
+
"position_spec",
|
|
83
|
+
"positions_to_pixels",
|
|
84
|
+
"velocities_per_frame",
|
|
85
|
+
"velocity_field_spec",
|
|
86
|
+
"velocity_from_state",
|
|
87
|
+
"write_shard",
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def __getattr__(name: str):
|
|
92
|
+
"""Expose :class:`~eclise.clips.dataset.ClipDataset` without importing torch eagerly."""
|
|
93
|
+
if name in ("ClipDataset", "iter_clip_batches", "PIXEL_RANGES"):
|
|
94
|
+
from . import dataset
|
|
95
|
+
|
|
96
|
+
return getattr(dataset, name)
|
|
97
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""In-memory mp4 encode/decode for clip bytestreams.
|
|
2
|
+
|
|
3
|
+
Upstream phyworld carries a copy of this logic in every generator script (four
|
|
4
|
+
copies of ``convert_frames_to_mp4_bytestream_wo_disk``, plus disk-roundtripping
|
|
5
|
+
variants that were left behind after the in-memory version replaced them). This
|
|
6
|
+
is the one home for it.
|
|
7
|
+
|
|
8
|
+
Encoding is lossy. H.264 on synthetic content -- flat backgrounds with a few
|
|
9
|
+
hard-edged coloured circles -- is close to visually lossless at sane bitrates,
|
|
10
|
+
but "close" is not "exact", and every consumer of a decoded clip is looking at
|
|
11
|
+
compression artifacts. That is a property of the storage format we inherit, not
|
|
12
|
+
something this module can fix; it is why
|
|
13
|
+
``data/phyworld/scripts/verify_shards.py`` measures state recovery *after* a
|
|
14
|
+
decode round-trip rather than trusting the pre-encode frames.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from io import BytesIO
|
|
20
|
+
|
|
21
|
+
import numpy as np
|
|
22
|
+
|
|
23
|
+
#: Encoder quality knob. imageio/ffmpeg's default CRF is 23, visibly soft on
|
|
24
|
+
#: hard synthetic edges; 18 is near-transparent for this content at a modest
|
|
25
|
+
#: size increase. Applies only to clips this package writes.
|
|
26
|
+
DEFAULT_CRF = 18
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def encode_mp4(frames: np.ndarray, *, crf: int = DEFAULT_CRF, fps: float = 24.0) -> np.ndarray:
|
|
30
|
+
"""Encode ``[T, H, W, 3]`` uint8 frames to an mp4 bytestream as ``[n] uint8``.
|
|
31
|
+
|
|
32
|
+
``fps`` lands in the container header only. It is *not* the physics frame
|
|
33
|
+
rate -- that is :attr:`eclise.clips.schema.ShardMeta.frame_dt`, recorded
|
|
34
|
+
separately. A value is passed anyway so the header is at least a valid,
|
|
35
|
+
deliberate number rather than an encoder default (upstream passes nothing,
|
|
36
|
+
which is how its stored clips ended up with a meaningless rate).
|
|
37
|
+
|
|
38
|
+
H.264 requires even frame dimensions; odd ones are rejected here rather than
|
|
39
|
+
silently rescaled, since a resized clip would invalidate every pixel-unit
|
|
40
|
+
label in the shard.
|
|
41
|
+
"""
|
|
42
|
+
import imageio.v3 as iio
|
|
43
|
+
|
|
44
|
+
arr = np.asarray(frames)
|
|
45
|
+
if arr.ndim != 4 or arr.shape[-1] != 3:
|
|
46
|
+
raise ValueError(f"expected frames [T, H, W, 3], got {arr.shape}")
|
|
47
|
+
if arr.dtype != np.uint8:
|
|
48
|
+
raise ValueError(f"expected uint8 frames, got {arr.dtype}")
|
|
49
|
+
h, w = arr.shape[1:3]
|
|
50
|
+
if h % 2 or w % 2:
|
|
51
|
+
raise ValueError(
|
|
52
|
+
f"H.264 needs even frame dimensions; got {h}x{w}. Render at an even "
|
|
53
|
+
"size rather than resizing here, which would invalidate pixel labels."
|
|
54
|
+
)
|
|
55
|
+
with BytesIO() as buffer:
|
|
56
|
+
iio.imwrite(
|
|
57
|
+
buffer,
|
|
58
|
+
arr,
|
|
59
|
+
extension=".mp4",
|
|
60
|
+
fps=fps,
|
|
61
|
+
codec="libx264",
|
|
62
|
+
# The pixel format goes through imageio's own keyword, not
|
|
63
|
+
# output_params: the ffmpeg plugin always emits a -pix_fmt of its own,
|
|
64
|
+
# so passing a second one makes ffmpeg warn once per encoded clip --
|
|
65
|
+
# millions of stderr lines on a full generation run, for a flag that
|
|
66
|
+
# was already set to the same value.
|
|
67
|
+
pixelformat="yuv420p",
|
|
68
|
+
output_params=["-crf", str(crf)],
|
|
69
|
+
)
|
|
70
|
+
payload = buffer.getvalue()
|
|
71
|
+
return np.frombuffer(payload, dtype=np.uint8)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def decode_mp4(stream: np.ndarray | bytes) -> np.ndarray:
|
|
75
|
+
"""Decode an mp4 bytestream to ``[T, H, W, 3]`` uint8 frames."""
|
|
76
|
+
import imageio.v3 as iio
|
|
77
|
+
|
|
78
|
+
payload = stream.tobytes() if isinstance(stream, np.ndarray) else stream
|
|
79
|
+
frames = iio.imread(payload, index=None, extension=".mp4")
|
|
80
|
+
arr = np.asarray(frames)
|
|
81
|
+
if arr.ndim == 3: # a single-frame clip decodes without the leading axis
|
|
82
|
+
arr = arr[None]
|
|
83
|
+
if arr.shape[-1] == 4: # some decoders hand back RGBA
|
|
84
|
+
arr = arr[..., :3]
|
|
85
|
+
return arr
|