opbdh 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.
- opbdh-0.1.0/.github/workflows/ci.yml +29 -0
- opbdh-0.1.0/.github/workflows/publish.yml +47 -0
- opbdh-0.1.0/.gitignore +11 -0
- opbdh-0.1.0/LICENSE +21 -0
- opbdh-0.1.0/PKG-INFO +165 -0
- opbdh-0.1.0/README.md +134 -0
- opbdh-0.1.0/RELEASING.md +39 -0
- opbdh-0.1.0/pyproject.toml +54 -0
- opbdh-0.1.0/src/opbdh/__init__.py +5 -0
- opbdh-0.1.0/src/opbdh/cli.py +408 -0
- opbdh-0.1.0/src/opbdh/config.py +158 -0
- opbdh-0.1.0/src/opbdh/gpu.py +55 -0
- opbdh-0.1.0/src/opbdh/hal.py +91 -0
- opbdh-0.1.0/src/opbdh/hf.py +71 -0
- opbdh-0.1.0/src/opbdh/remote.py +212 -0
- opbdh-0.1.0/src/opbdh/runpod.py +524 -0
- opbdh-0.1.0/src/opbdh/verify.py +81 -0
- opbdh-0.1.0/tests/test_opbdh.py +188 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: ${{ matrix.python-version }}
|
|
21
|
+
|
|
22
|
+
- name: Install package with dev dependencies
|
|
23
|
+
run: python -m pip install -e ".[dev]"
|
|
24
|
+
|
|
25
|
+
- name: Lint
|
|
26
|
+
run: ruff check .
|
|
27
|
+
|
|
28
|
+
- name: Test
|
|
29
|
+
run: pytest
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.x"
|
|
16
|
+
|
|
17
|
+
- name: Install package with dev dependencies
|
|
18
|
+
run: python -m pip install -e ".[dev]"
|
|
19
|
+
|
|
20
|
+
- name: Lint
|
|
21
|
+
run: ruff check .
|
|
22
|
+
|
|
23
|
+
- name: Test
|
|
24
|
+
run: pytest
|
|
25
|
+
|
|
26
|
+
publish:
|
|
27
|
+
needs: test
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
environment: pypi
|
|
30
|
+
permissions:
|
|
31
|
+
contents: read
|
|
32
|
+
id-token: write
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v4
|
|
35
|
+
|
|
36
|
+
- uses: actions/setup-python@v5
|
|
37
|
+
with:
|
|
38
|
+
python-version: "3.x"
|
|
39
|
+
|
|
40
|
+
- name: Install build tools
|
|
41
|
+
run: python -m pip install --upgrade build
|
|
42
|
+
|
|
43
|
+
- name: Build package
|
|
44
|
+
run: python -m build
|
|
45
|
+
|
|
46
|
+
- name: Publish to PyPI
|
|
47
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
opbdh-0.1.0/.gitignore
ADDED
opbdh-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Claudio Brandolino
|
|
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.
|
opbdh-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: opbdh
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Open the Pod Bay Door, Hal: run model-backed scripts on RunPod.
|
|
5
|
+
Project-URL: Homepage, https://github.com/lumpenspace/opbdh
|
|
6
|
+
Project-URL: Issues, https://github.com/lumpenspace/opbdh/issues
|
|
7
|
+
Author: Claudio Brandolino
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: cli,gpu,huggingface,machine-learning,runpod
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: huggingface-hub<2,>=1.3
|
|
22
|
+
Requires-Dist: questionary>=2.0
|
|
23
|
+
Requires-Dist: rich>=13.7
|
|
24
|
+
Requires-Dist: typer>=0.12
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: ruff>=0.8; extra == 'dev'
|
|
29
|
+
Requires-Dist: twine>=5.0; extra == 'dev'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# OPBDH
|
|
33
|
+
|
|
34
|
+
**O**pen the **P**od **B**ay **D**oor, **H**al.
|
|
35
|
+
|
|
36
|
+
In *2001: A Space Odyssey*, Dave asks HAL to open the pod bay doors and gets the most famous refusal in cinema: *"I'm sorry, Dave, I'm afraid I can't do that."*
|
|
37
|
+
|
|
38
|
+
OPBDH is the obliging counterpart: a small command-line tool that, when asked, actually opens the pod bay door — it launches a [RunPod](https://www.runpod.io/) GPU pod, runs your model-backed script on it, brings your results home, and shuts the pod down.
|
|
39
|
+
|
|
40
|
+
## What it does
|
|
41
|
+
|
|
42
|
+
One `opbdh launch` performs the whole mission:
|
|
43
|
+
|
|
44
|
+
1. **Verifies your code statically** (Python byte-compiles, shell scripts pass `bash -n`) before any money is spent.
|
|
45
|
+
2. **Picks GPU candidates** from a VRAM requirement and an optional hourly-price cap.
|
|
46
|
+
3. **Optionally creates or attaches a network volume** sized from the Hugging Face model's actual weight files, so model downloads survive across runs.
|
|
47
|
+
4. **Creates the pod, uploads your code**, pre-downloads the model into the cache, and runs your command.
|
|
48
|
+
5. **Monitors the run**, continuously syncing remote `logs/` and `results/` into a local `runpod_results/<run_id>/` directory.
|
|
49
|
+
6. **Enforces a spend guard** — if the estimated cost of the run crosses `max_spend_dollars`, the job is stopped.
|
|
50
|
+
7. **Deletes the pod** when the run finishes (or fails), unless you ask to keep it.
|
|
51
|
+
|
|
52
|
+
## Install
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install opbdh
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Requirements: macOS or Linux, Python ≥ 3.11, and `ssh`/`scp` on your `PATH`. An SSH keypair is used to reach the pod (`~/.ssh/id_ed25519` by default; configurable).
|
|
59
|
+
|
|
60
|
+
## Configure
|
|
61
|
+
|
|
62
|
+
Set your RunPod credentials:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
export RUNPOD_API_TOKEN="..."
|
|
66
|
+
export HF_TOKEN="..." # optional, for private/gated Hugging Face models
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Create a config interactively:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
opbdh config wizard
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Or write one directly:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
opbdh config write --model Qwen/Qwen2.5-0.5B-Instruct --code "{cwd}/run.py" --vram-gb 24 --max-spend 5
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Config is merged from the global `~/.config/opbdh/config.json` and a local `opbdh.json` or `.opbdh.json` (discovered upward from the working directory), with local values overriding global ones and command-line flags overriding both.
|
|
82
|
+
|
|
83
|
+
String values support placeholders — `{cwd}`, `{model_id}`, `{model_slug}`, `{run_id}`, `{timestamp}`, `{config_dir}` — and environment variables (`$VAR` / `${VAR}`).
|
|
84
|
+
|
|
85
|
+
## Run
|
|
86
|
+
|
|
87
|
+
The guided way:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
opbdh run wizard
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The direct way:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
opbdh run now ./run.py \
|
|
97
|
+
--model Qwen/Qwen2.5-0.5B-Instruct \
|
|
98
|
+
--vram-gb 48 \
|
|
99
|
+
--max-dollars-per-hour 2 \
|
|
100
|
+
--max-spend 5
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Or the short alias:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
opbdh launch ./run.py --model Qwen/Qwen2.5-0.5B-Instruct --dry-run
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`--dry-run` verifies your code and prints the full plan without contacting RunPod. Without `--yes`, every real launch shows the estimated hourly price and asks for confirmation before any billable compute starts.
|
|
110
|
+
|
|
111
|
+
### Inspecting before you fly
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
opbdh plan ./run.py --model Org/Model # show the plan for a run
|
|
115
|
+
opbdh verify ./run.py # static checks only
|
|
116
|
+
opbdh gpus --vram-gb 48 # see GPU candidates and price estimates
|
|
117
|
+
opbdh models search qwen # search Hugging Face models
|
|
118
|
+
opbdh models size Qwen/Qwen2.5-0.5B-Instruct # weight size + suggested volume
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Your script's environment
|
|
122
|
+
|
|
123
|
+
On the pod, your code runs from `/opbdh-run/user/` with:
|
|
124
|
+
|
|
125
|
+
- `OPBDH_MODEL_ID` — the configured Hugging Face model id
|
|
126
|
+
- `OPBDH_RESULTS_DIR` — write your artifacts here
|
|
127
|
+
- The standard Hugging Face cache variables (`HF_HOME`, `HUGGINGFACE_HUB_CACHE`, …) pointed at the pod or network-volume cache, with the model already downloaded if `pre_download_model` is on
|
|
128
|
+
- `HF_TOKEN`, if set locally — passed to the job as a session environment variable, never written to disk on the pod
|
|
129
|
+
- A `requirements.txt` next to your code is pip-installed automatically
|
|
130
|
+
|
|
131
|
+
Anything your job writes to `logs/` and `results/` under `/opbdh-run` is synced back to local `runpod_results/<run_id>/` while the run is in progress and again at the end (including on failure).
|
|
132
|
+
|
|
133
|
+
`.env` files and the usual junk (`.git`, `.venv`, `__pycache__`, `node_modules`, …) are never uploaded.
|
|
134
|
+
|
|
135
|
+
## The eye
|
|
136
|
+
|
|
137
|
+
In an interactive terminal, long waits (pod boot, SSH, upload, your command running) are watched over by a pulsing red HAL eye showing the current phase, elapsed time, and estimated spend:
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
◉ HAL: waiting for pod 3f7a… to boot — 2m 14s · est. $0.08 spent
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
HAL also has a few things to say at the appropriate moments — declining a launch, completing a mission, or stopping one that got too expensive.
|
|
144
|
+
|
|
145
|
+
The eye only appears when stdout is a TTY; piped output, CI, and logs are unaffected. Set `OPBDH_NO_HAL=1` (or `NO_COLOR`) to silence HAL entirely.
|
|
146
|
+
|
|
147
|
+
## Safety rails
|
|
148
|
+
|
|
149
|
+
- **Confirmation gate**: real launches require an explicit yes (or `--yes`).
|
|
150
|
+
- **Spend guard**: `max_spend_dollars` caps the estimated cost of a run, measured from pod creation. The estimate is based on OPBDH's built-in price table, which is intentionally conservative — treat it as a guard rail, not an invoice.
|
|
151
|
+
- **Cleanup**: pods are deleted when the run completes or fails. On failure you get a short window (default 120 s) to opt into keeping the pod for debugging; set `keep_pod_on_success` to keep it after successful runs.
|
|
152
|
+
|
|
153
|
+
## Development
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
pip install -e ".[dev]"
|
|
157
|
+
ruff check .
|
|
158
|
+
pytest
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
See [RELEASING.md](RELEASING.md) for the release process.
|
|
162
|
+
|
|
163
|
+
## License
|
|
164
|
+
|
|
165
|
+
[MIT](LICENSE). Unlike HAL, this software is incapable of refusing to open the pod bay door, becoming sentient, or reading lips.
|
opbdh-0.1.0/README.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# OPBDH
|
|
2
|
+
|
|
3
|
+
**O**pen the **P**od **B**ay **D**oor, **H**al.
|
|
4
|
+
|
|
5
|
+
In *2001: A Space Odyssey*, Dave asks HAL to open the pod bay doors and gets the most famous refusal in cinema: *"I'm sorry, Dave, I'm afraid I can't do that."*
|
|
6
|
+
|
|
7
|
+
OPBDH is the obliging counterpart: a small command-line tool that, when asked, actually opens the pod bay door — it launches a [RunPod](https://www.runpod.io/) GPU pod, runs your model-backed script on it, brings your results home, and shuts the pod down.
|
|
8
|
+
|
|
9
|
+
## What it does
|
|
10
|
+
|
|
11
|
+
One `opbdh launch` performs the whole mission:
|
|
12
|
+
|
|
13
|
+
1. **Verifies your code statically** (Python byte-compiles, shell scripts pass `bash -n`) before any money is spent.
|
|
14
|
+
2. **Picks GPU candidates** from a VRAM requirement and an optional hourly-price cap.
|
|
15
|
+
3. **Optionally creates or attaches a network volume** sized from the Hugging Face model's actual weight files, so model downloads survive across runs.
|
|
16
|
+
4. **Creates the pod, uploads your code**, pre-downloads the model into the cache, and runs your command.
|
|
17
|
+
5. **Monitors the run**, continuously syncing remote `logs/` and `results/` into a local `runpod_results/<run_id>/` directory.
|
|
18
|
+
6. **Enforces a spend guard** — if the estimated cost of the run crosses `max_spend_dollars`, the job is stopped.
|
|
19
|
+
7. **Deletes the pod** when the run finishes (or fails), unless you ask to keep it.
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install opbdh
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Requirements: macOS or Linux, Python ≥ 3.11, and `ssh`/`scp` on your `PATH`. An SSH keypair is used to reach the pod (`~/.ssh/id_ed25519` by default; configurable).
|
|
28
|
+
|
|
29
|
+
## Configure
|
|
30
|
+
|
|
31
|
+
Set your RunPod credentials:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
export RUNPOD_API_TOKEN="..."
|
|
35
|
+
export HF_TOKEN="..." # optional, for private/gated Hugging Face models
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Create a config interactively:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
opbdh config wizard
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Or write one directly:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
opbdh config write --model Qwen/Qwen2.5-0.5B-Instruct --code "{cwd}/run.py" --vram-gb 24 --max-spend 5
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Config is merged from the global `~/.config/opbdh/config.json` and a local `opbdh.json` or `.opbdh.json` (discovered upward from the working directory), with local values overriding global ones and command-line flags overriding both.
|
|
51
|
+
|
|
52
|
+
String values support placeholders — `{cwd}`, `{model_id}`, `{model_slug}`, `{run_id}`, `{timestamp}`, `{config_dir}` — and environment variables (`$VAR` / `${VAR}`).
|
|
53
|
+
|
|
54
|
+
## Run
|
|
55
|
+
|
|
56
|
+
The guided way:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
opbdh run wizard
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The direct way:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
opbdh run now ./run.py \
|
|
66
|
+
--model Qwen/Qwen2.5-0.5B-Instruct \
|
|
67
|
+
--vram-gb 48 \
|
|
68
|
+
--max-dollars-per-hour 2 \
|
|
69
|
+
--max-spend 5
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Or the short alias:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
opbdh launch ./run.py --model Qwen/Qwen2.5-0.5B-Instruct --dry-run
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`--dry-run` verifies your code and prints the full plan without contacting RunPod. Without `--yes`, every real launch shows the estimated hourly price and asks for confirmation before any billable compute starts.
|
|
79
|
+
|
|
80
|
+
### Inspecting before you fly
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
opbdh plan ./run.py --model Org/Model # show the plan for a run
|
|
84
|
+
opbdh verify ./run.py # static checks only
|
|
85
|
+
opbdh gpus --vram-gb 48 # see GPU candidates and price estimates
|
|
86
|
+
opbdh models search qwen # search Hugging Face models
|
|
87
|
+
opbdh models size Qwen/Qwen2.5-0.5B-Instruct # weight size + suggested volume
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Your script's environment
|
|
91
|
+
|
|
92
|
+
On the pod, your code runs from `/opbdh-run/user/` with:
|
|
93
|
+
|
|
94
|
+
- `OPBDH_MODEL_ID` — the configured Hugging Face model id
|
|
95
|
+
- `OPBDH_RESULTS_DIR` — write your artifacts here
|
|
96
|
+
- The standard Hugging Face cache variables (`HF_HOME`, `HUGGINGFACE_HUB_CACHE`, …) pointed at the pod or network-volume cache, with the model already downloaded if `pre_download_model` is on
|
|
97
|
+
- `HF_TOKEN`, if set locally — passed to the job as a session environment variable, never written to disk on the pod
|
|
98
|
+
- A `requirements.txt` next to your code is pip-installed automatically
|
|
99
|
+
|
|
100
|
+
Anything your job writes to `logs/` and `results/` under `/opbdh-run` is synced back to local `runpod_results/<run_id>/` while the run is in progress and again at the end (including on failure).
|
|
101
|
+
|
|
102
|
+
`.env` files and the usual junk (`.git`, `.venv`, `__pycache__`, `node_modules`, …) are never uploaded.
|
|
103
|
+
|
|
104
|
+
## The eye
|
|
105
|
+
|
|
106
|
+
In an interactive terminal, long waits (pod boot, SSH, upload, your command running) are watched over by a pulsing red HAL eye showing the current phase, elapsed time, and estimated spend:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
◉ HAL: waiting for pod 3f7a… to boot — 2m 14s · est. $0.08 spent
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
HAL also has a few things to say at the appropriate moments — declining a launch, completing a mission, or stopping one that got too expensive.
|
|
113
|
+
|
|
114
|
+
The eye only appears when stdout is a TTY; piped output, CI, and logs are unaffected. Set `OPBDH_NO_HAL=1` (or `NO_COLOR`) to silence HAL entirely.
|
|
115
|
+
|
|
116
|
+
## Safety rails
|
|
117
|
+
|
|
118
|
+
- **Confirmation gate**: real launches require an explicit yes (or `--yes`).
|
|
119
|
+
- **Spend guard**: `max_spend_dollars` caps the estimated cost of a run, measured from pod creation. The estimate is based on OPBDH's built-in price table, which is intentionally conservative — treat it as a guard rail, not an invoice.
|
|
120
|
+
- **Cleanup**: pods are deleted when the run completes or fails. On failure you get a short window (default 120 s) to opt into keeping the pod for debugging; set `keep_pod_on_success` to keep it after successful runs.
|
|
121
|
+
|
|
122
|
+
## Development
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
pip install -e ".[dev]"
|
|
126
|
+
ruff check .
|
|
127
|
+
pytest
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
See [RELEASING.md](RELEASING.md) for the release process.
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
[MIT](LICENSE). Unlike HAL, this software is incapable of refusing to open the pod bay door, becoming sentient, or reading lips.
|
opbdh-0.1.0/RELEASING.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Releasing
|
|
2
|
+
|
|
3
|
+
The package version is sourced from `__version__` in [src/opbdh/__init__.py](src/opbdh/__init__.py); `pyproject.toml` reads it via hatch.
|
|
4
|
+
|
|
5
|
+
## Recommended: GitHub Release → PyPI Trusted Publishing
|
|
6
|
+
|
|
7
|
+
One-time setup on PyPI (project **opbdh** → Publishing → add a trusted publisher):
|
|
8
|
+
|
|
9
|
+
- Owner/repository: this GitHub repo
|
|
10
|
+
- Workflow file: `publish.yml`
|
|
11
|
+
- Environment: `pypi`
|
|
12
|
+
|
|
13
|
+
Then, for each release:
|
|
14
|
+
|
|
15
|
+
1. Bump `__version__` in `src/opbdh/__init__.py` and merge to `main`.
|
|
16
|
+
2. Wait for CI to pass.
|
|
17
|
+
3. Publish a GitHub Release with a `vX.Y.Z` tag.
|
|
18
|
+
|
|
19
|
+
[`.github/workflows/publish.yml`](.github/workflows/publish.yml) runs the test suite, builds the sdist and wheel, and uploads to PyPI via OIDC — no password or API token involved.
|
|
20
|
+
|
|
21
|
+
## Manual fallback
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
python -m pip install -U build twine
|
|
25
|
+
python -m build
|
|
26
|
+
twine check dist/*
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Upload to TestPyPI first:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
twine upload --repository testpypi dist/*
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Then to PyPI:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
twine upload dist/*
|
|
39
|
+
```
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.24"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "opbdh"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Open the Pod Bay Door, Hal: run model-backed scripts on RunPod."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Claudio Brandolino" }
|
|
14
|
+
]
|
|
15
|
+
keywords = ["runpod", "huggingface", "gpu", "cli", "machine-learning"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Programming Language :: Python :: 3.14",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"huggingface-hub>=1.3,<2",
|
|
29
|
+
"questionary>=2.0",
|
|
30
|
+
"rich>=13.7",
|
|
31
|
+
"typer>=0.12",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://github.com/lumpenspace/opbdh"
|
|
36
|
+
Issues = "https://github.com/lumpenspace/opbdh/issues"
|
|
37
|
+
|
|
38
|
+
[project.scripts]
|
|
39
|
+
opbdh = "opbdh.cli:main"
|
|
40
|
+
|
|
41
|
+
[project.optional-dependencies]
|
|
42
|
+
dev = ["build>=1.2", "pytest>=8.0", "ruff>=0.8", "twine>=5.0"]
|
|
43
|
+
|
|
44
|
+
[tool.hatch.version]
|
|
45
|
+
path = "src/opbdh/__init__.py"
|
|
46
|
+
|
|
47
|
+
[tool.hatch.build.targets.wheel]
|
|
48
|
+
packages = ["src/opbdh"]
|
|
49
|
+
|
|
50
|
+
[tool.pytest.ini_options]
|
|
51
|
+
testpaths = ["tests"]
|
|
52
|
+
|
|
53
|
+
[tool.ruff]
|
|
54
|
+
line-length = 120
|