rexs 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 (37) hide show
  1. rexs-0.1.0/PKG-INFO +278 -0
  2. rexs-0.1.0/README.md +256 -0
  3. rexs-0.1.0/pyproject.toml +39 -0
  4. rexs-0.1.0/setup.cfg +4 -0
  5. rexs-0.1.0/src/rexs/__init__.py +14 -0
  6. rexs-0.1.0/src/rexs/__main__.py +4 -0
  7. rexs-0.1.0/src/rexs/auth.py +48 -0
  8. rexs-0.1.0/src/rexs/beaker.py +47 -0
  9. rexs-0.1.0/src/rexs/cli.py +363 -0
  10. rexs-0.1.0/src/rexs/compiler.py +694 -0
  11. rexs-0.1.0/src/rexs/config.py +136 -0
  12. rexs-0.1.0/src/rexs/controller.py +258 -0
  13. rexs-0.1.0/src/rexs/dryrun.py +164 -0
  14. rexs-0.1.0/src/rexs/errors.py +10 -0
  15. rexs-0.1.0/src/rexs/links.py +28 -0
  16. rexs-0.1.0/src/rexs/metrics.py +287 -0
  17. rexs-0.1.0/src/rexs/resources.py +85 -0
  18. rexs-0.1.0/src/rexs/server.py +455 -0
  19. rexs-0.1.0/src/rexs/state.py +412 -0
  20. rexs-0.1.0/src/rexs/static/rexs-logo.png +0 -0
  21. rexs-0.1.0/src/rexs/web.py +611 -0
  22. rexs-0.1.0/src/rexs.egg-info/PKG-INFO +278 -0
  23. rexs-0.1.0/src/rexs.egg-info/SOURCES.txt +35 -0
  24. rexs-0.1.0/src/rexs.egg-info/dependency_links.txt +1 -0
  25. rexs-0.1.0/src/rexs.egg-info/entry_points.txt +2 -0
  26. rexs-0.1.0/src/rexs.egg-info/requires.txt +14 -0
  27. rexs-0.1.0/src/rexs.egg-info/top_level.txt +1 -0
  28. rexs-0.1.0/tests/test_beaker_evaluation.py +38 -0
  29. rexs-0.1.0/tests/test_cli.py +32 -0
  30. rexs-0.1.0/tests/test_compiler.py +383 -0
  31. rexs-0.1.0/tests/test_controller.py +312 -0
  32. rexs-0.1.0/tests/test_dryrun.py +108 -0
  33. rexs-0.1.0/tests/test_gpu_assignment.py +26 -0
  34. rexs-0.1.0/tests/test_links.py +39 -0
  35. rexs-0.1.0/tests/test_metrics.py +181 -0
  36. rexs-0.1.0/tests/test_resources.py +59 -0
  37. rexs-0.1.0/tests/test_server_auth.py +123 -0
rexs-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,278 @@
1
+ Metadata-Version: 2.4
2
+ Name: rexs
3
+ Version: 0.1.0
4
+ Summary: Reproducible Experiments, eXecuted on Slurm
5
+ Project-URL: Homepage, https://github.com/goncalorafaria/rexs
6
+ Project-URL: Documentation, https://goncalorafaria.github.io/rexs/
7
+ Project-URL: Repository, https://github.com/goncalorafaria/rexs
8
+ Project-URL: Issues, https://github.com/goncalorafaria/rexs/issues
9
+ Requires-Python: >=3.11
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: fire>=0.7
12
+ Requires-Dist: PyYAML>=6.0
13
+ Provides-Extra: wandb
14
+ Requires-Dist: wandb<0.29,>=0.19; extra == "wandb"
15
+ Provides-Extra: dev
16
+ Requires-Dist: pytest>=8.0; extra == "dev"
17
+ Requires-Dist: ruff>=0.8; extra == "dev"
18
+ Requires-Dist: wandb<0.29,>=0.19; extra == "dev"
19
+ Provides-Extra: docs
20
+ Requires-Dist: mkdocs>=1.6; extra == "docs"
21
+ Requires-Dist: mkdocs-material>=9.6; extra == "docs"
22
+
23
+ <div align="center">
24
+ <img src="src/rexs/static/rexs-logo.png" width="160" alt="REXS green T-rex badge">
25
+ <h1>REXS</h1>
26
+ <p><strong>Reproducible Experiments, eXecuted on Slurm.</strong></p>
27
+ <p>Run the Beaker experiment configurations you already have on Slurm and Apptainer.</p>
28
+
29
+ [![CI](https://github.com/goncalorafaria/rexs/actions/workflows/ci.yml/badge.svg)](https://github.com/goncalorafaria/rexs/actions/workflows/ci.yml)
30
+ [![Docs](https://github.com/goncalorafaria/rexs/actions/workflows/pages.yml/badge.svg)](https://goncalorafaria.github.io/rexs/)
31
+ [![Python](https://img.shields.io/badge/python-3.11%2B-3776ab.svg)](https://www.python.org/)
32
+ [![License: MIT](https://img.shields.io/badge/license-MIT-8bd5ff.svg)](LICENSE)
33
+ </div>
34
+
35
+ REXS is a small compatibility layer between Beaker v2 experiment files and a
36
+ Slurm cluster. It compiles each experiment into an auditable `sbatch` script,
37
+ runs every task replica as an exclusive `srun` step inside Apptainer, and uses
38
+ Slurm itself as the execution engine. A lightweight SQLite controller records
39
+ the submitted configuration, job state, task replicas, logs, and lifecycle
40
+ events.
41
+
42
+ It focuses deliberately on the Beaker features exercised by `datadev` and
43
+ LiteRegistry. Unsupported fields produce warnings; `--strict` promotes any
44
+ lossy translation to an error before submission.
45
+
46
+ ## How it works
47
+
48
+ ```mermaid
49
+ flowchart LR
50
+ A[Beaker v2<br>YAML / JSON] --> B[REXS compiler]
51
+ P[Site profile<br>images · mounts · Slurm] --> B
52
+ B --> C[Auditable<br>sbatch script]
53
+ C --> D[Slurm allocation]
54
+ D --> E1[srun task 0<br>Apptainer]
55
+ D --> E2[srun task 1<br>Apptainer]
56
+ D --> EN[srun task N<br>Apptainer]
57
+ D --> S[(SQLite state)]
58
+ S --> UI[CLI + web dashboard]
59
+ ```
60
+
61
+ One Beaker experiment becomes one Slurm allocation. Task replicas are assigned
62
+ to nodes deterministically, receive Beaker-compatible replica environment
63
+ variables, share the host network, and write individual logs and result trees.
64
+ If one replica fails, REXS terminates its siblings and exits the allocation
65
+ with a failure.
66
+
67
+ ## Install
68
+
69
+ ```bash
70
+ git clone https://github.com/goncalorafaria/rexs.git
71
+ cd rexs
72
+ python -m venv .venv
73
+ . .venv/bin/activate
74
+ pip install -e .
75
+ ```
76
+
77
+ The submission host needs `sbatch`, `squeue`, `sacct`, and `scancel`. Compute
78
+ nodes need `srun`, `scontrol`, and `apptainer`.
79
+
80
+ ## Quick start
81
+
82
+ Copy the sample profile and adapt it to your Slurm site:
83
+
84
+ ```bash
85
+ cp examples/profile.yaml profile.yaml
86
+ $EDITOR profile.yaml
87
+ ```
88
+
89
+ Validate and render without touching Slurm:
90
+
91
+ ```bash
92
+ rexs validate experiment.yaml --profile profile.yaml
93
+ rexs render experiment.yaml \
94
+ --profile profile.yaml \
95
+ --output experiment.sbatch
96
+ bash -n experiment.sbatch
97
+ ```
98
+
99
+ Submit and inspect the run:
100
+
101
+ ```bash
102
+ rexs submit experiment.yaml --profile profile.yaml --name my-run --strict
103
+ rexs experiments # queued and running only
104
+ rexs experiments --all # include finished experiments
105
+ rexs status 123456
106
+ rexs logs 123456 --task=trainer --replica=0
107
+ rexs cancel 123456
108
+ ```
109
+
110
+ Start the dashboard in the background:
111
+
112
+ ```bash
113
+ rexs server --host=127.0.0.1 --port=8765 --daemon
114
+ # Open http://127.0.0.1:8765
115
+ rexs server_stop
116
+ ```
117
+
118
+ The dashboard and all API routes require HTTP Basic authentication by default.
119
+ Use username `rexs`. On first start, REXS generates a unique password and saves it
120
+ with owner-only permissions in `~/.local/state/rexs/server.password` (beside a
121
+ custom state database when `--db` is used). Read it with:
122
+
123
+ ```bash
124
+ cat ~/.local/state/rexs/server.password
125
+ ```
126
+
127
+ The saved password persists across restarts. Set `REXS_SERVER_PASSWORD` in the
128
+ server's environment to override it; empty passwords are rejected. The password
129
+ is never included in server URLs, process arguments, or logs. After changing a
130
+ password, restart the server and sign in again. Authenticated API POST requests
131
+ must also send `X-REXS-Request: 1`; the dashboard adds this automatically.
132
+ Keep the loopback binding and access it through SSH port forwarding. Basic
133
+ authentication should use HTTPS if exposing the server beyond that tunnel.
134
+
135
+ The dashboard shows searchable experiment history, status filters, submitted
136
+ YAML, state transitions, task-level logs, and guarded cancellation.
137
+
138
+ ## Site profile
139
+
140
+ Experiment intent remains in the Beaker file. Cluster policy belongs in a
141
+ separate REXS profile:
142
+
143
+ ```yaml
144
+ account: research
145
+ partition: gpu
146
+ qos: normal
147
+ time_limit: "24:00:00"
148
+ cpus_per_task: 8
149
+ memory: 64G
150
+
151
+ apptainer_binary: apptainer
152
+ image_cache: /shared/rexs/images
153
+ image_dir: /shared/apptainer/images
154
+ dataset_root: /shared/datasets
155
+ run_root: /shared/rexs/runs
156
+ secret_file: ~/.config/rexs/secrets.env
157
+
158
+ setup_commands:
159
+ - module load apptainer
160
+
161
+ images:
162
+ org/trainer: /shared/apptainer/images/trainer.sif
163
+ org/service: docker://ghcr.io/org/service:latest
164
+
165
+ datasets:
166
+ weka:shared-data: /shared/data
167
+ ```
168
+
169
+ Docker references are converted to `docker://` URIs automatically. Beaker
170
+ image names should be mapped to an OCI URI or existing SIF. Unmapped resources
171
+ fall back to predictable paths and generate warnings.
172
+
173
+ Secrets are loaded inside the allocation from `secret_file`; their values are
174
+ never written to the generated script or SQLite database.
175
+
176
+ ## Supported Beaker subset
177
+
178
+ - Beaker v2 YAML and JSON
179
+ - `image.beaker` and `image.docker`
180
+ - command plus arguments
181
+ - plain and secret-backed environment variables
182
+ - task replicas and leader selection
183
+ - Beaker replica/job/hostname compatibility variables
184
+ - Weka, host-path, and mapped Beaker dataset mounts
185
+ - persistent per-replica result directories
186
+ - GPU, CPU, memory, and shared-memory advisories
187
+ - host-networked multi-service deployments
188
+ - `${VARIABLE}` configuration substitution
189
+
190
+ Workspace, budget, cluster constraints, priority, retry policy, scheduler
191
+ groups, and preemption metadata are not translated. Slurm policy comes only
192
+ from the site profile. See the [compatibility reference](docs/compatibility.md)
193
+ for exact behavior.
194
+
195
+ ## Dry-run a repository
196
+
197
+ REXS can audit a directory full of experiment configurations without a Slurm
198
+ installation. It compiles every Beaker v2 YAML/JSON file and checks the output
199
+ with `bash -n`:
200
+
201
+ ```bash
202
+ rexs dry_run ~/datadev/beaker_experiments
203
+ rexs dry_run ~/datadev/beaker_experiments --strict --details
204
+ rexs dry_run ~/other/experiments --output_dir=/tmp/rexs-scripts
205
+ ```
206
+
207
+ No Slurm command is invoked in dry-run mode.
208
+
209
+ ## State and output
210
+
211
+ Global state defaults to `~/.local/state/rexs/state.sqlite3` and can be changed
212
+ with `--db` or `REXS_STATE_DB`. SQLite uses WAL mode so the CLI and background
213
+ server can safely share it.
214
+
215
+ Runtime files are organized as:
216
+
217
+ ```text
218
+ <run-root>/<slurm-job-id>/
219
+ ├── logs/<task>.<replica>.log
220
+ └── results/<task>/<replica>/
221
+ ```
222
+
223
+ Slurm remains authoritative. The controller reconciles active records through
224
+ `squeue` and completed records through `sacct`.
225
+
226
+ ## Optional W&B capture
227
+
228
+ Install `pip install -e '.[wandb]'` to let the REXS server capture local W&B
229
+ logs automatically. Every 30 seconds it scans registered jobs' output directories
230
+ for `.wandb` files, including offline runs. No W&B login or upload is required.
231
+ Jobs without W&B continue normally; this does not change their launch commands.
232
+
233
+ Open a run and select **GPU metrics**, after **History**. Each GPU card plots utilization and allocated memory over time, with hover
234
+ values and the latest sample. Long histories retain extrema when reduced for display.
235
+ Samples remain available after the job finishes. Missing values display a dash;
236
+ stale samples are marked. GPU indices are local to each logger, so separate
237
+ sources may report the same physical devices.
238
+
239
+ Scalar history, summary and system metrics, plus W&B console output records,
240
+ are saved incrementally in the existing SQLite database (`wandb_sources`,
241
+ `wandb_records`, and `wandb_gpu_latest`). Incomplete trailing records are retried
242
+ on the next scan. Original files are read only. This is not a W&B artifact or
243
+ media archive; capture requires the log files to remain accessible to the server.
244
+ Set `REXS_CAPTURE_WANDB=0` before starting the server to disable collection
245
+ without deleting saved samples. Source errors do not interrupt jobs.
246
+
247
+ ## Documentation
248
+
249
+ The full documentation lives in [`docs/`](docs/index.md) and is published with
250
+ GitHub Pages at **https://goncalorafaria.github.io/rexs/**.
251
+
252
+ To preview it locally:
253
+
254
+ ```bash
255
+ pip install -e '.[docs]'
256
+ mkdocs serve
257
+ ```
258
+
259
+ ## Development
260
+
261
+ ```bash
262
+ pip install -e '.[dev,docs]'
263
+ pytest -q
264
+ ruff check src tests
265
+ mkdocs build --strict
266
+ ```
267
+
268
+ REXS is a clean-slate implementation inspired by
269
+ [`slurmcompose`](https://github.com/goncalorafaria/slurmcompose), scoped around
270
+ reproducible Beaker-to-Slurm execution rather than a fixed service composition.
271
+
272
+ ## License
273
+
274
+ [MIT](LICENSE)
275
+
276
+ ### GPU isolation with shared CPUs
277
+
278
+ With `shared_cpus_per_node`, CPU-only service steps use `--overlap --gres=none`. GPU steps use `--exclusive --exact --gpus-per-task=N --gpus-per-node=N` and their own CPU count, allowing Slurm to allocate disjoint GPUs to concurrent model replicas. GPU steps must fit together within the shared CPU budget. The container wrapper must preserve Slurm's `CUDA_VISIBLE_DEVICES` through `APPTAINERENV_CUDA_VISIBLE_DEVICES` when using `--cleanenv`.
rexs-0.1.0/README.md ADDED
@@ -0,0 +1,256 @@
1
+ <div align="center">
2
+ <img src="src/rexs/static/rexs-logo.png" width="160" alt="REXS green T-rex badge">
3
+ <h1>REXS</h1>
4
+ <p><strong>Reproducible Experiments, eXecuted on Slurm.</strong></p>
5
+ <p>Run the Beaker experiment configurations you already have on Slurm and Apptainer.</p>
6
+
7
+ [![CI](https://github.com/goncalorafaria/rexs/actions/workflows/ci.yml/badge.svg)](https://github.com/goncalorafaria/rexs/actions/workflows/ci.yml)
8
+ [![Docs](https://github.com/goncalorafaria/rexs/actions/workflows/pages.yml/badge.svg)](https://goncalorafaria.github.io/rexs/)
9
+ [![Python](https://img.shields.io/badge/python-3.11%2B-3776ab.svg)](https://www.python.org/)
10
+ [![License: MIT](https://img.shields.io/badge/license-MIT-8bd5ff.svg)](LICENSE)
11
+ </div>
12
+
13
+ REXS is a small compatibility layer between Beaker v2 experiment files and a
14
+ Slurm cluster. It compiles each experiment into an auditable `sbatch` script,
15
+ runs every task replica as an exclusive `srun` step inside Apptainer, and uses
16
+ Slurm itself as the execution engine. A lightweight SQLite controller records
17
+ the submitted configuration, job state, task replicas, logs, and lifecycle
18
+ events.
19
+
20
+ It focuses deliberately on the Beaker features exercised by `datadev` and
21
+ LiteRegistry. Unsupported fields produce warnings; `--strict` promotes any
22
+ lossy translation to an error before submission.
23
+
24
+ ## How it works
25
+
26
+ ```mermaid
27
+ flowchart LR
28
+ A[Beaker v2<br>YAML / JSON] --> B[REXS compiler]
29
+ P[Site profile<br>images · mounts · Slurm] --> B
30
+ B --> C[Auditable<br>sbatch script]
31
+ C --> D[Slurm allocation]
32
+ D --> E1[srun task 0<br>Apptainer]
33
+ D --> E2[srun task 1<br>Apptainer]
34
+ D --> EN[srun task N<br>Apptainer]
35
+ D --> S[(SQLite state)]
36
+ S --> UI[CLI + web dashboard]
37
+ ```
38
+
39
+ One Beaker experiment becomes one Slurm allocation. Task replicas are assigned
40
+ to nodes deterministically, receive Beaker-compatible replica environment
41
+ variables, share the host network, and write individual logs and result trees.
42
+ If one replica fails, REXS terminates its siblings and exits the allocation
43
+ with a failure.
44
+
45
+ ## Install
46
+
47
+ ```bash
48
+ git clone https://github.com/goncalorafaria/rexs.git
49
+ cd rexs
50
+ python -m venv .venv
51
+ . .venv/bin/activate
52
+ pip install -e .
53
+ ```
54
+
55
+ The submission host needs `sbatch`, `squeue`, `sacct`, and `scancel`. Compute
56
+ nodes need `srun`, `scontrol`, and `apptainer`.
57
+
58
+ ## Quick start
59
+
60
+ Copy the sample profile and adapt it to your Slurm site:
61
+
62
+ ```bash
63
+ cp examples/profile.yaml profile.yaml
64
+ $EDITOR profile.yaml
65
+ ```
66
+
67
+ Validate and render without touching Slurm:
68
+
69
+ ```bash
70
+ rexs validate experiment.yaml --profile profile.yaml
71
+ rexs render experiment.yaml \
72
+ --profile profile.yaml \
73
+ --output experiment.sbatch
74
+ bash -n experiment.sbatch
75
+ ```
76
+
77
+ Submit and inspect the run:
78
+
79
+ ```bash
80
+ rexs submit experiment.yaml --profile profile.yaml --name my-run --strict
81
+ rexs experiments # queued and running only
82
+ rexs experiments --all # include finished experiments
83
+ rexs status 123456
84
+ rexs logs 123456 --task=trainer --replica=0
85
+ rexs cancel 123456
86
+ ```
87
+
88
+ Start the dashboard in the background:
89
+
90
+ ```bash
91
+ rexs server --host=127.0.0.1 --port=8765 --daemon
92
+ # Open http://127.0.0.1:8765
93
+ rexs server_stop
94
+ ```
95
+
96
+ The dashboard and all API routes require HTTP Basic authentication by default.
97
+ Use username `rexs`. On first start, REXS generates a unique password and saves it
98
+ with owner-only permissions in `~/.local/state/rexs/server.password` (beside a
99
+ custom state database when `--db` is used). Read it with:
100
+
101
+ ```bash
102
+ cat ~/.local/state/rexs/server.password
103
+ ```
104
+
105
+ The saved password persists across restarts. Set `REXS_SERVER_PASSWORD` in the
106
+ server's environment to override it; empty passwords are rejected. The password
107
+ is never included in server URLs, process arguments, or logs. After changing a
108
+ password, restart the server and sign in again. Authenticated API POST requests
109
+ must also send `X-REXS-Request: 1`; the dashboard adds this automatically.
110
+ Keep the loopback binding and access it through SSH port forwarding. Basic
111
+ authentication should use HTTPS if exposing the server beyond that tunnel.
112
+
113
+ The dashboard shows searchable experiment history, status filters, submitted
114
+ YAML, state transitions, task-level logs, and guarded cancellation.
115
+
116
+ ## Site profile
117
+
118
+ Experiment intent remains in the Beaker file. Cluster policy belongs in a
119
+ separate REXS profile:
120
+
121
+ ```yaml
122
+ account: research
123
+ partition: gpu
124
+ qos: normal
125
+ time_limit: "24:00:00"
126
+ cpus_per_task: 8
127
+ memory: 64G
128
+
129
+ apptainer_binary: apptainer
130
+ image_cache: /shared/rexs/images
131
+ image_dir: /shared/apptainer/images
132
+ dataset_root: /shared/datasets
133
+ run_root: /shared/rexs/runs
134
+ secret_file: ~/.config/rexs/secrets.env
135
+
136
+ setup_commands:
137
+ - module load apptainer
138
+
139
+ images:
140
+ org/trainer: /shared/apptainer/images/trainer.sif
141
+ org/service: docker://ghcr.io/org/service:latest
142
+
143
+ datasets:
144
+ weka:shared-data: /shared/data
145
+ ```
146
+
147
+ Docker references are converted to `docker://` URIs automatically. Beaker
148
+ image names should be mapped to an OCI URI or existing SIF. Unmapped resources
149
+ fall back to predictable paths and generate warnings.
150
+
151
+ Secrets are loaded inside the allocation from `secret_file`; their values are
152
+ never written to the generated script or SQLite database.
153
+
154
+ ## Supported Beaker subset
155
+
156
+ - Beaker v2 YAML and JSON
157
+ - `image.beaker` and `image.docker`
158
+ - command plus arguments
159
+ - plain and secret-backed environment variables
160
+ - task replicas and leader selection
161
+ - Beaker replica/job/hostname compatibility variables
162
+ - Weka, host-path, and mapped Beaker dataset mounts
163
+ - persistent per-replica result directories
164
+ - GPU, CPU, memory, and shared-memory advisories
165
+ - host-networked multi-service deployments
166
+ - `${VARIABLE}` configuration substitution
167
+
168
+ Workspace, budget, cluster constraints, priority, retry policy, scheduler
169
+ groups, and preemption metadata are not translated. Slurm policy comes only
170
+ from the site profile. See the [compatibility reference](docs/compatibility.md)
171
+ for exact behavior.
172
+
173
+ ## Dry-run a repository
174
+
175
+ REXS can audit a directory full of experiment configurations without a Slurm
176
+ installation. It compiles every Beaker v2 YAML/JSON file and checks the output
177
+ with `bash -n`:
178
+
179
+ ```bash
180
+ rexs dry_run ~/datadev/beaker_experiments
181
+ rexs dry_run ~/datadev/beaker_experiments --strict --details
182
+ rexs dry_run ~/other/experiments --output_dir=/tmp/rexs-scripts
183
+ ```
184
+
185
+ No Slurm command is invoked in dry-run mode.
186
+
187
+ ## State and output
188
+
189
+ Global state defaults to `~/.local/state/rexs/state.sqlite3` and can be changed
190
+ with `--db` or `REXS_STATE_DB`. SQLite uses WAL mode so the CLI and background
191
+ server can safely share it.
192
+
193
+ Runtime files are organized as:
194
+
195
+ ```text
196
+ <run-root>/<slurm-job-id>/
197
+ ├── logs/<task>.<replica>.log
198
+ └── results/<task>/<replica>/
199
+ ```
200
+
201
+ Slurm remains authoritative. The controller reconciles active records through
202
+ `squeue` and completed records through `sacct`.
203
+
204
+ ## Optional W&B capture
205
+
206
+ Install `pip install -e '.[wandb]'` to let the REXS server capture local W&B
207
+ logs automatically. Every 30 seconds it scans registered jobs' output directories
208
+ for `.wandb` files, including offline runs. No W&B login or upload is required.
209
+ Jobs without W&B continue normally; this does not change their launch commands.
210
+
211
+ Open a run and select **GPU metrics**, after **History**. Each GPU card plots utilization and allocated memory over time, with hover
212
+ values and the latest sample. Long histories retain extrema when reduced for display.
213
+ Samples remain available after the job finishes. Missing values display a dash;
214
+ stale samples are marked. GPU indices are local to each logger, so separate
215
+ sources may report the same physical devices.
216
+
217
+ Scalar history, summary and system metrics, plus W&B console output records,
218
+ are saved incrementally in the existing SQLite database (`wandb_sources`,
219
+ `wandb_records`, and `wandb_gpu_latest`). Incomplete trailing records are retried
220
+ on the next scan. Original files are read only. This is not a W&B artifact or
221
+ media archive; capture requires the log files to remain accessible to the server.
222
+ Set `REXS_CAPTURE_WANDB=0` before starting the server to disable collection
223
+ without deleting saved samples. Source errors do not interrupt jobs.
224
+
225
+ ## Documentation
226
+
227
+ The full documentation lives in [`docs/`](docs/index.md) and is published with
228
+ GitHub Pages at **https://goncalorafaria.github.io/rexs/**.
229
+
230
+ To preview it locally:
231
+
232
+ ```bash
233
+ pip install -e '.[docs]'
234
+ mkdocs serve
235
+ ```
236
+
237
+ ## Development
238
+
239
+ ```bash
240
+ pip install -e '.[dev,docs]'
241
+ pytest -q
242
+ ruff check src tests
243
+ mkdocs build --strict
244
+ ```
245
+
246
+ REXS is a clean-slate implementation inspired by
247
+ [`slurmcompose`](https://github.com/goncalorafaria/slurmcompose), scoped around
248
+ reproducible Beaker-to-Slurm execution rather than a fixed service composition.
249
+
250
+ ## License
251
+
252
+ [MIT](LICENSE)
253
+
254
+ ### GPU isolation with shared CPUs
255
+
256
+ With `shared_cpus_per_node`, CPU-only service steps use `--overlap --gres=none`. GPU steps use `--exclusive --exact --gpus-per-task=N --gpus-per-node=N` and their own CPU count, allowing Slurm to allocate disjoint GPUs to concurrent model replicas. GPU steps must fit together within the shared CPU budget. The container wrapper must preserve Slurm's `CUDA_VISIBLE_DEVICES` through `APPTAINERENV_CUDA_VISIBLE_DEVICES` when using `--cleanenv`.
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "rexs"
7
+ version = "0.1.0"
8
+ description = "Reproducible Experiments, eXecuted on Slurm"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ dependencies = ["fire>=0.7", "PyYAML>=6.0"]
12
+
13
+ [project.optional-dependencies]
14
+ wandb = ["wandb>=0.19,<0.29"]
15
+ dev = ["pytest>=8.0", "ruff>=0.8", "wandb>=0.19,<0.29"]
16
+ docs = ["mkdocs>=1.6", "mkdocs-material>=9.6"]
17
+
18
+ [project.urls]
19
+ Homepage = "https://github.com/goncalorafaria/rexs"
20
+ Documentation = "https://goncalorafaria.github.io/rexs/"
21
+ Repository = "https://github.com/goncalorafaria/rexs"
22
+ Issues = "https://github.com/goncalorafaria/rexs/issues"
23
+
24
+ [project.scripts]
25
+ rexs = "rexs.cli:main"
26
+
27
+ [tool.setuptools.packages.find]
28
+ where = ["src"]
29
+ include = ["rexs*"]
30
+
31
+ [tool.setuptools.package-data]
32
+ rexs = ["static/*.png"]
33
+
34
+ [tool.pytest.ini_options]
35
+ testpaths = ["tests"]
36
+
37
+ [tool.ruff]
38
+ line-length = 120
39
+ target-version = "py311"
rexs-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,14 @@
1
+ """Beaker-to-Slurm compiler for datadev workloads."""
2
+
3
+ from rexs.compiler import CompileResult, compile_experiment
4
+ from rexs.config import SlurmProfile, load_experiment, load_profile
5
+
6
+ __all__ = [
7
+ "CompileResult",
8
+ "SlurmProfile",
9
+ "compile_experiment",
10
+ "load_experiment",
11
+ "load_profile",
12
+ ]
13
+
14
+ __version__ = "0.1.0"
@@ -0,0 +1,4 @@
1
+ from rexs.cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
@@ -0,0 +1,48 @@
1
+ """Persistent, per-account credentials for the local dashboard."""
2
+ from __future__ import annotations
3
+
4
+ import os
5
+ import secrets
6
+ import stat
7
+ import tempfile
8
+ from pathlib import Path
9
+
10
+ USERNAME = 'rexs'
11
+
12
+
13
+ def dashboard_password(state_dir: Path) -> str:
14
+ """Use an environment override or atomically create a private default password."""
15
+ override = os.environ.get('REXS_SERVER_PASSWORD')
16
+ if override is not None:
17
+ if not override.strip():
18
+ raise ValueError('REXS_SERVER_PASSWORD must not be empty')
19
+ return override
20
+ state_dir.mkdir(parents=True, exist_ok=True)
21
+ path = state_dir / 'server.password'
22
+ # Publish only a fully written file, including when two servers start together.
23
+ if not path.exists():
24
+ temporary = None
25
+ try:
26
+ with tempfile.NamedTemporaryFile(mode='w', dir=state_dir, delete=False) as stream:
27
+ temporary = Path(stream.name)
28
+ os.fchmod(stream.fileno(), 0o600)
29
+ stream.write('rexs-' + secrets.token_urlsafe(18) + '\n')
30
+ stream.flush()
31
+ os.fsync(stream.fileno())
32
+ try:
33
+ os.link(temporary, path)
34
+ except FileExistsError:
35
+ pass
36
+ finally:
37
+ if temporary is not None:
38
+ temporary.unlink(missing_ok=True)
39
+ fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW)
40
+ with os.fdopen(fd, 'r', encoding='utf-8') as stream:
41
+ info = os.fstat(stream.fileno())
42
+ if not stat.S_ISREG(info.st_mode) or info.st_uid != os.getuid():
43
+ raise ValueError('dashboard password must be a regular file owned by this user')
44
+ os.fchmod(stream.fileno(), 0o600)
45
+ password = stream.read().rstrip('\r\n')
46
+ if not password.strip():
47
+ raise ValueError(f'dashboard password file is empty: {path}')
48
+ return password