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.
Files changed (116) hide show
  1. eclise-0.1.0/CHANGELOG.md +25 -0
  2. eclise-0.1.0/LICENSE +21 -0
  3. eclise-0.1.0/MANIFEST.in +6 -0
  4. eclise-0.1.0/PKG-INFO +183 -0
  5. eclise-0.1.0/README.md +117 -0
  6. eclise-0.1.0/data/clips/src/eclise/clips/__init__.py +97 -0
  7. eclise-0.1.0/data/clips/src/eclise/clips/codec.py +85 -0
  8. eclise-0.1.0/data/clips/src/eclise/clips/dataset.py +237 -0
  9. eclise-0.1.0/data/clips/src/eclise/clips/labels.py +198 -0
  10. eclise-0.1.0/data/clips/src/eclise/clips/readers/__init__.py +24 -0
  11. eclise-0.1.0/data/clips/src/eclise/clips/readers/phyworld_hdf5.py +489 -0
  12. eclise-0.1.0/data/clips/src/eclise/clips/schema.py +302 -0
  13. eclise-0.1.0/data/clips/src/eclise/clips/shards.py +487 -0
  14. eclise-0.1.0/data/datasets/src/eclise/datasets/__init__.py +17 -0
  15. eclise-0.1.0/data/datasets/src/eclise/datasets/_resources/imagenet_classes.json +5250 -0
  16. eclise-0.1.0/data/datasets/src/eclise/datasets/imagenet.py +131 -0
  17. eclise-0.1.0/data/datasets/src/eclise/datasets/labels.py +65 -0
  18. eclise-0.1.0/data/openvid/src/eclise/openvid/__init__.py +58 -0
  19. eclise-0.1.0/data/openvid/src/eclise/openvid/config.py +183 -0
  20. eclise-0.1.0/data/openvid/src/eclise/openvid/hub.py +178 -0
  21. eclise-0.1.0/data/openvid/src/eclise/openvid/ingest.py +526 -0
  22. eclise-0.1.0/data/openvid/src/eclise/openvid/metadata.py +342 -0
  23. eclise-0.1.0/data/openvid/src/eclise/openvid/split.py +33 -0
  24. eclise-0.1.0/data/openvid/src/eclise/openvid/video.py +291 -0
  25. eclise-0.1.0/data/phyworld/src/eclise/phyworld/__init__.py +66 -0
  26. eclise-0.1.0/data/phyworld/src/eclise/phyworld/analysis/__init__.py +41 -0
  27. eclise-0.1.0/data/phyworld/src/eclise/phyworld/analysis/metrics.py +201 -0
  28. eclise-0.1.0/data/phyworld/src/eclise/phyworld/analysis/state_recovery.py +275 -0
  29. eclise-0.1.0/data/phyworld/src/eclise/phyworld/analysis/verify.py +213 -0
  30. eclise-0.1.0/data/phyworld/src/eclise/phyworld/combinatorial_driver.py +302 -0
  31. eclise-0.1.0/data/phyworld/src/eclise/phyworld/config.py +353 -0
  32. eclise-0.1.0/data/phyworld/src/eclise/phyworld/generate.py +447 -0
  33. eclise-0.1.0/data/phyworld/src/eclise/phyworld/grids.py +368 -0
  34. eclise-0.1.0/data/phyworld/src/eclise/phyworld/render.py +477 -0
  35. eclise-0.1.0/data/phyworld/src/eclise/phyworld/scenes/__init__.py +51 -0
  36. eclise-0.1.0/data/phyworld/src/eclise/phyworld/scenes/base.py +177 -0
  37. eclise-0.1.0/data/phyworld/src/eclise/phyworld/scenes/collision.py +192 -0
  38. eclise-0.1.0/data/phyworld/src/eclise/phyworld/scenes/combinatorial.py +386 -0
  39. eclise-0.1.0/data/phyworld/src/eclise/phyworld/scenes/parabola.py +92 -0
  40. eclise-0.1.0/data/phyworld/src/eclise/phyworld/scenes/phyre_compat.py +400 -0
  41. eclise-0.1.0/data/phyworld/src/eclise/phyworld/scenes/uniform_motion.py +207 -0
  42. eclise-0.1.0/eclise.egg-info/PKG-INFO +183 -0
  43. eclise-0.1.0/eclise.egg-info/SOURCES.txt +114 -0
  44. eclise-0.1.0/eclise.egg-info/dependency_links.txt +1 -0
  45. eclise-0.1.0/eclise.egg-info/requires.txt +52 -0
  46. eclise-0.1.0/eclise.egg-info/top_level.txt +1 -0
  47. eclise-0.1.0/launch/src/eclise/launch/__init__.py +24 -0
  48. eclise-0.1.0/launch/src/eclise/launch/__main__.py +125 -0
  49. eclise-0.1.0/launch/src/eclise/launch/_version.py +3 -0
  50. eclise-0.1.0/launch/src/eclise/launch/errors.py +5 -0
  51. eclise-0.1.0/launch/src/eclise/launch/launcher.py +216 -0
  52. eclise-0.1.0/launch/src/eclise/launch/pixi.py +181 -0
  53. eclise-0.1.0/launch/src/eclise/launch/registry.py +69 -0
  54. eclise-0.1.0/launch/src/eclise/launch/root.py +153 -0
  55. eclise-0.1.0/models/robotics/libero/src/eclise/libero/__init__.py +27 -0
  56. eclise-0.1.0/models/robotics/libero/src/eclise/libero/evaluation/__init__.py +11 -0
  57. eclise-0.1.0/models/robotics/libero/src/eclise/libero/evaluation/config.py +63 -0
  58. eclise-0.1.0/models/robotics/libero/src/eclise/libero/evaluation/metrics.py +36 -0
  59. eclise-0.1.0/models/robotics/libero/src/eclise/libero/evaluation/runner.py +174 -0
  60. eclise-0.1.0/models/robotics/libero/src/eclise/libero/harness/__init__.py +18 -0
  61. eclise-0.1.0/models/robotics/libero/src/eclise/libero/harness/env.py +88 -0
  62. eclise-0.1.0/models/robotics/libero/src/eclise/libero/harness/libero_env.py +107 -0
  63. eclise-0.1.0/models/robotics/libero/src/eclise/libero/harness/rollout.py +14 -0
  64. eclise-0.1.0/models/robotics/libero/src/eclise/libero/observation.py +32 -0
  65. eclise-0.1.0/models/robotics/libero/src/eclise/libero/serve.py +230 -0
  66. eclise-0.1.0/models/robotics/octo/src/eclise/octo/__init__.py +14 -0
  67. eclise-0.1.0/models/robotics/octo/src/eclise/octo/obs_adapter.py +190 -0
  68. eclise-0.1.0/models/robotics/octo/src/eclise/octo/policy.py +133 -0
  69. eclise-0.1.0/models/robotics/octo/src/eclise/octo/serve.py +76 -0
  70. eclise-0.1.0/models/robotics/octo/src/eclise/octo/training/__init__.py +39 -0
  71. eclise-0.1.0/models/robotics/octo/src/eclise/octo/training/config.py +76 -0
  72. eclise-0.1.0/models/robotics/octo/src/eclise/octo/training/data.py +507 -0
  73. eclise-0.1.0/models/robotics/octo/src/eclise/octo/training/step.py +47 -0
  74. eclise-0.1.0/models/robotics/octo/src/eclise/octo/training/train_state.py +102 -0
  75. eclise-0.1.0/models/robotics/octo/src/eclise/octo/training/transforms.py +53 -0
  76. eclise-0.1.0/models/robotics/pi0/src/eclise/pi0/__init__.py +17 -0
  77. eclise-0.1.0/models/robotics/serving/src/eclise/vla/__init__.py +74 -0
  78. eclise-0.1.0/models/robotics/serving/src/eclise/vla/_ws.py +23 -0
  79. eclise-0.1.0/models/robotics/serving/src/eclise/vla/env.py +70 -0
  80. eclise-0.1.0/models/robotics/serving/src/eclise/vla/env_client.py +104 -0
  81. eclise-0.1.0/models/robotics/serving/src/eclise/vla/env_server.py +134 -0
  82. eclise-0.1.0/models/robotics/serving/src/eclise/vla/msgpack_numpy.py +58 -0
  83. eclise-0.1.0/models/robotics/serving/src/eclise/vla/observation.py +78 -0
  84. eclise-0.1.0/models/robotics/serving/src/eclise/vla/policy.py +60 -0
  85. eclise-0.1.0/models/robotics/serving/src/eclise/vla/policy_client.py +95 -0
  86. eclise-0.1.0/models/robotics/serving/src/eclise/vla/rollout.py +181 -0
  87. eclise-0.1.0/models/robotics/serving/src/eclise/vla/serve_dummy.py +30 -0
  88. eclise-0.1.0/models/robotics/serving/src/eclise/vla/server.py +93 -0
  89. eclise-0.1.0/models/robotics/smolvla/src/eclise/smolvla/__init__.py +1 -0
  90. eclise-0.1.0/models/robotics/smolvla/src/eclise/smolvla/obs_adapter.py +89 -0
  91. eclise-0.1.0/models/robotics/smolvla/src/eclise/smolvla/policy.py +118 -0
  92. eclise-0.1.0/models/robotics/smolvla/src/eclise/smolvla/serve.py +67 -0
  93. eclise-0.1.0/pixi.lock +10377 -0
  94. eclise-0.1.0/pixi.toml +229 -0
  95. eclise-0.1.0/pyproject.toml +100 -0
  96. eclise-0.1.0/setup.cfg +4 -0
  97. eclise-0.1.0/setup.py +59 -0
  98. eclise-0.1.0/utils/src/eclise/utils/__init__.py +103 -0
  99. eclise-0.1.0/utils/src/eclise/utils/are.py +409 -0
  100. eclise-0.1.0/utils/src/eclise/utils/arrays.py +93 -0
  101. eclise-0.1.0/utils/src/eclise/utils/dicts.py +85 -0
  102. eclise-0.1.0/utils/src/eclise/utils/ensemble.py +137 -0
  103. eclise-0.1.0/utils/src/eclise/utils/fast_conv_scan.py +171 -0
  104. eclise-0.1.0/utils/src/eclise/utils/io.py +40 -0
  105. eclise-0.1.0/utils/src/eclise/utils/labeled_array.py +213 -0
  106. eclise-0.1.0/utils/src/eclise/utils/linalg.py +285 -0
  107. eclise-0.1.0/utils/src/eclise/utils/memory.py +61 -0
  108. eclise-0.1.0/utils/src/eclise/utils/modules.py +243 -0
  109. eclise-0.1.0/utils/src/eclise/utils/ode.py +77 -0
  110. eclise-0.1.0/utils/src/eclise/utils/optim.py +34 -0
  111. eclise-0.1.0/utils/src/eclise/utils/plotting.py +50 -0
  112. eclise-0.1.0/utils/src/eclise/utils/recursive.py +50 -0
  113. eclise-0.1.0/utils/src/eclise/utils/scan.py +67 -0
  114. eclise-0.1.0/utils/src/eclise/utils/settings.py +137 -0
  115. eclise-0.1.0/utils/src/eclise/utils/timing.py +96 -0
  116. 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.
@@ -0,0 +1,6 @@
1
+ # Extra files for the sdist. Wheels are unaffected (pixi files are never
2
+ # installed); shipping them makes the sdist a self-contained description of
3
+ # the environments that `eclise.launch` provisions.
4
+ include pixi.toml
5
+ include pixi.lock
6
+ include CHANGELOG.md
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