FoolDev Claude Opus 4.7 commited on
Commit
a4d3b6e
Β·
1 Parent(s): c1c4dfd

docs: ground "qwen35 is canonical" in upstream code-level evidence

Browse files

The README "Architecture" section previously asserted that qwen35 is
the canonical load-time label for Qwen 3.6 without showing the work.
Audit of the actual upstream stack found three converging pieces of
evidence β€” now listed inline:

- Qwen's own HF configs for Qwen/Qwen3.6-27B and
Qwen/Qwen3.6-35B-A3B declare model_type "qwen3_5" /
"qwen3_5_moe" and the Qwen3_5*ForConditionalGeneration arch
classes. No Qwen3_6 class exists in transformers.
- llama.cpp's convert_hf_to_gguf.py maps those classes to
MODEL_ARCH.QWEN35 / QWEN35MOE, so unsloth's GGUFs are
natively qwen35-stamped out of the converter.
- src/models/qwen35.cpp has `case 64: LLM_TYPE_27B` (this model)
and qwen35moe.cpp has `case 40: LLM_TYPE_35B_A3B` (the
Janus-35B sibling base). The arch entries were written to
load Qwen 3.6 weights, not just Qwen 3.5.

Conclusion: qwen35 isn't a stand-in awaiting qwen36 support; it's
already the upstream-canonical name for the whole Qwen 3.5 / 3.6
hybrid SSM + attention family. The "qwen36" name was Foolish-dev's
invention, not an upstream concept.

History paragraph softened from "qwen36 was the architecturally-
honest label" to "version-specific label, on the theory that qwen35
was a loader stand-in" β€” a theory the audit shows was mistaken.

The rename_arch.py example flipped to qwen36 -> qwen35 (the
direction that actually serves a real user need: legacy v0.6.0-era
recovery). CHANGELOG [Unreleased] picks up a "Changed (upstream-
evidence audit)" bullet with the per-source breakdown.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

Files changed (2) hide show
  1. CHANGELOG.md +39 -0
  2. README.md +42 -19
CHANGELOG.md CHANGED
@@ -61,6 +61,45 @@ and documentation**, not the underlying base model.
61
  (skips early); load-bundle does a no-op rebadge on a qwen35 bundle
62
  and runs `ollama create` as normal.
63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  ### Changed (round-trip residue cleanup)
65
  - Five doc surfaces still carried the v0.6.0-era "bundle is
66
  qwen36-stamped, fails today, rebadge to qwen35 first" narrative
 
61
  (skips early); load-bundle does a no-op rebadge on a qwen35 bundle
62
  and runs `ollama create` as normal.
63
 
64
+ ### Changed (upstream-evidence audit β€” qwen35 is canonical, not a workaround)
65
+ - Read the actual upstream code paths for Qwen 3.6 loading and
66
+ found that `qwen35` isn't a stand-in awaiting `qwen36` support;
67
+ it's the upstream-canonical name for the whole Qwen 3.5 / 3.6
68
+ hybrid SSM + attention family. Three converging pieces of
69
+ evidence, now folded into the README "Architecture" section:
70
+ - **Qwen's own HF configs** for both `Qwen/Qwen3.6-27B` and
71
+ `Qwen/Qwen3.6-35B-A3B` declare `model_type: "qwen3_5"` /
72
+ `"qwen3_5_moe"` and the `Qwen3_5*ForConditionalGeneration`
73
+ arch classes. No `Qwen3_6` arch class exists in `transformers`
74
+ β€” Qwen reuses the 3.5 class names for 3.6 weights.
75
+ - **`convert_hf_to_gguf.py`** in `ggml-org/llama.cpp` registers
76
+ `Qwen3_5ForCausalLM` β†’ `MODEL_ARCH.QWEN35` and
77
+ `Qwen3_5MoeForCausalLM` β†’ `MODEL_ARCH.QWEN35MOE`, so the
78
+ unsloth GGUFs this repo pulls from are natively qwen35-stamped
79
+ out of the converter.
80
+ - **`src/models/qwen35.cpp`** has an explicit
81
+ `case 64: type = LLM_TYPE_27B` branch (this model);
82
+ **`qwen35moe.cpp`** has `case 40: type = LLM_TYPE_35B_A3B`
83
+ (the Janus-35B sibling base). The arch entries were written
84
+ to load Qwen 3.6 weights, not just Qwen 3.5; this is
85
+ code-level confirmation, not interpretation.
86
+ - Reframed the README "Architecture" section accordingly: the
87
+ three pieces of evidence are listed inline; the "qwen35 is the
88
+ canonical load-time label" claim is now grounded rather than
89
+ asserted; the History paragraph softens the v0.6.0 stamp choice
90
+ from "architecturally-honest" to "version-specific on the
91
+ theory that `qwen35` was a loader stand-in awaiting `qwen36`
92
+ support" β€” a theory the audit shows was mistaken.
93
+ - Flipped the `scripts/rename_arch.py` example back to qwen36 β†’
94
+ qwen35 (the direction that actually serves the only realistic
95
+ use case: legacy v0.6.0-era recovery). Previous direction
96
+ (qwen35 β†’ qwen36) made less sense given the audit conclusion.
97
+ - Tooling unchanged. `scripts/rename_arch.py`, `make heal-hf`,
98
+ `make load-bundle` still exist and still work; they're just
99
+ documented honestly as legacy recovery + generic flip utility
100
+ rather than as workarounds awaiting upstream changes that will
101
+ never come.
102
+
103
  ### Changed (round-trip residue cleanup)
104
  - Five doc surfaces still carried the v0.6.0-era "bundle is
105
  qwen36-stamped, fails today, rebadge to qwen35 first" narrative
README.md CHANGED
@@ -174,37 +174,60 @@ If you want the safetensors for `transformers`, fetch them from [`Qwen/Qwen3.6-2
174
  current loader compatibility.
175
  - Multi-token prediction (MTP) head trained for speculative decoding
176
 
177
- **The bundled GGUF declares `general.architecture: 'qwen35'`** β€” the
178
- arch entry every released llama.cpp / Ollama loads the Qwen 3.6
179
- hybrid SSM + attention family under. Upstream `ggml-org/llama.cpp`
180
- and `ollama/ollama` register the stack as `qwen35` / `qwen35moe`,
181
- including for actual Qwen 3.6-family weights (the upstream
182
- `Qwen3.6-35B-A3B_Q8_0` GGUFs load under `qwen35moe`); no `qwen36`
183
- arch entry exists, and there's no open PR or tracking issue for
184
- one β€” `qwen35` is the canonical load-time label.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
 
186
  `ollama run hf.co/FoolDev/Thanatos-27B` and `llama-server -m
187
  Thanatos-27B.Q4_K_M.gguf` both load directly on current stock
188
  loaders.
189
 
190
- **History.** Through v0.6.0 the bundle was stamped `qwen36` β€” the
191
- architecturally-honest label, but unloadable on stock loaders.
192
- Users needed `make load-bundle` or `make heal-hf` to rebadge
193
- before loading. The stamp was reverted to `qwen35` in commit
194
- `964e418` to stop forcing the workaround on every fresh pull;
195
- tensor data was byte-identical across both stamps, only the
 
 
 
 
196
  `general.architecture` KV (and namespaced KV keys) flipped. See
197
  the [CHANGELOG](CHANGELOG.md) entry for the full rationale.
198
 
199
  `scripts/rename_arch.py` is kept in the repo as a generic GGUF
200
- arch renamer (metadata only, tensors byte-identical) for future
201
- flips:
202
 
203
  ```bash
204
  python3 scripts/rename_arch.py \
205
- --from-arch qwen35 --to-arch qwen36 \
206
- Thanatos-27B.Q4_K_M.gguf \
207
- Thanatos-27B.Q4_K_M.qwen36.gguf
208
  ```
209
 
210
  If you pulled the bundle while it was still qwen36-stamped (v0.6.0
 
174
  current loader compatibility.
175
  - Multi-token prediction (MTP) head trained for speculative decoding
176
 
177
+ **The bundled GGUF declares `general.architecture: 'qwen35'`** β€” not a
178
+ workaround for an unimplemented `qwen36` arch, but the canonical
179
+ upstream label for the entire Qwen 3.5 / 3.6 hybrid SSM + attention
180
+ family. The naming convergence runs through three layers of the
181
+ stack:
182
+
183
+ - **Qwen's own HF configs.** `Qwen/Qwen3.6-27B/config.json` declares
184
+ `"model_type": "qwen3_5"` and
185
+ `"architectures": ["Qwen3_5ForConditionalGeneration"]`. The MoE
186
+ sibling `Qwen/Qwen3.6-35B-A3B` declares `"qwen3_5_moe"` /
187
+ `Qwen3_5MoeForConditionalGeneration`. No `Qwen3_6` arch class
188
+ exists in `transformers`; Qwen reuses the 3.5 class names.
189
+ - **llama.cpp's converter.** `convert_hf_to_gguf.py` registers
190
+ `Qwen3_5ForCausalLM` β†’ `MODEL_ARCH.QWEN35` and
191
+ `Qwen3_5MoeForCausalLM` β†’ `MODEL_ARCH.QWEN35MOE`. The unsloth
192
+ GGUFs this repo pulls from (`unsloth/Qwen3.6-27B-GGUF`,
193
+ `unsloth/Qwen3.6-35B-A3B-GGUF`) inherit those stamps.
194
+ - **llama.cpp's model code.** `src/models/qwen35.cpp` has an
195
+ explicit `case 64: type = LLM_TYPE_27B` branch for this model;
196
+ `qwen35moe.cpp` has `case 40: type = LLM_TYPE_35B_A3B` for the
197
+ Janus-35B sibling base. The arch entries were written to load
198
+ Qwen 3.6 weights, not just Qwen 3.5.
199
+
200
+ There is no PR or tracking issue for a `qwen36` arch entry in
201
+ `ggml-org/llama.cpp` or `ollama/ollama` because none is needed β€”
202
+ `qwen35` already loads the model the upstream code path was
203
+ designed to load.
204
 
205
  `ollama run hf.co/FoolDev/Thanatos-27B` and `llama-server -m
206
  Thanatos-27B.Q4_K_M.gguf` both load directly on current stock
207
  loaders.
208
 
209
+ **History.** Through v0.6.0 the bundle was stamped `qwen36` as a
210
+ version-specific label, on the theory that `qwen35` was a
211
+ loader-side stand-in awaiting proper Qwen 3.6 support. Stock
212
+ loaders rejected it; users needed `make load-bundle` or
213
+ `make heal-hf` to rebadge before loading. The stamp was reverted
214
+ to `qwen35` in commit `964e418` once daily friction outweighed
215
+ the version-specificity. The upstream-evidence audit captured
216
+ above (post-`72259c1`) then confirmed `qwen35` is in fact the
217
+ canonical family label β€” not a workaround. Tensor data was
218
+ byte-identical across both stamps; only the
219
  `general.architecture` KV (and namespaced KV keys) flipped. See
220
  the [CHANGELOG](CHANGELOG.md) entry for the full rationale.
221
 
222
  `scripts/rename_arch.py` is kept in the repo as a generic GGUF
223
+ arch renamer (metadata only, tensors byte-identical), useful for
224
+ the legacy qwen36 β†’ qwen35 rebadge or any future arch flip:
225
 
226
  ```bash
227
  python3 scripts/rename_arch.py \
228
+ --from-arch qwen36 --to-arch qwen35 \
229
+ Thanatos-27B.Q4_K_M.qwen36.gguf \
230
+ Thanatos-27B.Q4_K_M.gguf
231
  ```
232
 
233
  If you pulled the bundle while it was still qwen36-stamped (v0.6.0