Skip to content

gguf: let a finished GGUF be re-converted - #464

Merged
0xShug0 merged 2 commits into
0xShug0:mainfrom
CaptainArni:gguf-requantize-from-gguf
Sep 5, 2026
Merged

gguf: let a finished GGUF be re-converted#464
0xShug0 merged 2 commits into
0xShug0:mainfrom
CaptainArni:gguf-requantize-from-gguf

Conversation

@CaptainArni

@CaptainArni CaptainArni commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Re-quantising a GGUF that audiocpp_gguf itself produced does not work today, and the way it fails is worse than the failure. From #457:

$ audiocpp_gguf --input ACE-Step1.5-XL-Turbo-bf16.gguf --type q8_0 --output test.gguf
error: no GGUF sidecars were found ...

That message is a symptom. Three things are going on.

A GGUF input's namespaces are ignored. select_package_spec builds the conversion's namespace set from the namespace= labels on the command line, so a GGUF whose tensors are already named dit_xl_turbo_weights/…, lm_weights/…, … presents as one unnamed namespace and matches nothing in its own family.

Which lets an unrelated family's spec match instead. With no --family, the catalog is searched and sense_asr — the only spec accepting a single unnamed namespace with no required on-disk sidecars — validates cleanly. The run then dies later, in sidecar embedding, which is the error above. Worse is when it doesn't die: re-converting kroko-en-community-64-l-q8_0.gguf with any small text file next to it succeeds and writes model_spec_family=sense_asr into the output.

And the sidecars the file already carries go unused. The input GGUF embeds its own configs and tokenizers, but sidecar collection only walks --root, so a standalone GGUF cannot supply them.

Changes

  • input_namespaces() reads the namespaces back out of a GGUF input's tensor names when no explicit label is given, so an audio.cpp GGUF validates against the spec it was built from.
  • A GGUF input's embedded model spec is registered as a top-priority candidate and sets the default family. The file says what it is; guessing from the catalog is both less accurate and how the sense_asr mislabel happens.
  • resolve_sidecar_root() falls back to materialize_gguf_sidecars() when --root is not given and the input's directory offers no sidecars and no --sidecar was passed. Existing invocations resolve exactly as before.

Result

$ audiocpp_gguf --input ace-step-1.5-xl-turbo-bf16.gguf --type q8_0 \
    --keep-type "lm_weights*=bf16" --output ace-step-1.5-xl-turbo-q8dit.gguf
note: reusing sidecars embedded in ace-step-1.5-xl-turbo-bf16.gguf
model_spec_family=ace_step
embedded_sidecars=true

and the Kroko case now writes model_spec_family=kroko_asr.

Docs

docs/models/ace_step.md claimed that building an XL GGUF "needs the other variants' safetensors on hand". It doesn't: the namespace check only reads the CLI labels, and --exclude-prefix drops those tensors before any data is read, so a 76-byte placeholder file works and the build needs 24 GB of downloads rather than 33 GB. That was the reporter's main complaint in #457 and it was a fair one — the section now shows the placeholder, notes that only the two config.jsons are genuinely required, and documents the mixed-precision build below.

The same section now records that q8_0's No (planner sampling can fail) grade is about the planner LM rather than the DiT, with the --keep-type invocation that quantises the DiT alone. Measured on an RTX 5090 at a fixed seed, 20 s of audio: 9.97 GiB against 14.2 GiB, 9.3 s against 14.2 s, and a 0.989 waveform correlation against the bf16 output (0.997 on a sung 40 s take, 0.999 for XL SFT). A fully quantised build of the same weights correlates 0.09 and sings a different lyric line. Both mixed-precision files are up at CaptainArni/audio.cpp-gguf if you want a package row for them.

Testing

There is no harness that drives audiocpp_gguf end to end, so this was verified by hand on Windows/CUDA (RTX 5090):

  • Re-quantising the published 14.2 GiB XL Turbo bf16 GGUF to a q8_0-DiT build: 10.7 GB out, model_spec_family=ace_step, sidecars re-embedded. Generating from the result at a fixed seed gives a 0.9965 waveform correlation against the bf16 original — closer than the same build made from safetensors (0.989), since it quantises from the same bf16 values.
  • Re-converting kroko-en-community-64-l-q8_0.gguf with no --root: now model_spec_family=kroko_asr, where before it either failed on sidecars or silently wrote sense_asr.
  • Regression: the nine-input safetensors invocation from docs/models/ace_step.md still resolves to ace_step with the same namespaces, exclusions and sidecar flags.
  • --root still overrides the embedded sidecars (verified by pointing it at a directory that lacks them and watching validation fail on the missing ones).

Happy to add a converter test if you want one — say where it should live.

🤖 Generated with Claude Code

CaptainArni and others added 2 commits September 5, 2026 21:25
Re-quantising a GGUF that audiocpp_gguf produced fails today, and the error
points at the wrong thing (0xShug0#457):

    $ audiocpp_gguf --input ACE-Step1.5-XL-Turbo-bf16.gguf --type q8_0 --output test.gguf
    error: no GGUF sidecars were found ...

Three things stand in the way. The conversion's namespaces come from the
`namespace=` labels on the command line, so a GGUF whose tensors are already
named `dit_xl_turbo_weights/...` presents as one unnamed namespace and matches
nothing in its own family. With no `--family`, the catalog search then settles
on whichever unrelated spec accepts a single unnamed namespace -- `sense_asr` --
and the run dies later, in sidecar embedding. When it does not die it is worse:
re-converting the Kroko GGUF with any small text file beside it succeeds and
writes `model_spec_family=sense_asr` into the output. And the sidecars the input
already carries go unused, because collection only walks `--root`.

So: read the namespaces back out of a GGUF input's tensor names, register its
embedded model spec as the top-priority candidate and let it set the default
family, and fall back to its embedded sidecars when neither `--root` nor
`--sidecar` was given. A re-conversion also stops requiring every namespace the
spec declares -- a package built with `--exclude-prefix` (an ACE-Step XL GGUF
carries no turbo or base DiT) ships fewer than the spec lists, and the runtime
loads it happily.

docs/models/ace_step.md said building an XL GGUF "needs the other variants'
safetensors on hand". It does not: the namespace check only reads the labels and
`--exclude-prefix` drops those tensors before any data is touched, so a 76-byte
placeholder works and the build needs 24 GB of downloads rather than 33 GB. The
section now shows that, notes that only the two config.json files are genuinely
required, and documents quantising the DiT alone -- q8_0's "planner sampling can
fail" grade is about the planner LM, and keeping it at bf16 holds a 0.989
waveform correlation against the bf16 build at a fixed seed where a fully
quantised build scores 0.09.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AYQxP9KPBeGmGRRhptbQWp
`ace_step_xl_turbo_q8dit` and `ace_step_xl_sft_q8dit` install the q8_0-DiT
builds next to the bf16 ones, from the same repo the bf16 rows already point at.
9.97 GiB against 14.2 GiB, and at a fixed seed the output holds a 0.989 waveform
correlation with the bf16 build (0.997 on a sung take, 0.999 for XL SFT) where a
fully quantised build scores 0.09.

`precision` is the validated enum, so these rows carry `q8_0` — the type the
conversion was run at. What the planner LM, text encoder and VAE keep is in the
id and display name instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AYQxP9KPBeGmGRRhptbQWp
@CaptainArni

Copy link
Copy Markdown
Contributor Author

Pushed one more commit: ace_step_xl_turbo_q8dit and ace_step_xl_sft_q8dit package rows.

I had left these out on the grounds that they point at CaptainArni/audio.cpp-gguf and would reopen the hosting question from #235 — but the two bf16 rows already carry that same download override on main, so the rows are symmetric with what is there rather than a new claim. Happy to move all four to audio-cpp/audio.cpp-gguf in one go whenever you want that; it drops the overrides entirely.

precision is a validated enum, so the rows carry q8_0 (the type the conversion ran at) and the id and display name say what stays at bf16. model_manager_v2.py sizes resolves both against the live repo: 10,710,565,056 and 10,710,562,816 bytes, state: ok.

@0xShug0
0xShug0 merged commit 49b0322 into 0xShug0:main Sep 5, 2026
6 checks passed
@0xShug0

0xShug0 commented Sep 5, 2026

Copy link
Copy Markdown
Owner

@CaptainArni PR merged! Thank you again for addressing the issue so quickly!

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