Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ flowchart LR
end

subgraph containers [Container Build]
buildContainer[Build cu129 Image]
buildContainer[Build cu129/cu130 Images]
publishGhcr[Publish GHCR Tags]
end

Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/ci-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ jobs:
checkout: "false"
bootstrap-tools: "true"

- name: Verify installation helper dry runs
run: mise run test:installer

- name: Build wheel
run: mise run build-wheel

Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/container-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ jobs:
- variant: cu129
extra: cu129
platforms: linux/amd64
- variant: cu130
extra: cu130
platforms: linux/amd64
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/gpu-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ jobs:
}}
timeout-minutes: 60
runs-on: linux-amd64-gpu-a100-latest-1
# Non-required while the CUDA 13 rollout is validated. Promote to required
# once cu130 has container-build coverage (see docs/developer-guide/docker.md).
# Non-required until a run of green nightly cu130 results validates the rollout;
# container-build and e2e coverage already exist (see docs/developer-guide/docker.md).
continue-on-error: ${{ matrix.cuda-extra == 'cu130' }}
strategy:
fail-fast: false
Expand Down Expand Up @@ -163,8 +163,8 @@ jobs:
include:
- cuda-extra: cu129
required: true
# Non-required while the CUDA 13 rollout is validated. Promote to
# required once cu130 has container-build coverage (see docs/developer-guide/docker.md).
# Non-required until a run of green nightly cu130 results validates the rollout;
# container-build and e2e coverage already exist (see docs/developer-guide/docker.md).
- cuda-extra: cu130
required: false
steps:
Expand All @@ -180,7 +180,7 @@ jobs:
cuda-extra: ${{ matrix.cuda-extra }}

- name: Run GPU E2E tests
run: mise -E gpu-ci run test:e2e:prepared
run: mise run test:e2e:prepared
timeout-minutes: 190

# ---------------------------------------------------------------------------
Expand Down
30 changes: 14 additions & 16 deletions .mise/tasks/quality.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,18 @@
# SPDX-License-Identifier: Apache-2.0

[format]
description = "Format TOML and Python files with dprint and ruff, then fix SPDX copyright headers. Modifies files in place; run before committing."
description = "Format all tracked source files with ruff and SPDX copyright fixer. Modifies files in place; run before committing."
run = [
"dprint fmt --incremental=false",
"bash tools/codestyle/format.sh",
"uv run --script tools/codestyle/copyright_fixer.py .",
"bash tools/codestyle/format.sh",
"uv run --script tools/codestyle/copyright_fixer.py .",
]

["format-check"]
description = "Check TOML and Python formatting, lint rules, and SPDX copyright headers without modifying source files. Safe for CI and local pre-review checks."
description = "Check ruff formatting, lint rules, and SPDX copyright headers without modifying files. Safe for CI and local pre-review checks."
run = [
"dprint check --incremental=false",
"bash tools/codestyle/format.sh --check",
"bash tools/codestyle/ruff_check.sh",
"uv run --script tools/codestyle/copyright_fixer.py --check .",
"bash tools/codestyle/format.sh --check",
"bash tools/codestyle/ruff_check.sh",
"uv run --script tools/codestyle/copyright_fixer.py --check .",
]

[typecheck]
Expand All @@ -25,9 +23,9 @@ run = "bash tools/codestyle/typecheck.sh"
["lock-check"]
description = "Verify generated CUDA metadata and uv.lock match their sources."
run = [
"uv run --offline --frozen tools/gen_cuda_deps.py cuda_deps.toml --pyproject pyproject.toml --check",
"uv lock",
"git diff --exit-code uv.lock",
"uv run --offline --frozen tools/gen_cuda_deps.py cuda_deps.toml --pyproject pyproject.toml --installer install_nss.sh --check",
"uv lock",
"git diff --exit-code uv.lock",
]

[check]
Expand All @@ -39,8 +37,8 @@ run = "echo 'quality checks passed'"
description = "Run the standard pre-PR validation suite: check, lock-check, then CI unit tests."
alias = "ci"
run = [
{ task = "check" },
{ task = "lock-check" },
{ task = "test:ci" },
"echo 'validation passed'",
{ task = "check" },
{ task = "lock-check" },
{ task = "test:ci" },
"echo 'validation passed'",
]
8 changes: 8 additions & 0 deletions .mise/tasks/test/installer
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#MISE description="Validate install_nss.sh dry-run commands without installing packages."

set -euo pipefail

bash tests/tools/test_install_nss.sh
14 changes: 7 additions & 7 deletions .mise/tasks/tests.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ alias = "test-smoke-gpu-smollm2"
run = 'uv run --frozen pytest --dist loadscope -vv -n 0 tests/smoke/ -m "requires_gpu and smollm2"'

["test:gpu-integration"]
description = "Run GPU integration e2e tests for default and DP configs. Requires CUDA; stages are sequential to control GPU load."
description = "Run GPU integration e2e tests for default and DP configs. Requires CUDA; stages are sequential to control GPU load. Bootstraps $NSS_GPU_CUDA_EXTRA (default cu129)."
alias = "test-gpu-integration"
run = [
{ task = "bootstrap-nss", args = ["cu129"] },
'mise run bootstrap-nss "${NSS_GPU_CUDA_EXTRA:-cu129}"',
"uv run --frozen pytest -n 0 --dist loadscope -vv tests/e2e/test_safe_synthesizer.py -k default",
"uv run --frozen pytest -n 0 --dist loadscope -vv tests/e2e/test_safe_synthesizer.py -k dp",
]
Expand All @@ -91,10 +91,10 @@ description = "Run all GPU e2e tests without bootstrapping dependencies. Require
run = [{ task = "test:e2e:default:run" }, { task = "test:e2e:dp:run" }]

["test:e2e:default"]
description = "Run default e2e tests (requires CUDA)"
description = "Run default e2e tests (requires CUDA). Bootstraps $NSS_GPU_CUDA_EXTRA (default cu129)."
alias = "test-e2e-default"
run = [
{ task = "bootstrap-nss", args = ["cu129"] },
'mise run bootstrap-nss "${NSS_GPU_CUDA_EXTRA:-cu129}"',
{ task = "test:e2e:default:run" },
]

Expand All @@ -103,10 +103,10 @@ hide = true
run = "uv run --frozen --no-sync pytest --dist loadscope -vv -n 0 tests/e2e/test_safe_synthesizer.py -k default"

["test:e2e:dp"]
description = "Run dp e2e tests (requires CUDA)"
description = "Run dp e2e tests (requires CUDA). Bootstraps $NSS_GPU_CUDA_EXTRA (default cu129)."
alias = "test-e2e-dp"
run = [
{ task = "bootstrap-nss", args = ["cu129"] },
'mise run bootstrap-nss "${NSS_GPU_CUDA_EXTRA:-cu129}"',
{ task = "test:e2e:dp:run" },
]

Expand All @@ -118,7 +118,7 @@ run = "uv run --frozen --no-sync pytest --dist loadscope -vv -n 0 tests/e2e/test
description = "Dry-run e2e and GPU selectors with pytest collection only. Does not run tests; useful before changing task selectors."
alias = "test-e2e-collect"
run = [
{ task = "bootstrap-nss", args = ["cu129"] },
'mise run bootstrap-nss "${NSS_GPU_CUDA_EXTRA:-cu129}"',
'''
#!/usr/bin/env bash
set -euo pipefail
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,7 @@ All mise tasks check the entire project. Pre-commit scopes checks to staged file
| ty typecheck | `mise run typecheck` | read-only | all files |
| copyright headers | `mise run format-check` | `format`: auto-fix; `check`: read-only | staged files (auto-fix) |
| generated CUDA metadata and uv lock drift | `mise run lock-check` | not checked | on `pyproject.toml` or `cuda_deps.toml` changes |
| installation helper dry runs | `mise run test:installer` | not checked | all wheel-install CI runs |
| DCO signoff | branch protection | not checked | commit-msg hook |

## Documentation
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,19 @@ Read detailed usage below, or jump to the documentation with [Getting Started](h

### Installation

`install_nss.sh` selects the required package indexes for each supported runtime:

```bash
# Run from a source checkout. CUDA 12.9 is the default.
./install_nss.sh
CUDA=130 ./install_nss.sh
CUDA=cpu ./install_nss.sh
```

Use `DRY_RUN=1` to print the command before installing. The helper requires
[uv](https://docs.astral.sh/uv/); use the manual commands below only when you
need to customize the installation.

```bash
# With uv (recommended):
uv pip install "nemo-safe-synthesizer[cu129,engine]" \
Expand Down
6 changes: 2 additions & 4 deletions containers/Dockerfile.cuda
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@
# Multistage GPU Dockerfile for Safe-Synthesizer.
#
# CUDA support is selected through the project extras instead of a CUDA base
# image. The current distributable variant is cu129; the pyproject extra for
# cu130 exists but its container-build matrix row has not landed yet. Adding
# a variant's container coverage should only require a matching pyproject
# extra plus another workflow matrix row.
# image. Supported variants are cu129 and cu130; adding a future variant should
# only require a matching pyproject extra plus another workflow matrix row.
#
# Build:
# docker build -f containers/Dockerfile.cuda \
Expand Down
2 changes: 1 addition & 1 deletion docs/developer-guide/docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ The variant name is intentionally the same as the CUDA package extra.
| Variant | Extra | Workflow status |
|---------|-------|-----------------|
| `cu129` | `cu129` | Enabled |
| `cu130` | `cu130` | Manifest, GPU smoke-test, and non-required GPU E2E coverage; no published container image yet |
| `cu130` | `cu130` | Enabled; non-required in CI while the CUDA 13 rollout is validated |

Adding a new variant should be mechanical:

Expand Down
2 changes: 1 addition & 1 deletion docs/user-guide/docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ the CUDA dependency variant.
- Docker with GPU support
- [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html)
installed and configured
- An NVIDIA driver compatible with the image's CUDA 12.9 libraries
- An NVIDIA driver compatible with the selected image's CUDA libraries
- An NVIDIA GPU (A100 or larger recommended)

Verify that Docker can expose the GPU before running the workload:
Expand Down
13 changes: 13 additions & 0 deletions docs/user-guide/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ does at each stage.
notebooks. Useful for evaluating Safe Synthesizer without a local NVIDIA GPU. The
instance bills continuously and cannot be paused -- delete it when you are finished.

From a source checkout, use the installation helper to select a supported
runtime and its package indexes:

```bash
./install_nss.sh # CUDA 12.9 (default)
CUDA=130 ./install_nss.sh # CUDA 13.0
CUDA=cpu ./install_nss.sh # CPU-only development and validation
```

The helper requires [uv](https://docs.astral.sh/uv/) and accepts `DRY_RUN=1`
to print its command without installing. CUDA 13.0 requires an NVIDIA driver
version 580.65.06 or newer.

The CUDA and CPU extras depend on packages (PyTorch, FlashInfer) hosted on
indexes outside PyPI. You must pass the extra index URLs shown below.

Expand Down
Loading
Loading