6.5 KiB
Audio8 TTS 0.1B (Falcon-H1) — Port Status
Status 2026-09-01 (resolved): The Falcon-H1 slow-AR path is a stateful native implementation (Mamba2 + hybrid GQA attention) and now matches the transformers reference: first-frame semantic argmax = 2732, per-step argmax parity over the whole prompt, ASR round-trip of synthesized "你好" returns "你好。", and long generation (~600 positions) is numerically stable. The 0.6B Qwen path is unaffected and fully functional.
What has been done
Branch feat/audio8-tts-falcon-h1-mamba2.
src/community_models/audio8_tts/ar.cppFalconH1StepState+init_falcon_step_state: per-layer conv/SSM states and attention KV cache.falcon_forward_step: stateful single-token forward —RMSNorm -> (Mamba2 || GQA attention) -> residual -> gated FFN -> RMSNorm -> semantic_output, matchingtransformers.models.falcon_h1.build_falcon_embedding_step:(text_emb + codebook_sum) * embedding_multiplier(multiplier applies to the whole sum).generate()falcon branch: token-by-token prefill + generation.
src/community_models/audio8_tts/falcon_kv_cache.happend_falcon_kv_token: host KV-cache append with per-head re-stride (see "Root causes" below). Covered byaudio8_tts_falcon_kv_cache_test.
external/ggml/src/ggml-metal/ggml-metal.metal- Fixed
kernel_ssm_scan_f32reduction: the oldsimd_sum(shared_sums[sgitg*NW + tiisg])read garbage columns whensgptg < NW(happens ford_state=64withn_t=1). Replaced with an explicit loop summingshared_sums[(i2+sgitg)*NW + g]overg < sgptg.
- Fixed
Resolved Issue 1 — logits argmax mismatch vs transformers
Symptom (before): first generated semantic code was wrong (argmax 3620 instead of 2732; ASR round-trip said "三星" instead of "你好").
Root causes (three, all fixed):
- conv1d kernel flip was wrong.
ggml_ssm_convcomputesy[c] = sum_k w[k,c]*window[k,c]withwindow[0]the OLDEST frame — the same orientation as HF (nn.Conv1dprefill and the cachedtorch.sum(conv_states * w, dim=-1)decode are both cross-correlation). The GGUF tensor[d_conv,1,conv_dim]is the HF[conv_dim,1,d_conv]weight with unchanged flat bytes, i.e. already in the layout ssm_conv wants. An earlier "fix" that flipped the kernel taps corrupted the x/B/C split every step. Fix: feed the kernel unflipped (load_falcon_layer,conv1d_kernel). - Unprotected host read-back of intermediate tensors.
sx(conv window) andk_r/v(fresh K/V) are graph intermediates whose buffers gallocr reuses; reading them back withoutggml_set_outputreturned garbage and corrupted conv state / KV cache every step. Fix:ggml_set_outputon exactly those three tensors per layer (pinning ~300 tensors corrupts the whole graph — pin only what is read back). - KV cache head-stride bug (the decisive one). The host cache used the
current sequence length as the per-head stride while appending only the
new token: at step 1 the new head-0 token was written over token 0's
head-1 block, so every head past the first read corrupted context from
the second token on (head 0 was always correct, which masked the bug).
Fix:
append_falcon_kv_tokenre-lays existing entries into the new stride before appending. Regression test:tests/unittests/test_audio8_tts_falcon_kv_cache.cpp(fails with the old algorithm at the second append, passes after).
Verification: bf16 GGUF vs f32 HF reference (transformers==4.57.6,
recurrent path forced for every token): per-layer conv/SSM/K/V states match
within bf16 rounding over the full 23-token prompt; argmax matches at every
prompt position except one knife-edge tie (ref top-2 margin 0.03 vs bf16
logit noise 0.19). First frame: argmax 2732 (logit 26.524 vs ref 26.557).
End-to-end: synthesized "你好" transcribes back as "你好。" (Qwen3-ASR), on
both CPU and Metal backends.
Resolved Issue 2 — recurrent SSM state blow-up on long sequences
Symptom (before): ~180 tokens in, per-layer states reached 1e15..1e18, then logits went to zero / NaN.
Root cause: not the recurrent scan itself — the corrupted KV cache
(Issue 1, cause 3) fed garbage attention output into the residual stream,
which drove x/dt of the Mamba2 branch into regime where the state
exploded. The f32 HF reference running the same recurrent math stays bounded
(states ~270 over the prompt), which ruled out the "inherent weak-decay"
theory previously recorded here.
Verification: 140-character text → 525 generated frames (position 605):
max per-layer SSM state ≈ 1.1e3, zero NaN, clean EOS, and the audio
transcribes back to the input text verbatim. No chunked-scan or dt-clamp
mitigation was needed; HF's recurrent fallback does not apply the
time_step_min/max clamp either (time_step_limit is hardcoded
(0.0, inf) in modeling_falcon_h1.py).
Notes for the next agent
- The parity harness used for the fix (an HF golden-dump script forcing the
recurrent path token-by-token, plus a differ) was session tooling and is not
committed. To rebuild it: run
modeling_arkttsundertransformers==4.57.6with eachlayer.mamba.forwardreplaced by theuse_precomputed_statesrecurrent branch, dumpcache.conv_states/ssm_states/key_cacheper step, and diff againststate.ssm_states/conv_states/k_cache/v_cacheread back infalcon_forward_step(same flat layouts: ssm[s + 64d + 2048h], conv rows oldest→newest, kvd + 64*(t + T*h)). A_log/D/dt_bias/conv1d.weightmust load withassets::TensorStorageType::F32(GGUF stores them quantized;Nativekeeps the quantized type andggml_backend_tensor_getthen reads out of bounds).- The GGUF layout for
conv1d.weightis[d_conv, 1, conv_dim], which is the HF[conv_dim, 1, d_conv]weight with unchanged flat bytes. Feed it toggml_ssm_convunflipped (see Resolved Issue 1, cause 1). ggml_set_outputon a graph intermediate pins its buffer so host read-back is safe — but mass-pinning hundreds of tensors corrupts the whole graph (all-zero logits). Pin only the tensors actually read back.- Reference environment:
uv venv+uv pip install torch "transformers>=4.57,<5";transformers>=5renamesFalconHybridMambaAttentionDynamicCacheand breaks the 0.1B remote code. - Known remaining gap: the fast-AR codebooks during generation mean the C++ rollout cannot be compared token-by-token against a reference that feeds zero codebook rows; parity was established over the prompt + first frame.