kingjones777 commited on
Commit
68439b5
·
verified ·
1 Parent(s): f3baa32

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ North-Mini-Code-1.0-Q4_0_ROCMFP4_STRIX.gguf filter=lfs diff=lfs merge=lfs -text
North-Mini-Code-1.0-Q4_0_ROCMFP4_STRIX.gguf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2f3b01320aba3de875c2370cf041f53d5915c54885aff09ef8764bb13ea88264
3
+ size 16407617344
README.md ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ base_model: CohereLabs/North-Mini-Code-1.0
4
+ base_model_relation: quantized
5
+ pipeline_tag: text-generation
6
+ library_name: llama.cpp
7
+ tags:
8
+ - gguf
9
+ - rocmfp4
10
+ - rocmfpx
11
+ - strix-halo
12
+ - gfx1151
13
+ - amd
14
+ - rocm
15
+ - moe
16
+ - cohere2moe
17
+ - code
18
+ ---
19
+
20
+ # North-Mini-Code-1.0 — ROCmFP4 STRIX (GGUF)
21
+
22
+ This is a `Q4_0_ROCMFP4_STRIX` quant of [CohereLabs/North-Mini-Code-1.0](https://huggingface.co/CohereLabs/North-Mini-Code-1.0), built and tested on an AMD Ryzen AI Max+ 395 (Strix Halo, gfx1151, 128 GB unified memory) running ROCm 7.2.4.
23
+
24
+ North-Mini-Code is the agentic coder I actually reach for on my AMD boxes, and ROCmFP4 is the format that pays for itself on Strix Halo. There was no ROCmFP4 build of it, and it turned out there *couldn't* be one — so I made the piece that was missing first.
25
+
26
+ **Short version: 17.93 GB of Q4_K_XL becomes 15.28 GB and decodes 26.6% faster, with the same answers. But you need the patch in this repo to load it at all.**
27
+
28
+ ## Read this before you download
29
+
30
+ The two things this file needs live in two different codebases, and neither one has both:
31
+
32
+ - **Mainline llama.cpp** implements the `cohere2moe` architecture. It does not implement ROCmFP4.
33
+ - **The [ROCmFPX fork](https://github.com/charlie12345/ROCmFPX)** implements ROCmFP4 (GGUF tensor types 100/101). It does not implement `cohere2moe`.
34
+
35
+ So no build in existence can load this GGUF. I ported `cohere2moe` from mainline into ROCmFPX, and **`cohere2moe-rocmfpx.patch` in this repo is that port**. Apply it, build, and the file works. Without it you will get an unknown-architecture error, and that is expected, not a corrupt download.
36
+
37
+ ```bash
38
+ git clone https://github.com/charlie12345/ROCmFPX && cd ROCmFPX
39
+ git apply /path/to/cohere2moe-rocmfpx.patch
40
+ HIPCXX=$(hipconfig -l)/clang HIP_PATH=$(hipconfig -R) cmake -B build -DGGML_HIP=ON \
41
+ -DGPU_TARGETS=gfx1151 -DGGML_HIP_ROCWMMA_FATTN=ON -DGGML_HIP_NO_VMM=ON \
42
+ -DGGML_HIP_MMQ_MFMA=ON -DCMAKE_BUILD_TYPE=Release -DGGML_VULKAN=OFF -DLLAMA_BUILD_WEBUI=OFF
43
+ cmake --build build -j 4
44
+ ```
45
+
46
+ Check it took: `strings build/bin/libllama.so | grep -cx cohere2moe` should print `1`. Check `libllama.so`, not `llama-server` — the server binary prints 0 even on a build that runs the model fine, which cost me an hour of thinking the port had failed.
47
+
48
+ I have not upstreamed this to ROCmFPX. If Charlie wants it, it's here.
49
+
50
+ ## What's in the patch
51
+
52
+ 12 files, 17 hunks. A new `src/models/cohere2moe.cpp` graph plus the arch enum, tensor mappings, hparams/tensor loading, the converter class, and the gguf-py constants.
53
+
54
+ The graph is mainline's, adapted where the fork's API differs — `hparams.n_layer` instead of `n_layer()`, `nextn_predict_layers` instead of `n_layer_nextn`, and the sliding-window pattern read as a bool array because the fork has no `is_swa_impl`. None of the arithmetic changed.
55
+
56
+ ⚠️ **MTP is not ported.** The fork lacks the `t_h_nextn` result slots the mainline MTP path needs. North-Mini-Code's GGUFs carry no nextn layers so nothing is lost today, but if Cohere ships an MTP variant this patch won't load it.
57
+
58
+ ## The bias thing, since it bit everyone else
59
+
60
+ North-Mini-Code ships **18,532 per-expert FC bias keys** in its HF weights. On vLLM that's a genuine three-layer problem — the model class, the NVFP4 MoE weight registration, and the fused kernel all assume no bias, and you get `'RoutedExperts' object has no attribute 'w2_bias'`.
61
+
62
+ On the GGUF path it's a non-issue, and it's worth knowing why rather than being surprised: **those biases are all zero**, and the converter drops zero biases. I parsed both files to confirm — 442 tensors each, **0 bias tensors**, in the Q4_K_XL I was comparing against and in my own output. Passing null biases is correct here, not a shortcut.
63
+
64
+ I still wired every bias tensor as `TENSOR_NOT_REQUIRED` anyway — expert gate/up/down, the router, attention output, and the dense FFN — so a future checkpoint with non-zero biases binds without recompiling. The converter keeps mainline's policy: skip if zero, raise if not.
65
+
66
+ ## Files
67
+
68
+ | File | Size |
69
+ |---|---|
70
+ | `North-Mini-Code-1.0-Q4_0_ROCMFP4_STRIX.gguf` | 15.281 GiB (16,407,617,344 bytes) |
71
+ | `cohere2moe-rocmfpx.patch` | 36 KB — required, see above |
72
+ | `SHA256SUMS` | |
73
+
74
+ Single file, 442 tensors, 53 metadata keys.
75
+
76
+ ## Running it
77
+
78
+ ```bash
79
+ env LD_LIBRARY_PATH=/path/to/ROCmFPX/build/bin:/opt/rocm/lib \
80
+ HSA_OVERRIDE_GFX_VERSION=11.5.1 GGML_HIP_ENABLE_UNIFIED_MEMORY=1 \
81
+ ./llama-server --host 127.0.0.1 --port 8080 \
82
+ -ngl 999 -fa on -dio --jinja --metrics \
83
+ --model North-Mini-Code-1.0-Q4_0_ROCMFP4_STRIX.gguf \
84
+ --ctx-size 65536 --parallel 1 --temp 0.3 --top-p 0.95
85
+ ```
86
+
87
+ Three things that will save you time:
88
+
89
+ **Use `--temp 0.3`, not greedy.** This is the one that surprised me. Greedy decoding on this model produces code whose modal output does not run — I get `IndentationError` most of the time at temp 0. At 0.3 it's clean. I originally "improved" my own config to temp 0 and had to walk it back.
90
+
91
+ **Don't send `reasoning_effort: none`.** It's the documented thinking-off lever for North and it works on vLLM, but on llama.cpp there's no `cohere_command4` reasoning parser, so the flag moves your answer out of `content` and into `reasoning_content` still wrapped in `<|START_TEXT|>…<|END_TEXT|>`. Just leave it off — `content` comes back clean and the chain of thought lands in `reasoning_content` where you'd want it.
92
+
93
+ **`-dio` matters** on the ROCmFP4 runtime. The mmap path can sit there looking hung on large models.
94
+
95
+ ## Benchmarks
96
+
97
+ One Ryzen AI Max+ 395, same binary, same flags for both, ROCm backend, `--parallel 1`, speculative decoding off, temp 0.3, nonce-prefixed prompts so nothing came from the prefix cache. Warmed before measuring. **7 runs each**, and I'm giving you the full range rather than just a median, because a median alone can't tell you whether a difference is real.
98
+
99
+ | | Size | Median tok/s | Min | Max |
100
+ |---|---|---|---|---|
101
+ | `UD-Q4_K_XL` (unsloth) | 17.931 GiB | 54.23 | 54.20 | 54.50 |
102
+ | **This build** | **15.281 GiB** | **68.65** | 68.51 | 69.74 |
103
+
104
+ **+26.6%, and the two ranges don't overlap** — 54.50 max against 68.51 min. That's the bar I hold myself to; if the ranges had touched I'd be telling you it was inconclusive.
105
+
106
+ I ran it a second time on a shorter prompt (128 max tokens instead of 256) and got 56.94 → 73.40, which is +28.9% and also disjoint. Different absolute numbers, same answer.
107
+
108
+ **Quality held.** Both builds were put through the same problems with the generated Python **executed against assertions** — reverse a string, primality, flatten-one-level, binary search, and an arithmetic check. Both 5/5, plus a merge-two-sorted-lists problem that also executed correctly on both. That is a small battery and I'm not going to dress it up as a benchmark suite; it's enough to say this quant didn't break the model, and not enough to claim a quality ranking.
109
+
110
+ **No BF16 A/B.** I did not benchmark the full-precision weights on this box, so I'm not quoting a quality delta against them.
111
+
112
+ **Measured BPW is 4.30**, not the ~4.49 the quantizer's help text advertises for this recipe. I'm quoting what the log said, not the menu.
113
+
114
+ ## How it was made
115
+
116
+ Quantized from the unsloth BF16 shards (split-aware, no merge needed), not requantized from a lower-bit source:
117
+
118
+ ```bash
119
+ ./llama-quantize North-Mini-Code-1.0-BF16-00001-of-00002.gguf \
120
+ North-Mini-Code-1.0-Q4_0_ROCMFP4_STRIX.gguf Q4_0_ROCMFP4_STRIX 8
121
+ ```
122
+
123
+ 58,168 MiB in, 15,638 MiB out, about six minutes.
124
+
125
+ I used `Q4_0_ROCMFP4_STRIX` (type 105) and not `Q4_0_ROCMFP4_STRIX_LEAN` (106) deliberately. LEAN saves about half a gig by dropping token embeddings to Q5_K, and on a coder I ran this week that cost two executed-code problems out of thirty for no speed gain at all — the two recipes measured identically on throughput. If you're quantizing something correctness-sensitive for Strix Halo, spend the half gig.
126
+
127
+ ## The model
128
+
129
+ North-Mini-Code-1.0 is Cohere Labs' agentic coding model — 30B total, ~3B active, 128 experts, `cohere2moe`. Everything about behaviour, prompt format, and capability comes from upstream; see their [model card](https://huggingface.co/CohereLabs/North-Mini-Code-1.0). All I changed is the numeric format.
130
+
131
+ Worth noting for anyone sizing this: ~3B active is a *small* active-parameter count, and the received wisdom around ROCmFP4 has been that it pays off on dense attention and big-active MoE but not on small-active MoE. That's now two small-active MoE models in a row where I've measured a solid double-digit decode win, so I no longer believe that rule.
132
+
133
+ ## License
134
+
135
+ Apache 2.0, inherited from the base model. The patch is a derivative of llama.cpp and ROCmFPX and carries their MIT license.
136
+
137
+ ## Credit
138
+
139
+ The model is Cohere Labs'. ROCmFP4 and the `Q4_0_ROCMFP4_STRIX` recipe are [charlie12345/ROCmFPX](https://github.com/charlie12345/ROCmFPX). The `cohere2moe` implementation is [ggml-org/llama.cpp](https://github.com/ggml-org/llama.cpp) — I ported it across, I didn't invent it. I built the quant, ran the benchmarks, and wrote down what I found.
SHA256SUMS ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ 2f3b01320aba3de875c2370cf041f53d5915c54885aff09ef8764bb13ea88264 North-Mini-Code-1.0-Q4_0_ROCMFP4_STRIX.gguf
2
+ 87fbf2e0ffe8c985bd95295f615e78a01286df6d8854f3b6090bd907c0e1f791 cohere2moe-rocmfpx.patch
3
+ e51d193a651c6fd9438c848135b30d852c399f49088d93d7328eb3d65e702bcd README.md
cohere2moe-rocmfpx.patch ADDED
@@ -0,0 +1,760 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/src/llama-arch.h
2
+ +++ b/src/llama-arch.h
3
+ @@ -72,6 +72,7 @@
4
+ LLM_ARCH_XVERSE,
5
+ LLM_ARCH_COMMAND_R,
6
+ LLM_ARCH_COHERE2,
7
+ + LLM_ARCH_COHERE2MOE,
8
+ LLM_ARCH_DBRX,
9
+ LLM_ARCH_OLMO,
10
+ LLM_ARCH_OLMO2,
11
+ --- a/src/llama-arch.cpp
12
+ +++ b/src/llama-arch.cpp
13
+ @@ -67,6 +67,7 @@
14
+ { LLM_ARCH_XVERSE, "xverse" },
15
+ { LLM_ARCH_COMMAND_R, "command-r" },
16
+ { LLM_ARCH_COHERE2, "cohere2" },
17
+ + { LLM_ARCH_COHERE2MOE, "cohere2moe" },
18
+ { LLM_ARCH_DBRX, "dbrx" },
19
+ { LLM_ARCH_OLMO, "olmo" },
20
+ { LLM_ARCH_OLMO2, "olmo2" },
21
+ --- a/src/llama-model.cpp
22
+ +++ b/src/llama-model.cpp
23
+ @@ -155,6 +155,8 @@
24
+ return new llama_model_command_r(params);
25
+ case LLM_ARCH_COHERE2:
26
+ return new llama_model_cohere2(params);
27
+ + case LLM_ARCH_COHERE2MOE:
28
+ + return new llama_model_cohere2moe(params);
29
+ case LLM_ARCH_DBRX:
30
+ return new llama_model_dbrx(params);
31
+ case LLM_ARCH_OLMO:
32
+ @@ -1788,7 +1790,7 @@
33
+ LLAMA_LOG_INFO("%s: n_ff_shexp = %d\n", __func__, hparams.n_ff_shexp);
34
+ }
35
+
36
+ - if (arch == LLM_ARCH_QWEN3MOE || arch == LLM_ARCH_OPENAI_MOE || arch == LLM_ARCH_QWEN3VLMOE || arch == LLM_ARCH_RND1) {
37
+ + if (arch == LLM_ARCH_COHERE2MOE || arch == LLM_ARCH_QWEN3MOE || arch == LLM_ARCH_OPENAI_MOE || arch == LLM_ARCH_QWEN3VLMOE || arch == LLM_ARCH_RND1) {
38
+ LLAMA_LOG_INFO("%s: n_ff_exp = %d\n", __func__, hparams.n_ff_exp);
39
+ }
40
+
41
+ @@ -2338,6 +2340,7 @@
42
+ case LLM_ARCH_XVERSE:
43
+ case LLM_ARCH_COMMAND_R:
44
+ case LLM_ARCH_COHERE2:
45
+ + case LLM_ARCH_COHERE2MOE:
46
+ case LLM_ARCH_OLMO:
47
+ case LLM_ARCH_ARCTIC:
48
+ case LLM_ARCH_DEEPSEEK:
49
+ --- a/src/models/models.h
50
+ +++ b/src/models/models.h
51
+ @@ -964,6 +964,20 @@
52
+ };
53
+
54
+
55
+ +
56
+ +struct llama_model_cohere2moe : public llama_model_base {
57
+ + llama_model_cohere2moe(const struct llama_model_params & params) : llama_model_base(params) {}
58
+ + void load_arch_hparams(llama_model_loader & ml) override;
59
+ + void load_arch_tensors(llama_model_loader & ml) override;
60
+ +
61
+ + struct graph : public llm_graph_context {
62
+ + graph(const llama_model & model, const llm_graph_params & params);
63
+ + };
64
+ +
65
+ + std::unique_ptr<llm_graph_context> build_arch_graph(const llm_graph_params & params) const override;
66
+ +};
67
+ +
68
+ +
69
+ struct llama_model_dbrx : public llama_model_base {
70
+ llama_model_dbrx(const struct llama_model_params & params) : llama_model_base(params) {}
71
+ void load_arch_hparams(llama_model_loader & ml) override;
72
+ --- a/src/llama-model-saver.cpp
73
+ +++ b/src/llama-model-saver.cpp
74
+ @@ -21,6 +21,7 @@
75
+ case LLM_ARCH_GEMMA3:
76
+ case LLM_ARCH_GEMMA3N:
77
+ case LLM_ARCH_COHERE2:
78
+ + case LLM_ARCH_COHERE2MOE:
79
+ case LLM_ARCH_OLMO2:
80
+ case LLM_ARCH_BITNET:
81
+ case LLM_ARCH_T5:
82
+ --- a/src/llama-vocab.cpp
83
+ +++ b/src/llama-vocab.cpp
84
+ @@ -2267,7 +2267,8 @@
85
+ pre_type = LLAMA_VOCAB_PRE_TYPE_GPT4O;
86
+ clean_spaces = false;
87
+ } else if (
88
+ - tokenizer_pre == "tiny_aya") {
89
+ + tokenizer_pre == "tiny_aya" ||
90
+ + tokenizer_pre == "cohere2moe") {
91
+ pre_type = LLAMA_VOCAB_PRE_TYPE_TINY_AYA;
92
+ clean_spaces = false;
93
+ } else if (
94
+ --- a/gguf-py/gguf/constants.py
95
+ +++ b/gguf-py/gguf/constants.py
96
+ @@ -467,6 +467,7 @@
97
+ XVERSE = auto()
98
+ COMMAND_R = auto()
99
+ COHERE2 = auto()
100
+ + COHERE2MOE = auto()
101
+ DBRX = auto()
102
+ OLMO = auto()
103
+ OLMO2 = auto()
104
+ @@ -1029,6 +1030,7 @@
105
+ MODEL_ARCH.XVERSE: "xverse",
106
+ MODEL_ARCH.COMMAND_R: "command-r",
107
+ MODEL_ARCH.COHERE2: "cohere2",
108
+ + MODEL_ARCH.COHERE2MOE: "cohere2moe",
109
+ MODEL_ARCH.DBRX: "dbrx",
110
+ MODEL_ARCH.OLMO: "olmo",
111
+ MODEL_ARCH.OLMO2: "olmo2",
112
+ @@ -2905,6 +2907,33 @@
113
+ MODEL_TENSOR.FFN_DOWN,
114
+ MODEL_TENSOR.FFN_UP,
115
+ ],
116
+ + MODEL_ARCH.COHERE2MOE: [
117
+ + MODEL_TENSOR.TOKEN_EMBD,
118
+ + MODEL_TENSOR.OUTPUT_NORM,
119
+ + MODEL_TENSOR.OUTPUT,
120
+ + MODEL_TENSOR.ATTN_NORM,
121
+ + MODEL_TENSOR.ATTN_Q,
122
+ + MODEL_TENSOR.ATTN_K,
123
+ + MODEL_TENSOR.ATTN_V,
124
+ + MODEL_TENSOR.ATTN_OUT,
125
+ + MODEL_TENSOR.FFN_GATE,
126
+ + MODEL_TENSOR.FFN_DOWN,
127
+ + MODEL_TENSOR.FFN_UP,
128
+ + MODEL_TENSOR.FFN_GATE_INP,
129
+ + MODEL_TENSOR.FFN_GATE_EXP,
130
+ + MODEL_TENSOR.FFN_GATE_UP_EXP,
131
+ + MODEL_TENSOR.FFN_DOWN_EXP,
132
+ + MODEL_TENSOR.FFN_UP_EXP,
133
+ + MODEL_TENSOR.FFN_GATE_SHEXP,
134
+ + MODEL_TENSOR.FFN_DOWN_SHEXP,
135
+ + MODEL_TENSOR.FFN_UP_SHEXP,
136
+ + MODEL_TENSOR.NEXTN_EH_PROJ,
137
+ + MODEL_TENSOR.NEXTN_EMBED_TOKENS,
138
+ + MODEL_TENSOR.NEXTN_ENORM,
139
+ + MODEL_TENSOR.NEXTN_HNORM,
140
+ + MODEL_TENSOR.NEXTN_SHARED_HEAD_HEAD,
141
+ + MODEL_TENSOR.NEXTN_SHARED_HEAD_NORM,
142
+ + ],
143
+ MODEL_ARCH.DBRX: [
144
+ MODEL_TENSOR.TOKEN_EMBD,
145
+ MODEL_TENSOR.OUTPUT_NORM,
146
+ --- a/convert_hf_to_gguf.py
147
+ +++ b/convert_hf_to_gguf.py
148
+ @@ -1443,7 +1443,7 @@
149
+ res = "command-r"
150
+ if chkhsh == "d772b220ace2baec124bed8cfafce0ead7d6c38a4b65ef11261cf9d5d62246d1":
151
+ # ref: https://huggingface.co/CohereLabs/tiny-aya-base
152
+ - res = "tiny_aya"
153
+ + res = "tiny_aya" # also used by cohere2moe / North Mini
154
+ if chkhsh == "e636dc30a262dcc0d8c323492e32ae2b70728f4df7dfe9737d9f920a282b8aea":
155
+ # ref: https://huggingface.co/Qwen/Qwen1.5-7B
156
+ res = "qwen2"
157
+ @@ -8978,6 +8978,89 @@
158
+ yield from super().modify_tensors(data_torch, name, bid)
159
+
160
+
161
+ +
162
+ +@ModelBase.register("Cohere2MoeForCausalLM")
163
+ +class Cohere2MoeModel(TextModel):
164
+ + """Cohere2 MoE (North Mini Code). Ported from mainline conversion/command_r.py.
165
+ + Per-expert FC biases: skipped when zero; ValueError if non-zero (runtime has no bias path required for North GGUFs).
166
+ + """
167
+ + model_arch = gguf.MODEL_ARCH.COHERE2MOE
168
+ + _n_main_layers = None
169
+ + _expert_tensor_re = __import__("re").compile(
170
+ + r"model\.layers\.(\d+)\.mlp\.experts\.(\d+)\.(down_proj|gate_proj|up_proj)\.weight"
171
+ + )
172
+ +
173
+ + def __init__(self, *args, **kwargs):
174
+ + super().__init__(*args, **kwargs)
175
+ + n_nextn = int(self.hparams.get("num_nextn_predict_layers", 0) or 0)
176
+ + if n_nextn > 0 and not getattr(self, "no_mtp", False):
177
+ + self.block_count += n_nextn
178
+ + self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count)
179
+ + self._experts = [{} for _ in range(self.block_count)]
180
+ +
181
+ + def set_gguf_parameters(self):
182
+ + hparams = self.hparams
183
+ + expert_intermediate_size = hparams["intermediate_size"]
184
+ + mlp_layer_types = hparams.get("mlp_layer_types")
185
+ + n_dense_lead = hparams.get("first_k_dense_replace", 0)
186
+ + if mlp_layer_types is not None:
187
+ + n_dense_lead = next((i for i, tp in enumerate(mlp_layer_types) if tp != "dense"), len(mlp_layer_types))
188
+ + super().set_gguf_parameters()
189
+ + self.gguf_writer.add_logit_scale(hparams["logit_scale"])
190
+ + self.gguf_writer.add_sliding_window(hparams["sliding_window"])
191
+ + self.gguf_writer.add_sliding_window_pattern([tp == "sliding_attention" for tp in hparams["layer_types"]])
192
+ + self.gguf_writer.add_vocab_size(hparams["vocab_size"])
193
+ + self.gguf_writer.add_expert_feed_forward_length(expert_intermediate_size)
194
+ + self.gguf_writer.add_leading_dense_block_count(n_dense_lead)
195
+ + self.gguf_writer.add_expert_weights_norm(hparams.get("norm_topk_prob", False))
196
+ + if (num_shared_experts := hparams.get("num_shared_experts", 0)) > 0:
197
+ + if hparams.get("shared_expert_combination_strategy", "average") != "average":
198
+ + raise ValueError("Cohere2 MoE only supports average shared expert combination")
199
+ + self.gguf_writer.add_expert_shared_count(num_shared_experts)
200
+ + self.gguf_writer.add_expert_shared_feed_forward_length(expert_intermediate_size * num_shared_experts)
201
+ + n_nextn = hparams.get("num_nextn_predict_layers", 0)
202
+ + if n_nextn > 0 and not getattr(self, "no_mtp", False):
203
+ + self.gguf_writer.add_nextn_predict_layers(n_nextn)
204
+ + self.gguf_writer.add_rope_dimension_count(hparams["head_dim"])
205
+ + self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE)
206
+ +
207
+ + def modify_tensors(self, data_torch, name, bid=None):
208
+ + import torch
209
+ + if name.endswith(".bias"):
210
+ + if torch.any(data_torch != 0):
211
+ + raise ValueError(f"Bias tensor {name!r} is not zero.")
212
+ + logger.debug(f"Skipping bias tensor {name!r}.")
213
+ + return
214
+ + m = self._expert_tensor_re.fullmatch(name)
215
+ + if m is not None:
216
+ + n_experts = self.hparams["num_experts"]
217
+ + layer_idx = int(m.group(1))
218
+ + self._experts[layer_idx][name] = data_torch
219
+ + expected = {
220
+ + f"model.layers.{layer_idx}.mlp.experts.{xid}.{w_name}.weight"
221
+ + for xid in range(n_experts)
222
+ + for w_name in ("down_proj", "gate_proj", "up_proj")
223
+ + }
224
+ + if expected.issubset(self._experts[layer_idx]):
225
+ + for w_name in ["down_proj", "gate_proj", "up_proj"]:
226
+ + datas = []
227
+ + for xid in range(n_experts):
228
+ + ename = f"model.layers.{layer_idx}.mlp.experts.{xid}.{w_name}.weight"
229
+ + datas.append(self._experts[layer_idx][ename])
230
+ + del self._experts[layer_idx][ename]
231
+ + data_torch = torch.stack(datas, dim=0)
232
+ + merged_name = f"model.layers.{layer_idx}.mlp.experts.{w_name}.weight"
233
+ + yield from super().modify_tensors(data_torch, merged_name, layer_idx)
234
+ + return
235
+ + yield from super().modify_tensors(data_torch, name, bid)
236
+ +
237
+ + def prepare_tensors(self):
238
+ + super().prepare_tensors()
239
+ + experts = [k for d in self._experts for k in d.keys()]
240
+ + if len(experts) > 0:
241
+ + raise ValueError(f"Unprocessed experts: {experts}")
242
+ +
243
+ +
244
+ @ModelBase.register("OlmoForCausalLM")
245
+ @ModelBase.register("OLMoForCausalLM")
246
+ class OlmoModel(TextModel):
247
+ --- a/conversion/__init__.py
248
+ +++ b/conversion/__init__.py
249
+ @@ -42,6 +42,7 @@
250
+ "CodeShellForCausalLM": "codeshell",
251
+ "CogVLMForCausalLM": "cogvlm",
252
+ "Cohere2ForCausalLM": "command_r",
253
+ + "Cohere2MoeForCausalLM": "command_r",
254
+ "CohereForCausalLM": "command_r",
255
+ "DbrxForCausalLM": "dbrx",
256
+ "DeciLMForCausalLM": "deci",
257
+ --- a/conversion/base.py
258
+ +++ b/conversion/base.py
259
+ @@ -1505,7 +1505,7 @@
260
+ res = "command-r"
261
+ if chkhsh == "d772b220ace2baec124bed8cfafce0ead7d6c38a4b65ef11261cf9d5d62246d1":
262
+ # ref: https://huggingface.co/CohereLabs/tiny-aya-base
263
+ - res = "tiny_aya"
264
+ + res = "tiny_aya" # also used by cohere2moe / North Mini
265
+ if chkhsh == "e636dc30a262dcc0d8c323492e32ae2b70728f4df7dfe9737d9f920a282b8aea":
266
+ # ref: https://huggingface.co/Qwen/Qwen1.5-7B
267
+ res = "qwen2"
268
+ --- a/src/models/cohere2moe.cpp
269
+ +++ b/src/models/cohere2moe.cpp
270
+ @@ -0,0 +1,310 @@
271
+ +#include "models.h"
272
+ +
273
+ +// Port of ggml-org/llama.cpp cohere2moe into ROCmFPX fork.
274
+ +// Adapted to fork APIs:
275
+ +// - hparams.n_layer field (not n_layer())
276
+ +// - hparams.nextn_predict_layers (not n_layer_nextn / n_layer_all)
277
+ +// - no embeddings_nextn_masked / t_h_nextn (fork lacks those result slots)
278
+ +// - SWA pattern loaded as bool array into hparams.swa_layers
279
+ +// Bias tensors: HF Cohere2 MoE ships per-expert FC biases, but conversion
280
+ +// drops them when zero (mainline convert raises if non-zero). Production
281
+ +// North-Mini-Code GGUF has 0 bias tensors; graph therefore passes nullptr
282
+ +// biases, matching mainline. Optional TENSOR_NOT_REQUIRED bias loads are
283
+ +// included so a future non-zero-bias GGUF can bind without recompile.
284
+ +
285
+ +void llama_model_cohere2moe::load_arch_hparams(llama_model_loader & ml) {
286
+ + const bool found_norm = ml.get_key(LLM_KV_ATTENTION_LAYERNORM_EPS, hparams.f_norm_eps, false);
287
+ + const bool found_norm_rms = ml.get_key(LLM_KV_ATTENTION_LAYERNORM_RMS_EPS, hparams.f_norm_rms_eps, false);
288
+ + if (!found_norm && !found_norm_rms) {
289
+ + throw std::runtime_error("missing Cohere2 MoE norm epsilon");
290
+ + }
291
+ + if (!found_norm_rms) {
292
+ + hparams.f_norm_rms_eps = 0.0f;
293
+ + }
294
+ +
295
+ + ml.get_key(LLM_KV_ATTENTION_SLIDING_WINDOW, hparams.n_swa);
296
+ + ml.get_key(LLM_KV_LOGIT_SCALE, hparams.f_logit_scale);
297
+ + ml.get_key(LLM_KV_LEADING_DENSE_BLOCK_COUNT, hparams.n_layer_dense_lead);
298
+ + ml.get_key(LLM_KV_EXPERT_FEED_FORWARD_LENGTH, hparams.n_ff_exp);
299
+ + ml.get_key(LLM_KV_EXPERT_SHARED_FEED_FORWARD_LENGTH, hparams.n_ff_shexp, false);
300
+ + ml.get_key(LLM_KV_EXPERT_SHARED_COUNT, hparams.n_expert_shared, false);
301
+ + ml.get_key(LLM_KV_EXPERT_WEIGHTS_NORM, hparams.expert_weights_norm, false);
302
+ + ml.get_key(LLM_KV_EXPERT_WEIGHTS_SCALE, hparams.expert_weights_scale, false);
303
+ + ml.get_key(LLM_KV_EXPERT_GATING_FUNC, hparams.expert_gating_func, false);
304
+ +
305
+ + ml.get_key(LLM_KV_NEXTN_PREDICT_LAYERS, hparams.nextn_predict_layers, false);
306
+ + GGML_ASSERT(hparams.nextn_predict_layers < hparams.n_layer && "nextn_predict_layers must be < n_layer");
307
+ +
308
+ + if (hparams.expert_gating_func == LLAMA_EXPERT_GATING_FUNC_TYPE_NONE) {
309
+ + hparams.expert_gating_func = LLAMA_EXPERT_GATING_FUNC_TYPE_SIGMOID;
310
+ + }
311
+ +
312
+ + hparams.swa_type = LLAMA_SWA_TYPE_STANDARD;
313
+ + // Prefer full per-layer bool pattern (North Mini GGUF ships this);
314
+ + // fall back to period integer used by some cohere2 exports.
315
+ + if (!ml.get_key_or_arr(LLM_KV_ATTENTION_SLIDING_WINDOW_PATTERN, hparams.swa_layers, hparams.n_layer, false)) {
316
+ + uint32_t swa_period = 4;
317
+ + if (ml.get_key_or_arr(LLM_KV_ATTENTION_SLIDING_WINDOW_PATTERN, swa_period, false)) {
318
+ + hparams.set_swa_pattern(swa_period, true);
319
+ + } else {
320
+ + hparams.set_swa_pattern(swa_period, true);
321
+ + }
322
+ + }
323
+ +
324
+ + hparams.rope_freq_base_train_swa = hparams.rope_freq_base_train;
325
+ + hparams.rope_freq_scale_train_swa = hparams.rope_freq_scale_train;
326
+ + ml.get_key(LLM_KV_ROPE_FREQ_BASE_SWA, hparams.rope_freq_base_train_swa, false);
327
+ +
328
+ + // MTP layers (if any) are the last nextn_predict_layers of n_layer, same
329
+ + // convention as glm4-moe in this fork.
330
+ + if (hparams.nextn_predict_layers > 0) {
331
+ + hparams.n_layer_kv_from_start = hparams.n_layer - hparams.nextn_predict_layers;
332
+ + }
333
+ +
334
+ + switch (hparams.n_layer) {
335
+ + case 49: type = LLM_TYPE_30B_A3B; break; // North Mini Code (48 trunk + optional nextn)
336
+ + default: type = LLM_TYPE_UNKNOWN;
337
+ + }
338
+ +}
339
+ +
340
+ +void llama_model_cohere2moe::load_arch_tensors(llama_model_loader & ml) {
341
+ + LLAMA_LOAD_LOCALS;
342
+ +
343
+ + tok_embd = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), { n_embd, n_vocab }, 0);
344
+ +
345
+ + output_norm = create_tensor(tn(LLM_TENSOR_OUTPUT_NORM, "weight"), { n_embd }, 0);
346
+ + output = create_tensor(tn(LLM_TENSOR_OUTPUT, "weight"), { n_embd, n_vocab }, TENSOR_NOT_REQUIRED);
347
+ +
348
+ + if (output == NULL) {
349
+ + output = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), { n_embd, n_vocab }, TENSOR_DUPLICATED);
350
+ + }
351
+ +
352
+ + if (n_expert == 0) {
353
+ + throw std::runtime_error("n_expert must be > 0 for Cohere2Moe");
354
+ + }
355
+ + if (n_expert_used == 0) {
356
+ + throw std::runtime_error("n_expert_used must be > 0 for Cohere2Moe");
357
+ + }
358
+ +
359
+ + const int n_transformer_layers = n_layer - (int) hparams.nextn_predict_layers;
360
+ +
361
+ + for (int i = 0; i < n_layer; ++i) {
362
+ + auto & layer = layers[i];
363
+ + int flags = 0;
364
+ + const bool is_mtp = hparams.nextn_predict_layers > 0 &&
365
+ + static_cast<uint32_t>(i) >= static_cast<uint32_t>(n_transformer_layers);
366
+ + if (is_mtp) {
367
+ + // Load MTP tensors but skip execution in the main graph (glm4-moe pattern).
368
+ + flags |= TENSOR_SKIP;
369
+ + }
370
+ +
371
+ + layer.attn_norm = create_tensor(tn(LLM_TENSOR_ATTN_NORM, "weight", i), { n_embd }, flags);
372
+ +
373
+ + // Q: n_embd x (n_embd_head_k * n_head); K/V: n_embd x n_embd_gqa
374
+ + create_tensor_qkv(layer, i, n_embd, n_embd_head_k * n_head, n_embd_gqa, n_embd_gqa, flags);
375
+ + layer.wo = create_tensor(tn(LLM_TENSOR_ATTN_OUT, "weight", i), { n_embd_head_k * n_head, n_embd }, flags);
376
+ +
377
+ + // Optional attention output bias (HF may ship zeros; production GGUF has none).
378
+ + layer.wo_b = create_tensor(tn(LLM_TENSOR_ATTN_OUT, "bias", i), { n_embd }, flags | TENSOR_NOT_REQUIRED);
379
+ +
380
+ + if (!is_mtp && static_cast<uint32_t>(i) < hparams.n_layer_dense_lead) {
381
+ + // Leading dense FFN (layer 0 on North Mini)
382
+ + layer.ffn_gate = create_tensor(tn(LLM_TENSOR_FFN_GATE, "weight", i), { n_embd, n_ff }, flags);
383
+ + layer.ffn_down = create_tensor(tn(LLM_TENSOR_FFN_DOWN, "weight", i), { n_ff, n_embd }, flags);
384
+ + layer.ffn_up = create_tensor(tn(LLM_TENSOR_FFN_UP, "weight", i), { n_embd, n_ff }, flags);
385
+ +
386
+ + // Optional dense FC biases
387
+ + layer.ffn_gate_b = create_tensor(tn(LLM_TENSOR_FFN_GATE, "bias", i), { n_ff }, flags | TENSOR_NOT_REQUIRED);
388
+ + layer.ffn_up_b = create_tensor(tn(LLM_TENSOR_FFN_UP, "bias", i), { n_ff }, flags | TENSOR_NOT_REQUIRED);
389
+ + layer.ffn_down_b = create_tensor(tn(LLM_TENSOR_FFN_DOWN, "bias", i), { n_embd }, flags | TENSOR_NOT_REQUIRED);
390
+ + } else {
391
+ + const int64_t n_ff_exp = hparams.n_ff_exp ? hparams.n_ff_exp : n_ff;
392
+ +
393
+ + layer.ffn_gate_inp = create_tensor(tn(LLM_TENSOR_FFN_GATE_INP, "weight", i), { n_embd, n_expert }, flags);
394
+ + layer.ffn_down_exps = create_tensor(tn(LLM_TENSOR_FFN_DOWN_EXPS, "weight", i), { n_ff_exp, n_embd, n_expert }, flags);
395
+ + create_tensor_gate_up_exps(layer, i, n_embd, n_ff_exp, n_expert, flags);
396
+ +
397
+ + // Optional per-expert FC biases (mainline convert drops zeros; bind if present).
398
+ + // Merged expert bias tensors would be named like ffn_*_exps.bias if ever exported.
399
+ + layer.ffn_gate_exps_b = create_tensor(tn(LLM_TENSOR_FFN_GATE_EXPS, "bias", i), { n_ff_exp, n_expert }, flags | TENSOR_NOT_REQUIRED);
400
+ + layer.ffn_up_exps_b = create_tensor(tn(LLM_TENSOR_FFN_UP_EXPS, "bias", i), { n_ff_exp, n_expert }, flags | TENSOR_NOT_REQUIRED);
401
+ + layer.ffn_down_exps_b = create_tensor(tn(LLM_TENSOR_FFN_DOWN_EXPS, "bias", i), { n_embd, n_expert }, flags | TENSOR_NOT_REQUIRED);
402
+ + layer.ffn_gate_inp_b = create_tensor(tn(LLM_TENSOR_FFN_GATE_INP, "bias", i), { n_expert }, flags | TENSOR_NOT_REQUIRED);
403
+ +
404
+ + if (hparams.n_expert_shared > 0) {
405
+ + const int64_t n_ff_shexp = hparams.n_ff_shexp ? hparams.n_ff_shexp : n_ff_exp * hparams.n_expert_shared;
406
+ + layer.ffn_gate_shexp = create_tensor(tn(LLM_TENSOR_FFN_GATE_SHEXP, "weight", i), { n_embd, n_ff_shexp }, flags);
407
+ + layer.ffn_down_shexp = create_tensor(tn(LLM_TENSOR_FFN_DOWN_SHEXP, "weight", i), { n_ff_shexp, n_embd }, flags);
408
+ + layer.ffn_up_shexp = create_tensor(tn(LLM_TENSOR_FFN_UP_SHEXP, "weight", i), { n_embd, n_ff_shexp }, flags);
409
+ + }
410
+ + }
411
+ +
412
+ + if (is_mtp) {
413
+ + layer.nextn.eh_proj = create_tensor(tn(LLM_TENSOR_NEXTN_EH_PROJ, "weight", i), { 2 * n_embd, n_embd }, flags);
414
+ + layer.nextn.enorm = create_tensor(tn(LLM_TENSOR_NEXTN_ENORM, "weight", i), { n_embd }, flags);
415
+ + layer.nextn.hnorm = create_tensor(tn(LLM_TENSOR_NEXTN_HNORM, "weight", i), { n_embd }, flags);
416
+ + layer.nextn.embed_tokens = create_tensor(tn(LLM_TENSOR_NEXTN_EMBED_TOKENS, "weight", i), { n_embd, n_vocab }, flags | TENSOR_NOT_REQUIRED);
417
+ + layer.nextn.shared_head_head = create_tensor(tn(LLM_TENSOR_NEXTN_SHARED_HEAD_HEAD, "weight", i), { n_embd, n_vocab }, flags | TENSOR_NOT_REQUIRED);
418
+ + layer.nextn.shared_head_norm = create_tensor(tn(LLM_TENSOR_NEXTN_SHARED_HEAD_NORM, "weight", i), { n_embd }, flags | TENSOR_NOT_REQUIRED);
419
+ + }
420
+ + }
421
+ +}
422
+ +
423
+ +std::unique_ptr<llm_graph_context> llama_model_cohere2moe::build_arch_graph(const llm_graph_params & params) const {
424
+ + return std::make_unique<graph>(*this, params);
425
+ +}
426
+ +
427
+ +llama_model_cohere2moe::graph::graph(const llama_model & model, const llm_graph_params & params) : llm_graph_context(params) {
428
+ + const int64_t n_embd_head = hparams.n_embd_head_v();
429
+ +
430
+ + GGML_ASSERT(n_embd_head == hparams.n_embd_head_k());
431
+ + GGML_ASSERT(n_embd_head == n_rot);
432
+ +
433
+ + const llm_norm_type cohere2moe_norm_type = hparams.f_norm_rms_eps == 0.0f ? LLM_NORM : LLM_NORM_RMS;
434
+ + const float f_logit_scale = hparams.f_logit_scale;
435
+ +
436
+ + ggml_tensor * cur;
437
+ + ggml_tensor * inpL = build_inp_embd(model.tok_embd);
438
+ + ggml_tensor * inp_pos = build_inp_pos();
439
+ +
440
+ + auto * inp_attn = build_attn_inp_kv_iswa();
441
+ + ggml_tensor * inp_out_ids = build_inp_out_ids();
442
+ +
443
+ + // Skip MTP tail layers in the main decoder pass (glm4-moe convention).
444
+ + const int n_transformer_layers = n_layer - (int) hparams.nextn_predict_layers;
445
+ +
446
+ + for (int il = 0; il < n_transformer_layers; ++il) {
447
+ + const bool is_swa = hparams.is_swa(il);
448
+ + // Dense-prefix full-attention layers use RoPE; later layers follow SWA pattern.
449
+ + const bool force_rope = static_cast<uint32_t>(il) < hparams.n_layer_dense_lead;
450
+ +
451
+ + cur = build_norm(inpL, model.layers[il].attn_norm, nullptr, cohere2moe_norm_type, il);
452
+ + cb(cur, "attn_norm", il);
453
+ +
454
+ + ggml_tensor * ffn_inp = cur;
455
+ +
456
+ + {
457
+ + const auto & layer = model.layers[il];
458
+ +
459
+ + auto [Qcur, Kcur, Vcur] = build_qkv(layer, cur,
460
+ + n_embd_head, n_head, n_head_kv, il);
461
+ +
462
+ + if (is_swa || force_rope) {
463
+ + ggml_tensor * rope_factors = model.get_rope_factors(cparams, il);
464
+ +
465
+ + Qcur = ggml_rope_ext(
466
+ + ctx0, Qcur, inp_pos, rope_factors,
467
+ + n_rot, rope_type, n_ctx_orig, freq_base, freq_scale,
468
+ + ext_factor, attn_factor, beta_fast, beta_slow);
469
+ +
470
+ + Kcur = ggml_rope_ext(
471
+ + ctx0, Kcur, inp_pos, rope_factors,
472
+ + n_rot, rope_type, n_ctx_orig, freq_base, freq_scale,
473
+ + ext_factor, attn_factor, beta_fast, beta_slow);
474
+ + }
475
+ +
476
+ + cb(Qcur, "Qcur", il);
477
+ + cb(Kcur, "Kcur", il);
478
+ + cb(Vcur, "Vcur", il);
479
+ +
480
+ + cur = build_attn(inp_attn,
481
+ + layer.wo, layer.wo_b, layer.wo_s,
482
+ + Qcur, Kcur, Vcur, nullptr, nullptr, nullptr,
483
+ + 1.0f / sqrtf(float(n_embd_head)), il);
484
+ + }
485
+ +
486
+ + if (il == n_transformer_layers - 1 && inp_out_ids) {
487
+ + cur = ggml_get_rows(ctx0, cur, inp_out_ids);
488
+ + inpL = ggml_get_rows(ctx0, inpL, inp_out_ids);
489
+ + ffn_inp = ggml_get_rows(ctx0, ffn_inp, inp_out_ids);
490
+ + }
491
+ +
492
+ + ggml_tensor * attn_out = cur;
493
+ +
494
+ + const auto & layer = model.layers[il];
495
+ +
496
+ + if (layer.ffn_gate_inp == nullptr) {
497
+ + cur = build_ffn(ffn_inp,
498
+ + layer.ffn_up, layer.ffn_up_b, layer.ffn_up_s,
499
+ + layer.ffn_gate, layer.ffn_gate_b, layer.ffn_gate_s,
500
+ + layer.ffn_down, layer.ffn_down_b, layer.ffn_down_s,
501
+ + nullptr, LLM_FFN_SILU, LLM_FFN_PAR, il);
502
+ + cb(cur, "ffn_out", il);
503
+ + } else {
504
+ + // Prefer bias-aware overload if any expert bias is present; else weight-only path.
505
+ + if (layer.ffn_up_exps_b || layer.ffn_gate_exps_b || layer.ffn_down_exps_b || layer.ffn_gate_inp_b) {
506
+ + cur = build_moe_ffn(ffn_inp,
507
+ + layer.ffn_gate_inp, layer.ffn_gate_inp_b,
508
+ + layer.ffn_up_exps, layer.ffn_up_exps_b,
509
+ + layer.ffn_gate_exps, layer.ffn_gate_exps_b,
510
+ + layer.ffn_down_exps, layer.ffn_down_exps_b,
511
+ + nullptr,
512
+ + n_expert, n_expert_used,
513
+ + LLM_FFN_SILU, hparams.expert_weights_norm,
514
+ + hparams.expert_weights_scale,
515
+ + (llama_expert_gating_func_type) hparams.expert_gating_func,
516
+ + il,
517
+ + nullptr, layer.ffn_gate_up_exps, layer.ffn_gate_up_exps_b,
518
+ + layer.ffn_up_exps_s,
519
+ + layer.ffn_gate_exps_s,
520
+ + layer.ffn_down_exps_s);
521
+ + } else {
522
+ + cur = build_moe_ffn(ffn_inp,
523
+ + layer.ffn_gate_inp,
524
+ + layer.ffn_up_exps,
525
+ + layer.ffn_gate_exps,
526
+ + layer.ffn_down_exps,
527
+ + nullptr,
528
+ + n_expert, n_expert_used,
529
+ + LLM_FFN_SILU, hparams.expert_weights_norm,
530
+ + hparams.expert_weights_scale,
531
+ + (llama_expert_gating_func_type) hparams.expert_gating_func,
532
+ + il,
533
+ + nullptr, layer.ffn_gate_up_exps,
534
+ + layer.ffn_up_exps_s,
535
+ + layer.ffn_gate_exps_s,
536
+ + layer.ffn_down_exps_s);
537
+ + }
538
+ + cb(cur, "ffn_moe_out", il);
539
+ +
540
+ + if (layer.ffn_up_shexp) {
541
+ + ggml_tensor * ffn_shexp = build_ffn(ffn_inp,
542
+ + layer.ffn_up_shexp, nullptr, layer.ffn_up_shexp_s,
543
+ + layer.ffn_gate_shexp, nullptr, layer.ffn_gate_shexp_s,
544
+ + layer.ffn_down_shexp, nullptr, layer.ffn_down_shexp_s,
545
+ + nullptr, LLM_FFN_SILU, LLM_FFN_PAR, il);
546
+ + cb(ffn_shexp, "ffn_shexp", il);
547
+ +
548
+ + cur = ggml_add(ctx0, cur, ffn_shexp);
549
+ + cur = ggml_scale(ctx0, cur, 0.5f);
550
+ + cb(cur, "ffn_out", il);
551
+ + }
552
+ + }
553
+ +
554
+ + // Parallel residual: residual + FFN + attention (Cohere-style)
555
+ + cur = ggml_add(ctx0, cur, inpL);
556
+ + cur = ggml_add(ctx0, cur, attn_out);
557
+ +
558
+ + cur = build_cvec(cur, il);
559
+ + cb(cur, "l_out", il);
560
+ +
561
+ + inpL = cur;
562
+ + }
563
+ +
564
+ + cur = inpL;
565
+ + cur = build_norm(cur, model.output_norm, nullptr, cohere2moe_norm_type, -1);
566
+ +
567
+ + cb(cur, "result_norm", -1);
568
+ + res->t_embd = cur;
569
+ +
570
+ + cur = build_lora_mm(model.output, cur);
571
+ +
572
+ + if (f_logit_scale) {
573
+ + cur = ggml_scale(ctx0, cur, f_logit_scale);
574
+ + }
575
+ +
576
+ + cb(cur, "result_output", -1);
577
+ + res->t_logits = cur;
578
+ +
579
+ + ggml_build_forward_expand(gf, cur);
580
+ +}
581
+ --- a/conversion/command_r.py
582
+ +++ b/conversion/command_r.py
583
+ @@ -0,0 +1,177 @@
584
+ +from __future__ import annotations
585
+ +
586
+ +import re
587
+ +from typing import Iterable, TYPE_CHECKING
588
+ +
589
+ +import torch
590
+ +
591
+ +if TYPE_CHECKING:
592
+ + from torch import Tensor
593
+ +
594
+ +from .base import ModelBase, TextModel, gguf, logger
595
+ +
596
+ +
597
+ +@ModelBase.register("CohereForCausalLM")
598
+ +class CommandR2Model(TextModel):
599
+ + model_arch = gguf.MODEL_ARCH.COMMAND_R
600
+ +
601
+ + def __init__(self, *args, **kwargs):
602
+ + super().__init__(*args, **kwargs)
603
+ +
604
+ + # max_position_embeddings = 8192 in config.json but model was actually
605
+ + # trained on 128k context length
606
+ + # aya-23 models don't have model_max_length specified
607
+ + self.hparams["max_position_embeddings"] = self.find_hparam(["model_max_length", "max_position_embeddings"])
608
+ +
609
+ + def set_gguf_parameters(self):
610
+ + super().set_gguf_parameters()
611
+ + self.gguf_writer.add_logit_scale(self.hparams["logit_scale"])
612
+ + self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE)
613
+ +
614
+ +
615
+ +@ModelBase.register("Cohere2ForCausalLM")
616
+ +class Cohere2Model(TextModel):
617
+ + model_arch = gguf.MODEL_ARCH.COHERE2
618
+ +
619
+ + def set_gguf_parameters(self):
620
+ + super().set_gguf_parameters()
621
+ +
622
+ + self.gguf_writer.add_logit_scale(self.hparams["logit_scale"])
623
+ + self.gguf_writer.add_sliding_window(self.hparams["sliding_window"])
624
+ + self.gguf_writer.add_vocab_size(self.hparams["vocab_size"])
625
+ +
626
+ + rotary_pct = self.hparams["rotary_pct"]
627
+ + hidden_size = self.hparams["hidden_size"]
628
+ + num_attention_heads = self.hparams["num_attention_heads"]
629
+ + self.gguf_writer.add_rope_dimension_count(int(rotary_pct * (hidden_size // num_attention_heads)))
630
+ + self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE)
631
+ +
632
+ + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
633
+ + # Cohere2 runtime in llama.cpp expects no bias tensors;
634
+ + # the actual weight only contains 0-value tensors as bias, we can skip them
635
+ + if name.endswith(".bias"):
636
+ + if torch.any(data_torch != 0):
637
+ + raise ValueError(f"Bias tensor {name!r} is not zero.")
638
+ + logger.debug(f"Skipping bias tensor {name!r} for Cohere2 conversion.")
639
+ + return
640
+ +
641
+ + yield from super().modify_tensors(data_torch, name, bid)
642
+ +
643
+ +
644
+ +@ModelBase.register("Cohere2MoeForCausalLM")
645
+ +class Cohere2MoeModel(TextModel):
646
+ + model_arch = gguf.MODEL_ARCH.COHERE2MOE
647
+ + _n_main_layers: int | None = None
648
+ + _expert_tensor_re = re.compile(
649
+ + r"model\.layers\.(\d+)\.mlp\.experts\.(\d+)\.(down_proj|gate_proj|up_proj)\.weight"
650
+ + )
651
+ +
652
+ + def __init__(self, *args, **kwargs):
653
+ + super().__init__(*args, **kwargs)
654
+ + if (n_nextn := int(self.hparams.get("num_nextn_predict_layers", 0) or 0)) > 0 and not self.no_mtp:
655
+ + self.block_count += n_nextn
656
+ + self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count)
657
+ + self._experts: list[dict[str, Tensor]] = [{} for _ in range(self.block_count)]
658
+ +
659
+ + def _set_vocab_gpt2(self) -> None:
660
+ + tokens, toktypes, tokpre = self.get_vocab_base()
661
+ + self.gguf_writer.add_tokenizer_model("gpt2")
662
+ + self.gguf_writer.add_tokenizer_pre(tokpre)
663
+ + self.gguf_writer.add_token_list(tokens)
664
+ + self.gguf_writer.add_token_types(toktypes)
665
+ +
666
+ + special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True)
667
+ + special_vocab.add_to_gguf(self.gguf_writer)
668
+ +
669
+ + def set_gguf_parameters(self):
670
+ + hparams = self.hparams
671
+ + expert_intermediate_size = hparams["intermediate_size"]
672
+ + mlp_layer_types = hparams.get("mlp_layer_types")
673
+ + n_dense_lead = hparams.get("first_k_dense_replace", 0)
674
+ + if mlp_layer_types is not None:
675
+ + n_dense_lead = next((i for i, t in enumerate(mlp_layer_types) if t != "dense"), len(mlp_layer_types))
676
+ +
677
+ + super().set_gguf_parameters()
678
+ +
679
+ + self.gguf_writer.add_logit_scale(hparams["logit_scale"])
680
+ + self.gguf_writer.add_sliding_window(hparams["sliding_window"])
681
+ + self.gguf_writer.add_sliding_window_pattern([t == "sliding_attention" for t in hparams["layer_types"]])
682
+ + self.gguf_writer.add_vocab_size(hparams["vocab_size"])
683
+ + self.gguf_writer.add_expert_feed_forward_length(expert_intermediate_size)
684
+ + self.gguf_writer.add_leading_dense_block_count(n_dense_lead)
685
+ + self.gguf_writer.add_expert_weights_norm(hparams.get("norm_topk_prob", False))
686
+ + if (num_shared_experts := hparams.get("num_shared_experts", 0)) > 0:
687
+ + if hparams.get("shared_expert_combination_strategy", "average") != "average":
688
+ + raise ValueError("Cohere2 MoE only supports average shared expert combination")
689
+ + self.gguf_writer.add_expert_shared_count(num_shared_experts)
690
+ + self.gguf_writer.add_expert_shared_feed_forward_length(expert_intermediate_size * num_shared_experts)
691
+ + if (n_nextn := hparams.get("num_nextn_predict_layers", 0)) > 0 and not self.no_mtp:
692
+ + self.gguf_writer.add_nextn_predict_layers(n_nextn)
693
+ + self.gguf_writer.add_rope_dimension_count(hparams["head_dim"])
694
+ + self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE)
695
+ +
696
+ + def index_tensors(self, remote_hf_model_id: str | None = None):
697
+ + hparams = {**self.hparams, **self.hparams.get("text_config", {})}
698
+ + self._n_main_layers = hparams.get("num_hidden_layers")
699
+ + type(self)._n_main_layers = self._n_main_layers
700
+ + return super().index_tensors(remote_hf_model_id=remote_hf_model_id)
701
+ +
702
+ + @classmethod
703
+ + def filter_tensors(cls, item):
704
+ + if (titem := super().filter_tensors(item)) is None:
705
+ + return None
706
+ + name, gen = titem
707
+ +
708
+ + if cls._n_main_layers is not None:
709
+ + is_mtp = (m := re.match(r"model\.layers\.(\d+)\.", name)) is not None and int(m.group(1)) >= cls._n_main_layers
710
+ + if is_mtp and cls.no_mtp:
711
+ + return None
712
+ + if cls.mtp_only and not is_mtp and name not in (
713
+ + "model.embed_tokens.weight", "model.norm.weight", "lm_head.weight",
714
+ + ):
715
+ + return None
716
+ +
717
+ + return name, gen
718
+ +
719
+ + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
720
+ + if name.endswith(".bias"):
721
+ + if torch.any(data_torch != 0):
722
+ + raise ValueError(f"Bias tensor {name!r} is not zero.")
723
+ + logger.debug(f"Skipping bias tensor {name!r}.")
724
+ + return
725
+ +
726
+ + if (m := self._expert_tensor_re.fullmatch(name)) is not None:
727
+ + n_experts = self.hparams["num_experts"]
728
+ + layer_idx = int(m.group(1))
729
+ + assert bid is None or bid == layer_idx
730
+ +
731
+ + self._experts[layer_idx][name] = data_torch
732
+ +
733
+ + expected = {
734
+ + f"model.layers.{layer_idx}.mlp.experts.{xid}.{w_name}.weight"
735
+ + for xid in range(n_experts)
736
+ + for w_name in ("down_proj", "gate_proj", "up_proj")
737
+ + }
738
+ + if expected.issubset(self._experts[layer_idx]):
739
+ + for w_name in ["down_proj", "gate_proj", "up_proj"]:
740
+ + datas: list[Tensor] = []
741
+ +
742
+ + for xid in range(n_experts):
743
+ + ename = f"model.layers.{layer_idx}.mlp.experts.{xid}.{w_name}.weight"
744
+ + datas.append(self._experts[layer_idx][ename])
745
+ + del self._experts[layer_idx][ename]
746
+ +
747
+ + data_torch = torch.stack(datas, dim=0)
748
+ + merged_name = f"model.layers.{layer_idx}.mlp.experts.{w_name}.weight"
749
+ +
750
+ + yield from super().modify_tensors(data_torch, merged_name, layer_idx)
751
+ + return
752
+ +
753
+ + yield from super().modify_tensors(data_torch, name, bid)
754
+ +
755
+ + def prepare_tensors(self):
756
+ + super().prepare_tensors()
757
+ +
758
+ + experts = [k for d in self._experts for k in d.keys()]
759
+ + if len(experts) > 0:
760
+ + raise ValueError(f"Unprocessed experts: {experts}")