Skip to content

Commit 37cedcc

Browse files
feat: add CUDA-aware install helper
Document the generated CUDA dependency workflow and provide a shared installer path so users can select CPU, CUDA 12.8, or CUDA 13.0 with the required indexes and constraints. Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent ba892a3 commit 37cedcc

5 files changed

Lines changed: 419 additions & 20 deletions

File tree

‎CONTRIBUTING.md‎

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,60 @@ All `make` targets check the entire project. Pre-commit scopes checks to staged
487487
| uv lock drift | `make lock-check` | not checked | on `pyproject.toml` changes |
488488
| DCO signoff | branch protection | not checked | commit-msg hook |
489489

490+
## Dependency Updates
491+
492+
Dependency updates have two generated surfaces:
493+
494+
- CUDA/runtime extras: `cuda_deps.toml` is the source of truth. `pyproject.toml` is generated from it.
495+
- Security floors: `[tool.uv] constraint-dependencies` in `pyproject.toml` is the source of truth. `constraints.txt` is generated from it and published for downstream `pip` / `uv pip` installs.
496+
497+
### CUDA and Accelerator Dependencies
498+
499+
Edit `cuda_deps.toml` for changes to the CPU, CUDA 12.8, CUDA 13.0, PyTorch, FlashInfer, or NVIDIA package matrix. Do not hand-edit the generated `cpu`, `cu128`, `cu130`, `[tool.uv.sources]`, or `[[tool.uv.index]]` sections in `pyproject.toml`.
500+
501+
After editing `cuda_deps.toml`, regenerate `pyproject.toml`:
502+
503+
```bash
504+
uv run --script tools/gen_cuda_deps.py cuda_deps.toml --pyproject pyproject.toml
505+
uv lock
506+
```
507+
508+
Then verify the generated sections are current:
509+
510+
```bash
511+
uv run --script tools/gen_cuda_deps.py cuda_deps.toml --pyproject pyproject.toml --check
512+
uv run pytest tests/test_gen_cuda_deps.py
513+
make lock-check
514+
```
515+
516+
When adding a new CUDA extra, update all user-facing install surfaces in the same PR:
517+
518+
- `generated_extras` in `cuda_deps.toml`
519+
- `install_nss.sh`
520+
- `docs/user-guide/getting-started.md`
521+
- `README.md`
522+
523+
Use dry runs to confirm the installer emits the expected command matrix:
524+
525+
```bash
526+
DRY_RUN=1 CUDA=128 bash install_nss.sh
527+
DRY_RUN=1 CUDA=130 bash install_nss.sh
528+
DRY_RUN=1 CUDA=cpu bash install_nss.sh
529+
```
530+
531+
### Security Floors and Constraints
532+
533+
Dependabot CVE floors are applied with `tools/patch_dependabot.py`. The script bumps direct dependencies in `pyproject.toml`, adds transitive floors to `[tool.uv] constraint-dependencies`, writes the exported `constraints.txt`, and refreshes `uv.lock`.
534+
535+
```bash
536+
export GITHUB_TOKEN="$(gh auth token)"
537+
uv run tools/patch_dependabot.py
538+
```
539+
540+
The exported `constraints.txt` is intentionally not CUDA-version-specific. Runtime selection comes from the selected extra (`cpu`, `cu128`, `cu130`) and package indexes; security floors come from the shared constraints file.
541+
542+
If you update `[tool.uv] constraint-dependencies` by hand, regenerate `constraints.txt` before opening a PR. The simplest supported path is to run `tools/patch_dependabot.py` with a cached `dependabot.json` or an active `GITHUB_TOKEN`, then review the resulting `pyproject.toml`, `constraints.txt`, and `uv.lock` diff.
543+
490544
## Documentation
491545

492546
This project uses [MkDocs Material](https://squidfunk.github.io/mkdocs-material/) for its documentation site, hosted at <https://nvidia-nemo.github.io/Safe-Synthesizer/>.
@@ -576,7 +630,29 @@ Before contributing, run `make format` and `make check`. See `AGENTS.md` for ful
576630

577631
Releases are published to PyPI via the **Release NeMo Safe Synthesizer** GitHub Actions workflow. The workflow builds the wheel from a git tag, publishes to Test PyPI as a pre-flight check, publishes to the real PyPI, and creates a GitHub release.
578632

579-
### 1. Create and push a tag
633+
### 1. Run dependency and install preflight
634+
635+
Before tagging a release, verify generated dependency artifacts and install instructions are current:
636+
637+
```bash
638+
uv run --script tools/gen_cuda_deps.py cuda_deps.toml --pyproject pyproject.toml --check
639+
uv run pytest tests/test_gen_cuda_deps.py
640+
make lock-check
641+
DRY_RUN=1 CUDA=128 bash install_nss.sh
642+
DRY_RUN=1 CUDA=130 bash install_nss.sh
643+
DRY_RUN=1 CUDA=cpu bash install_nss.sh
644+
```
645+
646+
If Dependabot security floors changed since the last release, run:
647+
648+
```bash
649+
export GITHUB_TOKEN="$(gh auth token)"
650+
uv run tools/patch_dependabot.py
651+
```
652+
653+
Review `pyproject.toml`, `constraints.txt`, and `uv.lock` together. The release publishes the wheel to PyPI, but downstream installers also depend on the raw `constraints.txt` and `install_nss.sh` URLs from the release branch.
654+
655+
### 2. Create and push a tag
580656

581657
Release versions follow [PEP440](https://peps.python.org/pep-0440/) with major, minor, and patch release numbers.
582658
This project uses stable releases and release candidates only; prerelease versions append the suffix rcN (no dash, as specified by PEP440).
@@ -607,7 +683,7 @@ git tag v0.1.0rc1 <commit-sha>
607683
git push origin <tag>
608684
```
609685

610-
### 2. Monitor the workflow run
686+
### 3. Monitor the workflow run
611687

612688
The [workflow](https://github.com/NVIDIA-NeMo/Safe-Synthesizer/actions/workflows/release.yml) to release is triggered automatically when a tag starting with `v` is pushed to GitHub.
613689

‎README.md‎

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,47 @@ Read detailed usage below, or jump to the documentation with [Getting Started](h
1616

1717
### Installation
1818

19+
Choose one CUDA runtime extra. See the
20+
[installation guide](https://nvidia-nemo.github.io/Safe-Synthesizer/user-guide/getting-started/#install-the-package)
21+
for CPU and Docker installs.
22+
23+
Installer script:
24+
25+
```bash
26+
# CUDA 12.8 with the installer script:
27+
curl -fsSL https://raw.githubusercontent.com/NVIDIA-NeMo/Safe-Synthesizer/main/install_nss.sh | CUDA=128 bash
28+
29+
# CUDA 13.0 with the installer script:
30+
curl -fsSL https://raw.githubusercontent.com/NVIDIA-NeMo/Safe-Synthesizer/main/install_nss.sh | CUDA=130 bash
31+
```
32+
33+
Raw commands:
34+
1935
```bash
20-
# With uv (recommended):
36+
CONSTRAINTS_URL="https://raw.githubusercontent.com/NVIDIA-NeMo/Safe-Synthesizer/main/constraints.txt"
37+
38+
# CUDA 12.8 with uv (recommended):
2139
uv pip install "nemo-safe-synthesizer[cu128,engine]" \
40+
-c "$CONSTRAINTS_URL" \
2241
--index https://flashinfer.ai/whl/cu128 \
2342
--index https://download.pytorch.org/whl/cu128 \
43+
--index https://pypi.nvidia.com \
44+
--index-strategy unsafe-best-match
45+
46+
# CUDA 13.0 with uv (requires NVIDIA Linux driver 580.65.06+):
47+
uv pip install "nemo-safe-synthesizer[cu130,engine]" \
48+
-c "$CONSTRAINTS_URL" \
49+
--index https://flashinfer.ai/whl/cu130 \
50+
--index https://download.pytorch.org/whl/cu130 \
51+
--index https://pypi.nvidia.com \
2452
--index-strategy unsafe-best-match
2553

26-
# With pip:
54+
# CUDA 12.8 with pip:
2755
pip install "nemo-safe-synthesizer[cu128,engine]" \
56+
-c "$CONSTRAINTS_URL" \
2857
--extra-index-url https://download.pytorch.org/whl/cu128 \
29-
--extra-index-url https://flashinfer.ai/whl/cu128
58+
--extra-index-url https://flashinfer.ai/whl/cu128 \
59+
--extra-index-url https://pypi.nvidia.com
3060
```
3161

3262
Or install from source:
@@ -35,7 +65,9 @@ Or install from source:
3565
git clone https://github.com/NVIDIA-NeMo/Safe-Synthesizer.git
3666
cd Safe-Synthesizer
3767
make setup # installs the pinned mise version (if missing) + pinned tool versions from mise.lock
38-
make bootstrap-nss cuda
68+
uv sync --frozen --extra cu128 --extra engine --group dev
69+
# or, for CUDA 13.0:
70+
uv sync --frozen --extra cu130 --extra engine --group dev
3971
```
4072

4173
Development tools (`ruff`, `ty`, `yq`, `gh`, etc.) are managed via [mise](https://mise.jdx.dev/). Tool versions are declared in `.mise.toml` and locked in `mise.lock` (committed). mise also manages environment variables -- place project-local secrets or overrides in `.env` or `.env.local` (both git-ignored, auto-loaded by mise).

‎cuda_deps.toml‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ cuda_dependencies = [
3030

3131
generated_extras = ["cpu", "cu128", "cu130"]
3232

33+
# CUDA Python packaging context:
34+
# - Starting with CUDA Python 12.8, cuda-python is a metapackage that currently
35+
# depends on cuda-bindings; the newer docs describe split components such as
36+
# cuda.core, cuda.bindings, and cuda.pathfinder.
37+
# - CUDA 13.x optional Toolkit components are installed through the cuda-toolkit
38+
# PyPI metapackage, and current NVIDIA package names are generally unsuffixed
39+
# (for example, nvidia-cuda-nvrtc and nvidia-nvjitlink).
40+
# - Keep NVIDIA package routing per-library: nvidia-cublas is unsuffixed for both
41+
# CUDA 12 and CUDA 13, while other CUDA 12 runtime packages may still need a
42+
# -cu12 suffix and CUDA-specific PyTorch indexes.
43+
# - CUDA 13.x bindings require Linux driver 580.65.06 or newer.
3344
nvidia_cuda_libraries = [
3445
{ name = "cublas", nvidia_package_suffix = "", index = "nvidia-pypi-public" },
3546
"cuda-cupti",

‎docs/user-guide/getting-started.md‎

Lines changed: 102 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,35 +26,117 @@ does at each stage.
2626

2727
### Install the Package
2828

29-
The CUDA and CPU extras depend on packages (PyTorch, FlashInfer) hosted on
30-
indexes outside PyPI. You must pass the extra index URLs shown below.
29+
Choose exactly one runtime extra: `cpu`, `cu128`, or `cu130`. Add `engine`
30+
when you want the CLI pipeline dependencies. Do not combine `cpu`, `cu128`,
31+
and `cu130`; they conflict by design.
32+
33+
The CUDA and Linux CPU extras depend on packages hosted on indexes outside
34+
PyPI. You must pass the index URLs shown below for published-package installs.
35+
From a source checkout, `uv sync` reads the configured indexes from
36+
`pyproject.toml`.
37+
38+
Published-package installs also use the exported
39+
[`constraints.txt`](https://raw.githubusercontent.com/NVIDIA-NeMo/Safe-Synthesizer/main/constraints.txt)
40+
security floor file. The same constraints file applies to all runtime extras;
41+
CUDA-specific package selection comes from the chosen extra and index URLs.
42+
43+
#### Option 1: Installer Script
44+
45+
The installer script is the shortest published-package path. It wraps the same
46+
extras, index URLs, and constraints file shown in the raw commands below.
47+
48+
```bash
49+
# CUDA 12.8
50+
curl -fsSL https://raw.githubusercontent.com/NVIDIA-NeMo/Safe-Synthesizer/main/install_nss.sh | CUDA=128 bash
51+
52+
# CUDA 13.0
53+
curl -fsSL https://raw.githubusercontent.com/NVIDIA-NeMo/Safe-Synthesizer/main/install_nss.sh | CUDA=130 bash
54+
55+
# CPU-only development install
56+
curl -fsSL https://raw.githubusercontent.com/NVIDIA-NeMo/Safe-Synthesizer/main/install_nss.sh | CUDA=cpu bash
57+
```
58+
59+
To inspect the script before running it:
60+
61+
```bash
62+
curl -fsSLO https://raw.githubusercontent.com/NVIDIA-NeMo/Safe-Synthesizer/main/install_nss.sh
63+
CUDA=130 bash install_nss.sh
64+
```
65+
66+
#### Option 2: Raw Commands
67+
68+
Use the raw commands when you want to audit or customize every installer flag.
69+
70+
!!! info "Why `--index-strategy unsafe-best-match`"
71+
FlashInfer publishes wheels to flashinfer.ai, but `flashinfer-python`
72+
can also appear on other package indexes at older versions. uv's default
73+
`first-match` strategy stops at the first index that contains a package
74+
name, so it can pick up the wrong version and fail to resolve.
75+
`--index-strategy unsafe-best-match` tells uv to consider all indexes and
76+
pick the best matching version.
3177

3278
=== "CUDA 12.8 (Linux with NVIDIA GPU)"
3379

80+
Use this for CUDA 12.8 environments.
81+
3482
=== "pip"
3583

3684
```bash
3785
pip install "nemo-safe-synthesizer[cu128,engine]" \
86+
-c https://raw.githubusercontent.com/NVIDIA-NeMo/Safe-Synthesizer/main/constraints.txt \
3887
--extra-index-url https://download.pytorch.org/whl/cu128 \
39-
--extra-index-url https://flashinfer.ai/whl/cu128
88+
--extra-index-url https://flashinfer.ai/whl/cu128 \
89+
--extra-index-url https://pypi.nvidia.com
4090
```
4191

4292
=== "uv"
4393

4494
```bash
4595
uv pip install "nemo-safe-synthesizer[cu128,engine]" \
96+
-c https://raw.githubusercontent.com/NVIDIA-NeMo/Safe-Synthesizer/main/constraints.txt \
4697
--index https://flashinfer.ai/whl/cu128 \
4798
--index https://download.pytorch.org/whl/cu128 \
99+
--index https://pypi.nvidia.com \
48100
--index-strategy unsafe-best-match
49101
```
50102

51-
!!! info "Why `--index-strategy unsafe-best-match`"
52-
FlashInfer publishes wheels to flashinfer.ai, but `flashinfer-python`
53-
also appears on the PyTorch index at older versions. uv's default
54-
`first-match` strategy stops at the first index that contains a
55-
package name, so it picks up the wrong version from the PyTorch
56-
index and fails to resolve. `--index-strategy unsafe-best-match`
57-
tells uv to consider all indexes and pick the best matching version.
103+
=== "source checkout"
104+
105+
```bash
106+
uv sync --frozen --extra cu128 --extra engine
107+
```
108+
109+
=== "CUDA 13.0 (Linux with NVIDIA GPU)"
110+
111+
Use this for CUDA 13.0 environments. CUDA 13.x bindings require Linux
112+
NVIDIA driver 580.65.06 or newer.
113+
114+
=== "pip"
115+
116+
```bash
117+
pip install "nemo-safe-synthesizer[cu130,engine]" \
118+
-c https://raw.githubusercontent.com/NVIDIA-NeMo/Safe-Synthesizer/main/constraints.txt \
119+
--extra-index-url https://download.pytorch.org/whl/cu130 \
120+
--extra-index-url https://flashinfer.ai/whl/cu130 \
121+
--extra-index-url https://pypi.nvidia.com
122+
```
123+
124+
=== "uv"
125+
126+
```bash
127+
uv pip install "nemo-safe-synthesizer[cu130,engine]" \
128+
-c https://raw.githubusercontent.com/NVIDIA-NeMo/Safe-Synthesizer/main/constraints.txt \
129+
--index https://flashinfer.ai/whl/cu130 \
130+
--index https://download.pytorch.org/whl/cu130 \
131+
--index https://pypi.nvidia.com \
132+
--index-strategy unsafe-best-match
133+
```
134+
135+
=== "source checkout"
136+
137+
```bash
138+
uv sync --frozen --extra cu130 --extra engine
139+
```
58140

59141
=== "CPU (macOS / Linux without GPU)"
60142

@@ -66,26 +148,30 @@ indexes outside PyPI. You must pass the extra index URLs shown below.
66148
=== "pip (macOS)"
67149

68150
```bash
69-
pip install "nemo-safe-synthesizer[cpu,engine]"
151+
pip install "nemo-safe-synthesizer[cpu,engine]" \
152+
-c https://raw.githubusercontent.com/NVIDIA-NeMo/Safe-Synthesizer/main/constraints.txt
70153
```
71154

72155
=== "pip (Linux)"
73156

74157
```bash
75158
pip install "nemo-safe-synthesizer[cpu,engine]" \
159+
-c https://raw.githubusercontent.com/NVIDIA-NeMo/Safe-Synthesizer/main/constraints.txt \
76160
--extra-index-url https://download.pytorch.org/whl/cpu
77161
```
78162

79163
=== "uv (macOS)"
80164

81165
```bash
82-
uv pip install "nemo-safe-synthesizer[cpu,engine]"
166+
uv pip install "nemo-safe-synthesizer[cpu,engine]" \
167+
-c https://raw.githubusercontent.com/NVIDIA-NeMo/Safe-Synthesizer/main/constraints.txt
83168
```
84169

85170
=== "uv (Linux)"
86171

87172
```bash
88173
uv pip install "nemo-safe-synthesizer[cpu,engine]" \
174+
-c https://raw.githubusercontent.com/NVIDIA-NeMo/Safe-Synthesizer/main/constraints.txt \
89175
--index https://download.pytorch.org/whl/cpu
90176
```
91177

@@ -116,13 +202,15 @@ indexes outside PyPI. You must pass the extra index URLs shown below.
116202
=== "pip"
117203

118204
```bash
119-
pip install "nemo-safe-synthesizer"
205+
pip install "nemo-safe-synthesizer" \
206+
-c https://raw.githubusercontent.com/NVIDIA-NeMo/Safe-Synthesizer/main/constraints.txt
120207
```
121208

122209
=== "uv"
123210

124211
```bash
125-
uv pip install "nemo-safe-synthesizer"
212+
uv pip install "nemo-safe-synthesizer" \
213+
-c https://raw.githubusercontent.com/NVIDIA-NeMo/Safe-Synthesizer/main/constraints.txt
126214
```
127215

128216
!!! note "Limited use"

0 commit comments

Comments
 (0)