From fb2f80d9c52fed6417038965713d27eff3fad01b Mon Sep 17 00:00:00 2001 From: "Gabriel A. Devenyi" Date: Sat, 22 Aug 2026 08:19:26 -0400 Subject: [PATCH] build(deps): make the torch CUDA build a uv group choice `[tool.uv.sources]` pinned torch to the cu130 index unconditionally, so `uv sync` in a checkout could only ever resolve the CUDA 13 build. CUDA 13 dropped Maxwell, Pascal and Volta, and its wheels carry no cubin below sm_75, so on a pre-Turing card that resolution produces a torch that cannot address the GPU at all. Split the pin into two conflicting dependency groups. cu130 is the default group, so a plain `uv sync` resolves byte-for-byte as it did before; a pre-Turing GPU asks for the other one: uv sync --no-default-groups --group cu126 torch 2.11.0+cu126 exists on the pytorch index and satisfies the existing `torch>=2.11,<2.12` range, so no version constraint moves. Its arch list is sm_50/60/70/75/80/86/90, and the sm_60 cubin runs on sm_61 under CUDA's minor-version binary compatibility guarantee. Declaring the groups conflicting means the two can never resolve into one environment; asking for both is a clean error rather than a silent winner. `sglang-kernel` keeps its single cu130 pin deliberately: it publishes cu130 wheels only and its AOT kernels are sm_75+ regardless, so a pre-Turing install leaves the `sgl` extra off (same for flashinfer's `fi`) and falls back to the pure-triton kernels. Verified against the real dependency set (130 packages resolved): uv export -> torch==2.11.0+cu130 uv export --no-default-groups --group cu126 -> torch==2.11.0+cu126 uv export --group cu126 --group cu130 -> error: groups are incompatible Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01KQy3DziJN9peJ7nA8L59ns --- pyproject.toml | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8bd653f87..c46edd23c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -89,10 +89,39 @@ accel = ["freetoken[fi,sgl]"] # indexes serve. `explicit = true` scopes each index to the one package that needs it # (the torch index also mirrors stale copies of common deps, e.g. packaging<=24.1, # which would otherwise shadow PyPI under uv's first-index strategy). +# +# Which CUDA build of torch to pin is a *group* choice. cu130 is the default, so a plain +# `uv sync` resolves exactly as it did before this was split out. CUDA 13 dropped Maxwell, +# Pascal and Volta, so its wheels carry no cubin below sm_75 and simply cannot run on those +# cards; the cu126 build still ships sm_50/60/70/75/80/86/90, and its sm_60 cubin runs on +# sm_61 under CUDA's minor-version binary compatibility guarantee. For a pre-Turing GPU: +# +# uv sync --no-default-groups --group cu126 +# +# The groups are declared conflicting, so the two can never resolve into one environment. +[dependency-groups] +cu130 = ["torch>=2.11,<2.12"] +cu126 = ["torch>=2.11,<2.12"] + +[tool.uv] +default-groups = ["cu130"] +conflicts = [[{ group = "cu126" }, { group = "cu130" }]] + [tool.uv.sources] -torch = { index = "pytorch-cu130" } +torch = [ + { index = "pytorch-cu126", group = "cu126" }, + { index = "pytorch-cu130", group = "cu130" }, +] +# No cu126 counterpart on purpose: sglang-kernel publishes cu130 wheels only, and its AOT +# kernels are sm_75+ regardless, so a pre-Turing install leaves the `sgl` extra off and +# falls back to the pure-triton kernels. Same for flashinfer's `fi` extra (cu13 cubins). sglang-kernel = { index = "sglang-cu130" } +[[tool.uv.index]] +name = "pytorch-cu126" +url = "https://download.pytorch.org/whl/cu126" +explicit = true + [[tool.uv.index]] name = "pytorch-cu130" url = "https://download.pytorch.org/whl/cu130"