uv and Build System
Package management with uv, extras for CPU/CUDA, hatch build, and dynamic versioning.
Bootstrap Commands
# Full dev environment (tools + Python + CPU deps)
mise run setup && mise run bootstrap-nss cpu
# Pick a variant:
mise run bootstrap-nss dev # dev tools only (no engine/torch)
mise run bootstrap-nss cpu # + engine + CPU PyTorch
mise run bootstrap-nss cu129 # + engine + CUDA 12.9 PyTorch
mise run bootstrap-nss cuda # alias for cu129
mise run bootstrap-nss engine # + engine (no torch)
# Slurm: force Python, caches, and the project venv onto Lustre
LUSTRE_DIR="/path/to/container-visible/project/directory" \
MISE_IGNORED_CONFIG_PATHS="$HOME/.config/mise/config.toml" \
MISE_LOCKED=1 mise run bootstrap-nss-slurm cu129
Under the hood: uv sync --frozen --extra <extra> [--extra engine] --group dev
bootstrap-nss-slurm requires LUSTRE_DIR, installs the pinned Python under
that directory, recreates .venv if its interpreter is not container-visible,
then runs the same frozen profile sync as bootstrap-nss.
Extras and Conflicts
| Extra |
What it installs |
cpu |
PyTorch CPU, faiss-cpu, flashinfer (Linux only) |
cu129 |
PyTorch+CUDA 12.9, faiss-gpu, flashinfer-jit-cache |
engine |
ML pipeline deps (outlines, wandb, tiktoken, etc.) -- no torch |
microservices |
nemo-microservices from local path |
cpu and cu129 conflict -- you must pick one, never both. Enforced in [tool.uv] conflicts.
Index Management
PyTorch wheels come from dedicated indexes, not PyPI:
| Index |
URL |
Used for |
pytorch-cpu |
download.pytorch.org/whl/cpu |
torch, torchvision (CPU, Linux) |
pytorch-cu129 |
download.pytorch.org/whl/cu129 |
torch, torchvision, triton (CUDA) |
nv-shared-pypi-local |
NVIDIA Artifactory |
Internal NVIDIA packages |
flashinfer-jit-cache-cu129 |
flashinfer.ai/whl/cu129 |
FlashInfer JIT cache |
nvidia-pypi-public |
pypi.nvidia.com |
Public NVIDIA packages |
All indexes are explicit = true (only used when a package is mapped to them in [tool.uv.sources]).
Adding Dependencies
# Add to base dependencies
uv add <package>
# Add to a dependency group
uv add --group dev <package>
uv add --group test <package>
# Change CPU or CUDA runtime extras
# Edit cuda_deps.toml, regenerate pyproject.toml, then lock.
uv run --frozen tools/gen_cuda_deps.py cuda_deps.toml --pyproject pyproject.toml
mise run lock:update
After any change: mise run lock:update to regenerate uv.lock. Pre-commit verifies the lock is up to date.
The generated CPU/CUDA sections of pyproject.toml must not be edited directly;
mise run check:lock verifies that they match cuda_deps.toml. The
generator owns the complete [tool.uv.sources] and [[tool.uv.index]]
sections, so add every source or index there through cuda_deps.toml.
Dependency Groups
| Group |
Contains |
dev |
Includes docs + test groups, plus ipywidgets, pandas-stubs, prek, typer, etc. |
test |
pytest, pytest-asyncio, pytest-cov, pytest-env, pytest-subtests, pytest-timeout, pytest-xdist |
docs |
mkdocs-material, mkdocstrings, mkdocs-gen-files, etc. |
Running Tools
Always use uv run to ensure the correct environment:
uv run pytest ...
uv run --frozen pytest ... # Don't update lock
uv run --group docs mkdocs serve
uv run --frozen --no-project --group docs mkdocs build
Build and Version
# Build wheel (version from git tag via uv-dynamic-versioning)
mise run build-wheel # or: uv build --wheel
# Publish to NVIDIA Artifactory
mise run publish:internal
Version source: uv-dynamic-versioning reads git tags (PEP 440 style). Fallback 0.0.0 for shallow clones.
Build backend: hatchling with wheel target packages = ["src/nemo_safe_synthesizer"].
Key pyproject.toml Sections
| Section |
Purpose |
[tool.uv] |
Required version, cache-keys, conflicts, overrides, environments |
[tool.uv.sources] |
Map packages to specific indexes by extra/marker |
[[tool.uv.index]] |
Define named package indexes |
[build-system] |
hatchling + uv-dynamic-versioning |
[tool.hatch.version] |
Source: uv-dynamic-versioning |
[tool.uv-dynamic-versioning] |
Git VCS, PEP 440, fallback version |
[tool.vendor-package] |
Vendoring into NMP SDK |
Vendor Package
[tool.vendor-package] configures vendoring Safe-Synthesizer into the NMP SDK:
- Target:
beta.safe_synthesizer
- Includes specific paths from
src/ and tests/
- Used by the
prek tool during NMP sync
Conventions
- Never use
pip -- always uv
- Use
--frozen in CI and Make targets to prevent lock updates
- Use
uv run to run tools (pytest, mkdocs, etc.)
- uv version is pinned in
.mise.toml.
- Edit non-generated
pyproject.toml sections directly (e.g. dependency groups); CPU/CUDA extras go through cuda_deps.toml instead, then mise run lock:update
- Use
uv add for base/group deps
1---2name: uv-build3description: uv package management, dependency groups, PyTorch index handling, hatch build system, and versioning for this repo. Triggers on: uv, uv sync, uv lock, uv add, uv build, dependency, pyproject.toml, extras, cpu, cu129, hatch, wheel, version, publish.4license: Apache-2.05---67# uv and Build System89Package management with uv, extras for CPU/CUDA, hatch build, and dynamic versioning.1011## Bootstrap Commands1213```bash14# Full dev environment (tools + Python + CPU deps)15mise run setup && mise run bootstrap-nss cpu1617# Pick a variant:18mise run bootstrap-nss dev # dev tools only (no engine/torch)19mise run bootstrap-nss cpu # + engine + CPU PyTorch20mise run bootstrap-nss cu129 # + engine + CUDA 12.9 PyTorch21mise run bootstrap-nss cuda # alias for cu12922mise run bootstrap-nss engine # + engine (no torch)2324# Slurm: force Python, caches, and the project venv onto Lustre25LUSTRE_DIR="/path/to/container-visible/project/directory" \26 MISE_IGNORED_CONFIG_PATHS="$HOME/.config/mise/config.toml" \27 MISE_LOCKED=1 mise run bootstrap-nss-slurm cu12928```2930Under the hood: `uv sync --frozen --extra <extra> [--extra engine] --group dev`3132`bootstrap-nss-slurm` requires `LUSTRE_DIR`, installs the pinned Python under33that directory, recreates `.venv` if its interpreter is not container-visible,34then runs the same frozen profile sync as `bootstrap-nss`.3536## Extras and Conflicts3738| Extra | What it installs |39|-------|------------------|40| `cpu` | PyTorch CPU, faiss-cpu, flashinfer (Linux only) |41| `cu129` | PyTorch+CUDA 12.9, faiss-gpu, flashinfer-jit-cache |42| `engine` | ML pipeline deps (outlines, wandb, tiktoken, etc.) -- no torch |43| `microservices` | `nemo-microservices` from local path |4445`cpu` and `cu129` conflict -- you must pick one, never both. Enforced in `[tool.uv] conflicts`.4647## Index Management4849PyTorch wheels come from dedicated indexes, not PyPI:5051| Index | URL | Used for |52|-------|-----|----------|53| `pytorch-cpu` | `download.pytorch.org/whl/cpu` | torch, torchvision (CPU, Linux) |54| `pytorch-cu129` | `download.pytorch.org/whl/cu129` | torch, torchvision, triton (CUDA) |55| `nv-shared-pypi-local` | NVIDIA Artifactory | Internal NVIDIA packages |56| `flashinfer-jit-cache-cu129` | `flashinfer.ai/whl/cu129` | FlashInfer JIT cache |57| `nvidia-pypi-public` | `pypi.nvidia.com` | Public NVIDIA packages |5859All indexes are `explicit = true` (only used when a package is mapped to them in `[tool.uv.sources]`).6061## Adding Dependencies6263```bash64# Add to base dependencies65uv add <package>6667# Add to a dependency group68uv add --group dev <package>69uv add --group test <package>7071# Change CPU or CUDA runtime extras72# Edit cuda_deps.toml, regenerate pyproject.toml, then lock.73uv run --frozen tools/gen_cuda_deps.py cuda_deps.toml --pyproject pyproject.toml74mise run lock:update75```7677After any change: `mise run lock:update` to regenerate `uv.lock`. Pre-commit verifies the lock is up to date.78The generated CPU/CUDA sections of `pyproject.toml` must not be edited directly;79`mise run check:lock` verifies that they match `cuda_deps.toml`. The80generator owns the complete `[tool.uv.sources]` and `[[tool.uv.index]]`81sections, so add every source or index there through `cuda_deps.toml`.8283## Dependency Groups8485| Group | Contains |86|-------|----------|87| `dev` | Includes `docs` + `test` groups, plus ipywidgets, pandas-stubs, prek, typer, etc. |88| `test` | pytest, pytest-asyncio, pytest-cov, pytest-env, pytest-subtests, pytest-timeout, pytest-xdist |89| `docs` | mkdocs-material, mkdocstrings, mkdocs-gen-files, etc. |9091## Running Tools9293Always use `uv run` to ensure the correct environment:9495```bash96uv run pytest ...97uv run --frozen pytest ... # Don't update lock98uv run --group docs mkdocs serve99uv run --frozen --no-project --group docs mkdocs build100```101102## Build and Version103104```bash105# Build wheel (version from git tag via uv-dynamic-versioning)106mise run build-wheel # or: uv build --wheel107108# Publish to NVIDIA Artifactory109mise run publish:internal110```111112Version source: `uv-dynamic-versioning` reads git tags (PEP 440 style). Fallback `0.0.0` for shallow clones.113114Build backend: `hatchling` with wheel target `packages = ["src/nemo_safe_synthesizer"]`.115116## Key pyproject.toml Sections117118| Section | Purpose |119|---------|---------|120| `[tool.uv]` | Required version, cache-keys, conflicts, overrides, environments |121| `[tool.uv.sources]` | Map packages to specific indexes by extra/marker |122| `[[tool.uv.index]]` | Define named package indexes |123| `[build-system]` | hatchling + uv-dynamic-versioning |124| `[tool.hatch.version]` | Source: uv-dynamic-versioning |125| `[tool.uv-dynamic-versioning]` | Git VCS, PEP 440, fallback version |126| `[tool.vendor-package]` | Vendoring into NMP SDK |127128## Vendor Package129130`[tool.vendor-package]` configures vendoring Safe-Synthesizer into the NMP SDK:131- Target: `beta.safe_synthesizer`132- Includes specific paths from `src/` and `tests/`133- Used by the `prek` tool during NMP sync134135## Conventions1361371. Never use `pip` -- always `uv`1382. Use `--frozen` in CI and Make targets to prevent lock updates1393. Use `uv run` to run tools (pytest, mkdocs, etc.)1404. uv version is pinned in `.mise.toml`.1415. Edit non-generated `pyproject.toml` sections directly (e.g. dependency groups); CPU/CUDA extras go through `cuda_deps.toml` instead, then `mise run lock:update`1426. Use `uv add` for base/group deps