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.
@@ -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
@@ -0,0 +1,11 @@
1
+ .DS_Store
2
+ .pytest_cache/
3
+ .ruff_cache/
4
+ .venv/
5
+ __pycache__/
6
+ *.egg-info/
7
+ build/
8
+ dist/
9
+ runpod_results/
10
+ .env
11
+ .env.*
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.
@@ -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
@@ -0,0 +1,5 @@
1
+ """OPBDH: a small RunPod launcher for model-backed scripts."""
2
+
3
+ __all__ = ["__version__"]
4
+
5
+ __version__ = "0.1.0"