Skip to content

Fix ADTOF on ROCm, authenticate HuggingFace downloads, rebuild the macOS variant - #31

Merged
tsondo merged 5 commits into
mainfrom
claude/repo-rocm-hf-auth-issues-wcssf3
Sep 13, 2026
Merged

tsondo merged 5 commits into
mainfrom
claude/repo-rocm-hf-auth-issues-wcssf3

Conversation

@tsondo

@tsondo tsondo commented Aug 30, 2026

Copy link
Copy Markdown
Owner

Three related fixes, one commit each.

1. adtof-pytorch missing from pyproject.toml.ROCM (+ variant drift guard)

The ROCm variant is copied over pyproject.toml and never touched during normal development, so it silently missed adtof-pytorch — on AMD hardware the ADTOF drum transcription pipeline had no package to import. Added it (its own deps — torch, librosa, pretty_midi, numpy — are already declared and platform-neutral).

To stop the class of bug, tests/test_pyproject_variants.py diffs the variants' dependency lists by PEP 503 canonical name and by version specifier, in both directions. Legitimate platform splits live in an exception table with reasons (nvidia-cuda-runtime-cu12, triton-rocm, nano-vllm on macOS), and the exceptions are exact — a stale exemption fails too. Removing the adtof-pytorch line reproduces the original failure. The guard runs in the resolve job (it only reads TOML, so it fires before the slow resolve) and in the test job.

2. BS-Roformer downloads used unauthenticated urllib → HTTP 401

models/roformer_loader.py fetched checkpoints with urllib.request.urlretrieve, which sends no credentials — huggingface.co answers a gated repo with 401, so roformer-jarredou-6stem (the one Hub-hosted Roformer spec) failed with an opaque "Download failed".

New utils/hf_hub.py:

  • get_hf_token() — one resolution order everywhere: the UI-saved token, then HF_TOKEN / huggingface-cli login. models/musicgen_loader.py had this inline and now shares it.
  • download_file() — parses huggingface.co/.../resolve/... URLs (nested paths, encoded revisions, dataset/space prefixes; look-alike hosts are rejected so the token can't leak) and fetches via hf_hub_download with the token. GitHub-release / raw.githubusercontent.com checkpoints keep the plain path.

Both paths are atomic (no truncated file can pose as a cache hit), Hub downloads stage beside the destination instead of duplicating a GB in the shared HF cache, and 401/403 now raises a message naming the repo URL and distinguishing "no token" from "token rejected". 19 tests in tests/test_hf_hub.py.

3. pyproject.toml.MAC rebuilt; CLAUDE.md/README brought back to reality

The macOS variant predated the web UI: it still declared dearpygui, had no fastapi/uvicorn, no AceStep, floated torch at >=2.1.0, and built a gui package that no longer exists — copying it in produced an install that couldn't run the app. It is now rebuilt from the current CUDA file, matching line for line except:

  • torch/torchaudio/torchvision keep the same 2.10.0 pins from PyPI (macOS arm64 wheels carry MPS; no wheel index)
  • nano-vllm dropped (ACE-Step's marker excludes it on darwin/arm64 — the LM runs through MLX; its flash-attn requirements are CUDA wheel URLs) and nvidia-cuda-runtime-cu12 with it

The parity guard now covers all three variants, and the rocm-resolve CI job becomes a variant-resolve matrix over ROCM + MAC (the darwin/arm64 graph resolves from Linux because the variant pins its target environment — verified locally, with AceStep resolving via mlx/mlx-lm; the committed uv.lock stays cu130).

Docs: CLAUDE.md's BS-Roformer claims corrected (3 registered models, not 6 — roformer-kj-vocals never existed in the registry), the project tree catches up on ~9 undocumented modules, and README's macOS limitations lose two no-longer-true claims (ai-edge-litert now ships macOS arm64 wheels; nothing ever hid mdx_extra_q from the UI) and gain the sdist-build note for pyworld/diffq.

Validation

  • tests/test_pyproject_variants.py + tests/test_hf_hub.py: 22 passed locally; drift guard verified to fail on the original bug and on stale exemptions
  • uv lock run against both the ROCM probe and the full MAC variant; lock restored afterwards
  • ci.yml parses; both new test files wired into the CPU test job

macOS resolution is CI-verified but untested on real hardware.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Vb2foWspag8yfjz1hvxas2


Generated by Claude Code

claude added 5 commits August 30, 2026 11:49
pyproject.toml.ROCM is a copy-over-the-top variant that only swaps the
torch wheel index, so it is never touched during normal development and
silently misses every dependency added to the CUDA pyproject. That is how
adtof-pytorch came to be absent from it: on AMD hardware the ADTOF drum
transcription pipeline had no package to import at all.

Add adtof-pytorch to the ROCm variant — its own requirements are torch,
librosa, pretty_midi and numpy, all already declared there and all
platform-neutral, so nothing else needs to move.

Then stop the class of bug rather than just this instance:
tests/test_pyproject_variants.py diffs the two dependency lists by
PEP 503 canonical name and by version specifier, in both directions, and
fails on any difference not recorded in PLATFORM_ONLY with a reason. A
third test keeps PLATFORM_ONLY itself honest, so a stale exemption cannot
quietly re-open the hole. Removing the adtof line reproduces the original
failure; restoring it passes.

The guard reads only TOML, so it runs in the rocm-resolve job (before the
slow resolve) as well as in the main test job.

pyproject.toml.MAC is deliberately left out of the diff: it predates the
web UI — still declaring dearpygui, with no fastapi or uvicorn — and
needs a rewrite before a comparison against it would mean anything.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vb2foWspag8yfjz1hvxas2
The Roformer loader fetched checkpoints with urllib.request.urlretrieve,
which sends no credentials. huggingface.co answers an anonymous request
for a gated repo with HTTP 401, so loading roformer-jarredou-6stem — the
one Roformer spec hosted on the Hub — failed with an opaque "Download
failed: HTTP Error 401", with nothing to tell the user a token was what
was missing.

Add utils/hf_hub.py, which owns both halves of the problem:

  get_hf_token()  resolves the token once, in one place: the token saved
                  through the Synth tab's banner first, then whatever
                  huggingface_hub finds (HF_TOKEN, huggingface-cli login).
                  models/musicgen_loader.py had this logic inline and now
                  calls the shared version, so the two cannot diverge.

  download_file() parses huggingface.co /resolve/ URLs into a repo id,
                  revision, filename and repo type, and fetches them with
                  hf_hub_download carrying that token. Anything else —
                  the GitHub-release and raw.githubusercontent checkpoints
                  the other two Roformer specs use — takes the plain path
                  unchanged, since there is nothing there to authenticate.

Both paths stay atomic: the file only appears at its destination once the
bytes are complete, so an interrupted download can't leave a truncated
file that the next load treats as a cache hit. Hub downloads stage beside
the destination rather than in the shared HF cache, so a gigabyte
checkpoint isn't stored twice, and a symlinked result (huggingface_hub
< 0.26 could satisfy local_dir that way) is materialised rather than
moved, so pruning the Hub cache can't dangle our copy.

A 401/403/gated failure now raises a message naming the repo's URL and
the two steps to fix it, and distinguishes "you have no token" from "your
token was rejected" — a different fix each. Unrelated failures (timeouts,
missing files) pass through untouched rather than being mislabeled as an
auth problem.

tests/test_hf_hub.py covers URL parsing (nested paths, single-segment
repo ids, encoded revisions, dataset/space prefixes, and a look-alike
host that must not receive our token), the routing decision in both
directions, partial-file cleanup, and the error text.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vb2foWspag8yfjz1hvxas2
pyproject.toml.MAC predated the web UI rewrite: it still declared
dearpygui and sounddevice, had no fastapi/uvicorn/httpx, no AceStep, no
Enhance/RVC/effects stack, floated torch at >=2.1.0, built a "gui"
package that no longer exists, and pointed its console script at
gui.app:main. Copying it over pyproject.toml produced an install that
could not run the application at all.

Rebuild it from the current CUDA pyproject so the dependency lists match
line for line, with three deliberate differences, each recorded in the
parity test's exception table:

  - torch/torchaudio/torchvision keep the same 2.10.0 pins but come from
    PyPI — the macOS arm64 wheels carry MPS, so no wheel index is needed
  - nano-vllm is dropped: ACE-Step's own environment marker excludes it
    on darwin/arm64 (its LM runs through MLX there), and its flash-attn
    requirements are CUDA wheel URLs
  - nvidia-cuda-runtime-cu12 goes with it — it existed only to provide
    the CUDA runtime that flash-attn links against

pyworld and diffq publish no cp312 macOS wheels and build from sdist,
noted in the header and README (Xcode command line tools required).

Generalize tests/test_pyproject_variants.py from a CUDA/ROCm pair diff
to all three variants: every dependency must be declared everywhere,
PLATFORM_EXCEPTIONS records which variants may omit a package and why,
and exceptions are exact — a package declared where its entry says it
must be absent fails too, so a stale exemption cannot hide new drift.
The rocm-resolve CI job becomes a variant-resolve matrix over ROCM and
MAC; the darwin/arm64 graph resolves from a Linux runner because the
variant pins its target environment, and the full resolve was verified
here (AceStep resolves for darwin/arm64 with mlx/mlx-lm, adtof-pytorch
and diffq included; lock restored afterwards, as the committed uv.lock
stays cu130).

Also bring CLAUDE.md and README back to reality:

  - BS-Roformer: 3 registered models, not 6 — roformer-kj-vocals and the
    "+ 2 more" never existed in models/registry.py
  - project tree: add the files the map had fallen behind on
    (transcribe/voice API routers, transcribe/RVC pipelines,
    sfx_renderer, music21_bridge, the four pitch-shift utils)
  - macOS platform notes rewritten to match the new variant; the README
    limitations section loses two claims that are no longer true
    (ai-edge-litert now ships macOS arm64 wheels, and no code ever hid
    mdx_extra_q from the UI) and gains the sdist-build note

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vb2foWspag8yfjz1hvxas2
Both branches appended test files to the same pytest invocation in
ci.yml; the resolution keeps all three additions. CLAUDE.md and README
auto-merged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vb2foWspag8yfjz1hvxas2
Keep the variant-resolve matrix job and parity-check step from this
branch; take main's setup-uv bump to v10.0.1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HxPcFzqTzXT4G86yLrTY71
@tsondo
tsondo merged commit df62147 into main Sep 13, 2026
4 checks passed
@tsondo
tsondo deleted the claude/repo-rocm-hf-auth-issues-wcssf3 branch September 13, 2026 12:44
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.

2 participants