Skip to content

CPU: Qwen3.5 / Qwen3.8 hybrid GGUFs are refused entirely — no fallback exists #3091

Description

@alfredodeza

Summary

Unlike most GPU-ineligible architectures (e.g. Phi's LayerNorm gap, #3075), which fall
back to CPU inference with a warning, Qwen3.5/Qwen3.8-architecture GGUFs (hybrid Gated
DeltaNet / SSM layers, general.architecture = qwen35) are refused completely
there is no CPU inference path at all today. Confirmed with two different downloads:

apr chat ~/Downloads/Qwen3.8-27B-UD-IQ2_XXS.gguf
apr chat ~/Downloads/Qwen3.5-4B-UD-Q4_K_XL.gguf

Both fail identically, even without --gpu:

[GGUF model parse failed: Format error: Architecture 'qwen35' uses SSM/Gated Delta Net
layers which are not yet supported for inference. Use a standard transformer model
(e.g., Qwen2.5, LLaMA, Mistral) or wait for SSM support in a future release., will use CPU]
You: hey
Assistant: [Error: Failed to create GGUF model: Format error: Architecture 'qwen35' ...]

The chat loop even prints "will use CPU" in the CUDA-init warning line, but then the
CPU load of the same GGUF fails with the exact same error — because the refusal is in
the shared GGUF transformer constructor (QuantizedGGUFTransformer::from_gguf), which
both the CPU and GPU code paths call. There is no degraded-but-working CPU mode; the
model simply cannot be loaded by this binary in any form.

Root cause

crates/aprender-serve/src/gguf/transformer.rs::unsupported_architecture_reason is
called unconditionally inside QuantizedGGUFTransformer::from_gguf (GH-704), before
backend selection, and returns Err(FormatError) whenever any tensor name contains
ssm_/ssm.. This is upstream of the CPU/GPU fork — it blocks both.

Ask

  1. Implement a scalar/SIMD CPU Gated DeltaNet (linear attention) forward pass for
    GGUF-loaded hybrid layers — recurrent state + short causal conv1d + gated delta rule,
    per Qwen3.5/Qwen3-Next's architecture — analogous to what
    crates/aprender-serve/src/gpu/scheduler/linear_attn.rs already sketches for the GPU
    scheduler (SafeTensors path), but for the GGUF QuantizedGGUFTransformer CPU path.
  2. Until CPU inference exists, consider narrowing the current blanket refusal message so
    it doesn't imply CPU already works ("will use CPU" in the CLI's CUDA-fallback log line
    is actively misleading for this architecture — see below) — or explicitly state in
    the error that neither backend supports it yet, to save users a round trip.
  3. Once a real CPU path lands, extend apr qa/apr validate to report this
    architecture's status accurately (load-time capability, not just a runtime crash).

Secondary bug: misleading CLI message

The chat CLI's own log line claims a fallback that doesn't exist:

[GGUF model parse failed: ... will use CPU]

printed by crates/apr-cli/src/commands/chat_load_tokenizers.rs:166
(format!("[GGUF CUDA init failed: {}, will use CPU]", e)). That code path assumes any
GGUF parse failure on the GPU attempt is recoverable by retrying on CPU — true for the
Phi LayerNorm case (#3075), false here, since the same parse failure happens on the
CPU retry too. Worth a small fix regardless of when the real SSM CPU path lands: don't
print "will use CPU" when the failure reason is architecture-unsupported rather than
GPU-capability-specific, since the retry is guaranteed to fail with the identical message.

Evidence

  • crates/aprender-serve/src/gguf/transformer.rs:73-86,158-167 (refusal call site,
    upstream of backend selection)
  • crates/apr-cli/src/commands/chat_load_tokenizers.rs:166 (misleading "will use CPU" log line)
  • Repro commands above, no --gpu flag, same host

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High prioritybugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions