Skip to content

fix(cuda): stop sending every RTX 40-series card to the CPU - #1289

Merged
debpalash merged 5 commits into
mainfrom
fix/cuda-arch-binary-compat-1285
Jul 29, 2026
Merged

fix(cuda): stop sending every RTX 40-series card to the CPU#1289
debpalash merged 5 commits into
mainfrom
fix/cuda-arch-binary-compat-1285

Conversation

@debpalash

@debpalash debpalash commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Closes #1285.

Not one card — a whole generation

The SM-arch gate required the device's exact tag to appear in torch.cuda.get_arch_list(). NVIDIA's compatibility rules are not exact, and PyTorch deliberately relies on that.

SASS is binary-compatible upward within a major version: a cubin built for 8.6 runs on any 8.x device with minor ≥ 6. So the official wheels ship sm_80 and sm_86 and no sm_89 — the 8.6 kernels already cover Ada. The reporter's real cu128 arch list shows exactly this:

sm_61, sm_70, sm_75, sm_80, sm_86, sm_90, sm_100, sm_120

sm_89 is absent by design. Exact matching read that as "unsupported", check_device_compatibility() returned False, and get_best_device() silently returned "cpu".

RTX 4060, 4070, 4080 and 4090 are all sm_89. Every one of them has been running TTS on the CPU on hardware that works fine — and being told their GPU was unsupported.

The fix

cuda_build_covers() applies the actual rules:

Tag Rule
sm_XY covers same-major devices with minor ≥ Y (SASS upward compat)
compute_XY JIT-compiles forward to any newer arch (PTX)
sm_90a / compute_100f architecture-specific — exact capability only

Unparseable entries are skipped rather than guessed at, and an empty arch list still degrades to "compatible", preserving the pre-existing fail-open contract.

Also: the remediation text pointed users at a nightly index for a stable, long-supported card. It now names the stable cu128 index.

Tests

12 cases in tests/test_cuda_arch_compat.py, built on the arch list verbatim from the report: the Ada regression (4060 + 4090), Jetson Orin (8.7), downward-within-major rejection (8.9 cubin does not run on 8.6), cross-major rejection, PTX forward-JIT, arch-specific suffixes, unparseable entries, and a genuine sm_120-on-an-old-wheel mismatch so the gate is proven to still catch real problems.

Direct before/after on the reporter's arch list:

RTX 4060 (sm_89) vs real cu128 arch list
  OLD exact-match  -> supported=False  => forced to CPU
  NEW compat rules -> supported=True

test_rocm_arch_gate.py + test_device_caps.py + new suite: 50 passing, so #1228's ROCm branch is untouched.

CUDA architecture detection now uses a new cuda_build_covers() compatibility check (upward SASS within the same major, forward PTX JIT, exact matching for sm_* suffix targets, skipping malformed entries, and fail-open for empty/unknown lists) instead of requiring an exact sm_XX/compute_XX match, fixing cases like RTX 40-series (sm_89) being incorrectly marked unsupported; this is covered by expanded CUDA coverage tests including Ada/Jetson Orin, PTX forward-JIT, suffix/non-forwarding behavior, malformed-entry handling, and true mismatches. Generation-time UX also adds fire-and-forget preflight to warn users when the selected engine may run slowly, driven by shared routing-notice logic and new localized “generate caveat” strings. Main risk to review: the compatibility rule boundaries (especially suffix and PTX/SASS forward-compat interpretation) and the “warn without awaiting” behavior to ensure warnings don’t misfire or degrade error handling in edge cases.

debpalash and others added 2 commits July 28, 2026 16:38
… not after

Six reports are the same story: #1240, #1246, #1248, #1277, #1283, #1284 —
4 GB and 6 GB cards running an engine that wants 6 GB, each one waiting out
the full 300s compute budget to be told the job "was too heavy". The routing
layer knew the whole time. The error text even names the card and the figure.

The caveat only ever surfaced on the engine-PICK toast, so it reached people
who changed engines and nobody whose engine was already selected — the
default, or one persisted from a previous session. That is most users.
/generate does return X-OmniVoice-Routing, but a response header arrives when
the job ends, five minutes too late to be a warning.

So the check moves to the chokepoint every synth path shares (api/generate.ts,
same argument as the in-flight count). Fire-and-forget: never awaited, so it
cannot add latency to the request it warns about; never throws, so an
unreachable backend costs a warning rather than a generate; once per
engine+reason per session, so it informs instead of nagging. Advisory, not
blocking — the driver can page to system RAM and short inputs fit where long
ones don't.

Extracts routingNotice() as the single frontend mirror of the backend's
routing_notice(). Two callers now need "is this verdict worth interrupting
for", and two inline copies would drift — invisibly, until someone on DirectML
or an unavailable engine gets a hardware warning for a normal pick.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The SM-arch gate required the device's exact tag in get_arch_list(). NVIDIA's
rules are not exact, and PyTorch depends on that: SASS is binary-compatible
UPWARD within a major version, so the official wheels ship sm_80/sm_86 and
deliberately no sm_89 — the 8.6 kernels already cover Ada. Exact matching
therefore declared sm_89 unsupported, check_device_compatibility() returned
False, and get_best_device() silently returned "cpu".

That is every RTX 4060/4070/4080/4090, not just the reporter's card (#1285) —
each one running TTS on the CPU on hardware that works fine, with a message
telling them their GPU was unsupported.

cuda_build_covers() now applies the real rules: sm_XY covers same-major
devices with minor >= Y; compute_XY PTX JITs forward to anything newer; an
a/f suffix (sm_90a) is architecture-specific and matches exactly. Unparseable
entries are skipped, and an empty arch list still degrades to "compatible" —
the pre-existing fail-open contract.

The remediation text also pointed at a NIGHTLY index for what is a stable
supported card; it now names the stable cu128 index.

12 tests: the Ada regression, Jetson Orin (8.7), downward-within-major and
cross-major rejection, PTX forward-JIT, arch-specific suffixes, and a genuine
sm_120-on-old-wheel mismatch so the gate is proven to still work.

Closes #1285

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment thread frontend/src/utils/generatePreflight.js
@greptile-apps

greptile-apps Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Updates CUDA architecture compatibility detection and remediation guidance.

  • Recognizes upward-compatible same-major SASS targets, forward-compatible PTX targets, and exact-only architecture-specific suffixes.
  • Adds regression coverage for Ada, Jetson Orin, PTX JIT, malformed tags, architecture-specific targets, and genuine incompatibility.

Important Files Changed

Filename Overview
backend/core/device_caps.py Replaces exact CUDA architecture matching with compatibility-aware SASS, PTX, and architecture-specific target handling.
backend/services/model_manager.py Updates the incompatible-device remediation message to recommend the stable CUDA 12.8 PyTorch index.
tests/test_cuda_arch_compat.py Adds focused coverage for CUDA architecture compatibility and CPU-fallback prevention.

Reviews (3): Last reviewed commit: "fix: restore generate.ts and generatePre..." | Re-trigger Greptile

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b88a77de-b331-457e-a53c-6f60668045d4

📥 Commits

Reviewing files that changed from the base of the PR and between b855b57 and 6ab2fe5.

📒 Files selected for processing (1)
  • tests/test_cuda_arch_compat.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_cuda_arch_compat.py

📝 Walkthrough

Walkthrough

The backend now evaluates CUDA builds using forward-compatible SASS/PTX rules. The frontend centralizes routing caveat handling and adds cached, deduplicated generation-time warnings with localized messages.

Changes

CUDA architecture compatibility

Layer / File(s) Summary
CUDA coverage evaluation
backend/core/device_caps.py, backend/services/model_manager.py, tests/test_cuda_arch_compat.py
CUDA arch tags are parsed using SASS, PTX, and suffix compatibility rules; device checks, reinstall guidance, and regression tests are updated.

Generation-time engine caveats

Layer / File(s) Summary
Shared routing notice semantics
frontend/src/utils/routingNotice.js, frontend/src/utils/engineSelectToast.js
Routing outcomes are normalized and used for engine-selection warning toasts.
Generation preflight warning
frontend/src/utils/generatePreflight.js, frontend/src/api/generate.ts, frontend/src/test/generatePreflight.test.js
Generation starts a non-blocking cached engine check that deduplicates caveat warnings and handles fetch failures silently.
Localized generation caveats
frontend/src/i18n/locales/*.json
Supported locales add engines.generateCaveat messages with engine and reason placeholders.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 6 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description skips the required Summary, Changes, Type, Testing, and Checklist sections from the repo template. Reformat the description to the template and fill in the missing sections, especially changes, type, testing, and checklist.
Out of Scope Changes check ⚠️ Warning Frontend generate-preflight, toast, and locale changes add new UX behavior that is not required by #1285's CUDA detection fix. Split the frontend warning and i18n work into a separate PR unless it is covered by an explicit linked issue.
Docstring Coverage ⚠️ Warning Docstring coverage is 76.47% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed The title is conventional-commit style, scoped to CUDA, and matches the main fix; the issue is referenced in the PR body.
Linked Issues check ✅ Passed The CUDA compatibility fix now treats sm_89 as covered by cu128-style builds, matching #1285's goal of avoiding CPU fallback on RTX 40-series GPUs.
Cross-Platform Default Parity ✅ Passed No platform-divergent default: the CUDA fix is shared logic for all CUDA hosts, and the new generate preflight runs in the common frontend path without OS gating.
I18n Completeness (21 Locales) ✅ Passed PASS: frontend/src/i18n/locales has generateCaveat in all 21 locale files; touched frontend code uses i18n keys only, with no hardcoded user-facing strings.
Local-First Guarantee ✅ Passed New synth-time warning only calls the app’s own /engines backend endpoint; errors are swallowed, and no new telemetry/account/API-key dependency was added.
Backward Compatibility ✅ Passed Only GPU-compatibility detection and warning text changed; no schema/migration files, omnivoice_data handling, or model-cache/weights paths were modified.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/utils/generatePreflight.js`:
- Around line 37-42: The enginesCached function must evict a rejected
listEngines promise so subsequent calls can retry after recovery. Attach
rejection handling to the cached promise, clearing cache only when it still
references that failed promise, and add a regression test that verifies a failed
first call is followed by a successful retry.

In `@tests/test_cuda_arch_compat.py`:
- Around line 19-20: Move the application imports of device_caps,
arch_unsupported, and cuda_build_covers out of module scope in
tests/test_cuda_arch_compat.py and resolve them inside a fixture or helper
invoked by each test. Ensure every test receives the freshly resolved symbols at
runtime, avoiding stale sys.modules state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ca2dec3a-717c-401a-8901-19702c5eda69

📥 Commits

Reviewing files that changed from the base of the PR and between 574b283 and b855b57.

📒 Files selected for processing (29)
  • backend/core/device_caps.py
  • backend/services/model_manager.py
  • frontend/src/api/generate.ts
  • frontend/src/i18n/locales/ar.json
  • frontend/src/i18n/locales/de.json
  • frontend/src/i18n/locales/en.json
  • frontend/src/i18n/locales/es.json
  • frontend/src/i18n/locales/fr.json
  • frontend/src/i18n/locales/hi.json
  • frontend/src/i18n/locales/id.json
  • frontend/src/i18n/locales/it.json
  • frontend/src/i18n/locales/ja.json
  • frontend/src/i18n/locales/ko.json
  • frontend/src/i18n/locales/nl.json
  • frontend/src/i18n/locales/pl.json
  • frontend/src/i18n/locales/pt.json
  • frontend/src/i18n/locales/ru.json
  • frontend/src/i18n/locales/sv.json
  • frontend/src/i18n/locales/th.json
  • frontend/src/i18n/locales/tr.json
  • frontend/src/i18n/locales/uk.json
  • frontend/src/i18n/locales/vi.json
  • frontend/src/i18n/locales/zh-CN.json
  • frontend/src/i18n/locales/zh-TW.json
  • frontend/src/test/generatePreflight.test.js
  • frontend/src/utils/engineSelectToast.js
  • frontend/src/utils/generatePreflight.js
  • frontend/src/utils/routingNotice.js
  • tests/test_cuda_arch_compat.py

Comment thread frontend/src/utils/generatePreflight.js
Comment thread tests/test_cuda_arch_compat.py Outdated
The tests/** review contract forbids module-level imports of app modules —
they go stale under sys.modules pollution from other suites, which is the live
cause of #1269's cross-suite failures. Binds core.device_caps per call.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
debpalash and others added 2 commits July 29, 2026 14:14
# Conflicts:
#	frontend/src/api/generate.ts
#	frontend/src/test/generatePreflight.test.js
#	frontend/src/utils/engineSelectToast.js
#	frontend/src/utils/generatePreflight.js
Conflict markers were committed in the previous merge — `git add` on the
directory staged both files as resolved while the markers were still in them.
Both belong to #1288 and are unchanged by this PR, so they take main's version
verbatim.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] PyTorch build missing support for RTX 4060 (sm_89) / Falls back to CPU

1 participant