DreamFoundries commited on
Commit
8f1ee35
·
verified ·
1 Parent(s): bde4d98

Upload MLX metadata

Browse files
LICENSE.md ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ OpenMDW License Agreement, version 1.1 (OpenMDW-1.1)
2
+
3
+ By exercising rights granted to you under this agreement, you accept and agree
4
+ to its terms.
5
+
6
+ As used in this agreement, "Model Materials" means the materials provided to
7
+ you under this agreement, consisting of: (1) one or more machine learning
8
+ models (including architecture and parameters); and (2) all related artifacts
9
+ (including associated data, documentation and software) that are provided to
10
+ you hereunder.
11
+
12
+ Subject to your compliance with this agreement, permission is hereby granted,
13
+ free of charge, to deal in the Model Materials without restriction, including
14
+ under all copyright, patent, database, and trade secret rights included or
15
+ embodied therein.
16
+
17
+ If you distribute any portion of the Model Materials, you shall retain in your
18
+ distribution (1) a copy of this agreement, and (2) all copyright notices and
19
+ other notices of origin included in the Model Materials that are applicable to
20
+ your distribution.
21
+
22
+ If you file, maintain, or voluntarily participate in a lawsuit against any
23
+ person or entity asserting that the Model Materials directly or indirectly
24
+ infringe any patent or copyright, then all rights and grants made to you
25
+ hereunder are terminated, unless that lawsuit was in response to a
26
+ corresponding lawsuit first brought against you.
27
+
28
+ This agreement does not impose any restrictions or obligations with respect to
29
+ any use, modification, or sharing of any outputs generated by using the Model
30
+ Materials.
31
+
32
+ THE MODEL MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
33
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34
+ FITNESS FOR A PARTICULAR PURPOSE, TITLE, NONINFRINGEMENT, ACCURACY, OR THE
35
+ ABSENCE OF LATENT OR OTHER DEFECTS OR ERRORS, WHETHER OR NOT DISCOVERABLE, ALL
36
+ TO THE GREATEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW.
37
+
38
+ YOU ARE SOLELY RESPONSIBLE FOR (1) CLEARING RIGHTS OF OTHER PERSONS THAT MAY
39
+ APPLY TO THE MODEL MATERIALS OR ANY USE THEREOF, INCLUDING WITHOUT LIMITATION
40
+ ANY PERSON'S COPYRIGHTS OR OTHER RIGHTS INCLUDED OR EMBODIED IN THE MODEL
41
+ MATERIALS; (2) OBTAINING ANY NECESSARY CONSENTS, PERMISSIONS OR OTHER RIGHTS
42
+ REQUIRED FOR ANY USE OF THE MODEL MATERIALS; OR (3) PERFORMING ANY DUE
43
+ DILIGENCE OR UNDERTAKING ANY OTHER INVESTIGATIONS INTO THE MODEL MATERIALS OR
44
+ ANYTHING INCORPORATED OR EMBODIED THEREIN.
45
+
46
+ IN NO EVENT SHALL THE PROVIDERS OF THE MODEL MATERIALS BE LIABLE FOR ANY CLAIM,
47
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
48
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MODEL MATERIALS, THE
49
+ USE THEREOF OR OTHER DEALINGS THEREIN.
README.md ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ library_name: mlx
3
+ base_model: poolside/Laguna-S-2.1
4
+ license: openmdw-1.1
5
+ pipeline_tag: text-generation
6
+ tags:
7
+ - mlx
8
+ - mlx-lm
9
+ - laguna
10
+ - quantized
11
+ ---
12
+
13
+ # Laguna-S-2.1-6bit
14
+
15
+ This repository contains an MLX conversion of [poolside/Laguna-S-2.1](https://huggingface.co/poolside/Laguna-S-2.1).
16
+
17
+ ## Conversion Details
18
+
19
+ - Original model: `poolside/Laguna-S-2.1`
20
+ - Model family: Laguna S 2.1
21
+ - Model size: 118B total parameters, approximately 8B activated parameters per token
22
+ - Conversion: MLX-LM conversion using the Laguna architecture implementation
23
+ - Quantization: 6-bit affine quantization
24
+ - Quantization policy: Uniform 6-bit affine quantization
25
+ - Group size: 64
26
+ - Published MLX package size: 89.01 GiB
27
+
28
+ ## Benchmarks
29
+
30
+ No comparative benchmarks are available yet. This repository does not currently provide quality, speed, memory, or benchmark comparisons against the original weights or other quantizations.
31
+
32
+ ## License
33
+
34
+ This is a converted and quantized derivative of the original checkpoint. It retains the upstream [OpenMDW-1.1 license](https://huggingface.co/poolside/Laguna-S-2.1/blob/main/LICENSE.md) and applicable notices of origin.
chat_template.jinja ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {#- Iteration on laguna_glm_thinking_v8/chat_template.jinja -#}
2
+ {#- No formatting instructions -#}
3
+ {{- "〈|EOS|〉" -}}
4
+ {%- set enable_thinking = enable_thinking | default(true) -%}
5
+ {%- set add_generation_prompt = add_generation_prompt | default(false) -%}
6
+ {%- set preserve_thinking = preserve_thinking | default(false) -%}
7
+
8
+ {#- ───── header (system message) ───── -#}
9
+ {#- A caller-supplied system message with empty content opts out of the default below, producing no <system> block — used to train without a system message. -#}
10
+ {%- set system_message = "You are a helpful, conversationally-fluent assistant made by Poolside. You are here to be helpful to users through natural language conversations." -%}
11
+ {%- if messages and messages[0].role == "system" -%}
12
+ {%- set system_message = messages[0].content -%}
13
+ {%- set messages = messages[1:] -%}
14
+ {%- endif -%}
15
+
16
+ {%- set has_sys = system_message and system_message.strip() -%}
17
+ {%- if has_sys or tools or enable_thinking -%}
18
+ {{- "<system>" -}}
19
+
20
+ {%- if has_sys -%}
21
+ {{- system_message.rstrip() -}}
22
+ {%- if tools -%}{{- "\n\n" -}}{%- endif -%}
23
+ {%- endif -%}
24
+
25
+ {%- if tools -%}
26
+ {{- "### Tools\n\n" -}}
27
+ {{- "You may call functions to assist with the user query.\n" -}}
28
+ {{- "All available function signatures are listed below:\n" -}}
29
+ {{- "<available_tools>\n" -}}
30
+ {%- for tool in tools -%}
31
+ {{- (tool | tojson) ~ "\n" -}}
32
+ {%- endfor -%}
33
+ {{- "</available_tools>" -}}
34
+ {%- endif -%}
35
+
36
+ {{- "</system>\n" -}}
37
+ {%- endif -%}
38
+
39
+ {#- ───── main loop ───── -#}
40
+ {%- for message in messages -%}
41
+ {%- set content = message.content if message.content is string else "" -%}
42
+ {%- if message.role == "user" -%}
43
+ {{- "<user>" + content + "</user>\n" -}}
44
+ {%- elif message.role == "assistant" -%}
45
+ {%- generation -%}
46
+ {{- "<assistant>" -}}
47
+ {#- Extract reasoning content from message.reasoning (vLLM field name) or message.reasoning_content -#}
48
+ {%- set reasoning_content = '' -%}
49
+ {%- if message.reasoning is string -%}
50
+ {%- set reasoning_content = message.reasoning -%}
51
+ {%- elif message.reasoning_content is string -%}
52
+ {%- set reasoning_content = message.reasoning_content -%}
53
+ {%- endif -%}
54
+ {#- Display reasoning content for all messages if enable_thinking -#}
55
+ {%- if enable_thinking or preserve_thinking -%}
56
+ {{- '<think>' + reasoning_content + '</think>' -}}
57
+ {%- else -%}
58
+ {{- '</think>' -}}
59
+ {%- endif -%}
60
+ {#- Display main content (trailing newline only when no tool_calls follow) -#}
61
+ {%- if content -%}
62
+ {{- content -}}
63
+ {%- endif -%}
64
+ {%- if message.tool_calls -%}
65
+ {%- for tool_call in message.tool_calls -%}
66
+ {%- set function_data = tool_call.function -%}
67
+ {{- '<tool_call>' + function_data.name -}}
68
+ {%- set _args = function_data.arguments -%}
69
+ {%- for k, v in _args.items() -%}
70
+ {{- "<arg_key>" ~ k ~ "</arg_key>" -}}
71
+ {{- "<arg_value>" -}}{{- v | tojson(ensure_ascii=False) if v is not string else v -}}{{- "</arg_value>" -}}
72
+ {%- endfor -%}
73
+ {{- "</tool_call>" -}}
74
+ {%- endfor -%}
75
+ {%- endif -%}
76
+ {{- "</assistant>\n" -}}
77
+ {%- endgeneration -%}
78
+ {%- elif message.role == "tool" -%}
79
+ {{- "<tool_response>" + content + "</tool_response>\n" -}}
80
+ {%- elif message.role == "system" -%}
81
+ {#- Render additional system messages (the first one, if any, is handled separately in the header and was sliced off above) -#}
82
+ {{- "<system>" + content + "</system>\n" -}}
83
+ {%- endif -%}
84
+ {%- endfor -%}
85
+ {#- ───── generation prompt ───── -#}
86
+ {%- if add_generation_prompt -%}
87
+ {{- "<assistant>" -}}
88
+ {#- ───── Include reasoning mode directive ───── -#}
89
+ {%- if enable_thinking -%}
90
+ {{- '<think>' -}}
91
+ {%- else -%}
92
+ {{- '</think>' -}}
93
+ {%- endif -%}
94
+ {%- endif -%}
config.json ADDED
@@ -0,0 +1,272 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "LagunaForCausalLM"
4
+ ],
5
+ "attention_bias": false,
6
+ "attention_dropout": 0.0,
7
+ "auto_map": {
8
+ "AutoConfig": "configuration_laguna.LagunaConfig",
9
+ "AutoModelForCausalLM": "modeling_laguna.LagunaForCausalLM"
10
+ },
11
+ "bos_token_id": 2,
12
+ "decoder_sparse_step": 1,
13
+ "eos_token_id": [
14
+ 2,
15
+ 24
16
+ ],
17
+ "gating": "per-head",
18
+ "gating_types": [
19
+ "per_head",
20
+ "per_head",
21
+ "per_head",
22
+ "per_head",
23
+ "per_head",
24
+ "per_head",
25
+ "per_head",
26
+ "per_head",
27
+ "per_head",
28
+ "per_head",
29
+ "per_head",
30
+ "per_head",
31
+ "per_head",
32
+ "per_head",
33
+ "per_head",
34
+ "per_head",
35
+ "per_head",
36
+ "per_head",
37
+ "per_head",
38
+ "per_head",
39
+ "per_head",
40
+ "per_head",
41
+ "per_head",
42
+ "per_head",
43
+ "per_head",
44
+ "per_head",
45
+ "per_head",
46
+ "per_head",
47
+ "per_head",
48
+ "per_head",
49
+ "per_head",
50
+ "per_head",
51
+ "per_head",
52
+ "per_head",
53
+ "per_head",
54
+ "per_head",
55
+ "per_head",
56
+ "per_head",
57
+ "per_head",
58
+ "per_head",
59
+ "per_head",
60
+ "per_head",
61
+ "per_head",
62
+ "per_head",
63
+ "per_head",
64
+ "per_head",
65
+ "per_head",
66
+ "per_head"
67
+ ],
68
+ "head_dim": 128,
69
+ "hidden_size": 3072,
70
+ "intermediate_size": 12288,
71
+ "layer_types": [
72
+ "full_attention",
73
+ "sliding_attention",
74
+ "sliding_attention",
75
+ "sliding_attention",
76
+ "full_attention",
77
+ "sliding_attention",
78
+ "sliding_attention",
79
+ "sliding_attention",
80
+ "full_attention",
81
+ "sliding_attention",
82
+ "sliding_attention",
83
+ "sliding_attention",
84
+ "full_attention",
85
+ "sliding_attention",
86
+ "sliding_attention",
87
+ "sliding_attention",
88
+ "full_attention",
89
+ "sliding_attention",
90
+ "sliding_attention",
91
+ "sliding_attention",
92
+ "full_attention",
93
+ "sliding_attention",
94
+ "sliding_attention",
95
+ "sliding_attention",
96
+ "full_attention",
97
+ "sliding_attention",
98
+ "sliding_attention",
99
+ "sliding_attention",
100
+ "full_attention",
101
+ "sliding_attention",
102
+ "sliding_attention",
103
+ "sliding_attention",
104
+ "full_attention",
105
+ "sliding_attention",
106
+ "sliding_attention",
107
+ "sliding_attention",
108
+ "full_attention",
109
+ "sliding_attention",
110
+ "sliding_attention",
111
+ "sliding_attention",
112
+ "full_attention",
113
+ "sliding_attention",
114
+ "sliding_attention",
115
+ "sliding_attention",
116
+ "full_attention",
117
+ "sliding_attention",
118
+ "sliding_attention",
119
+ "sliding_attention"
120
+ ],
121
+ "max_position_embeddings": 1048576,
122
+ "mlp_layer_types": [
123
+ "dense",
124
+ "sparse",
125
+ "sparse",
126
+ "sparse",
127
+ "sparse",
128
+ "sparse",
129
+ "sparse",
130
+ "sparse",
131
+ "sparse",
132
+ "sparse",
133
+ "sparse",
134
+ "sparse",
135
+ "sparse",
136
+ "sparse",
137
+ "sparse",
138
+ "sparse",
139
+ "sparse",
140
+ "sparse",
141
+ "sparse",
142
+ "sparse",
143
+ "sparse",
144
+ "sparse",
145
+ "sparse",
146
+ "sparse",
147
+ "sparse",
148
+ "sparse",
149
+ "sparse",
150
+ "sparse",
151
+ "sparse",
152
+ "sparse",
153
+ "sparse",
154
+ "sparse",
155
+ "sparse",
156
+ "sparse",
157
+ "sparse",
158
+ "sparse",
159
+ "sparse",
160
+ "sparse",
161
+ "sparse",
162
+ "sparse",
163
+ "sparse",
164
+ "sparse",
165
+ "sparse",
166
+ "sparse",
167
+ "sparse",
168
+ "sparse",
169
+ "sparse",
170
+ "sparse"
171
+ ],
172
+ "mlp_only_layers": [
173
+ 0
174
+ ],
175
+ "model_type": "laguna",
176
+ "moe_apply_router_weight_on_input": false,
177
+ "moe_intermediate_size": 1024,
178
+ "moe_routed_scaling_factor": 2.5,
179
+ "moe_router_logit_softcapping": 0.0,
180
+ "norm_topk_prob": true,
181
+ "num_attention_heads": 48,
182
+ "num_attention_heads_per_layer": [
183
+ 48,
184
+ 72,
185
+ 72,
186
+ 72,
187
+ 48,
188
+ 72,
189
+ 72,
190
+ 72,
191
+ 48,
192
+ 72,
193
+ 72,
194
+ 72,
195
+ 48,
196
+ 72,
197
+ 72,
198
+ 72,
199
+ 48,
200
+ 72,
201
+ 72,
202
+ 72,
203
+ 48,
204
+ 72,
205
+ 72,
206
+ 72,
207
+ 48,
208
+ 72,
209
+ 72,
210
+ 72,
211
+ 48,
212
+ 72,
213
+ 72,
214
+ 72,
215
+ 48,
216
+ 72,
217
+ 72,
218
+ 72,
219
+ 48,
220
+ 72,
221
+ 72,
222
+ 72,
223
+ 48,
224
+ 72,
225
+ 72,
226
+ 72,
227
+ 48,
228
+ 72,
229
+ 72,
230
+ 72
231
+ ],
232
+ "num_experts": 256,
233
+ "num_experts_per_tok": 10,
234
+ "num_hidden_layers": 48,
235
+ "num_key_value_heads": 8,
236
+ "pad_token_id": 9,
237
+ "quantization": {
238
+ "group_size": 64,
239
+ "bits": 6,
240
+ "mode": "affine"
241
+ },
242
+ "quantization_config": {
243
+ "group_size": 64,
244
+ "bits": 6,
245
+ "mode": "affine"
246
+ },
247
+ "rms_norm_eps": 1e-06,
248
+ "rope_parameters": {
249
+ "full_attention": {
250
+ "rope_theta": 500000.0,
251
+ "rope_type": "yarn",
252
+ "factor": 128.0,
253
+ "original_max_position_embeddings": 8192,
254
+ "beta_slow": 1.0,
255
+ "beta_fast": 32.0,
256
+ "attention_factor": 1.4852030263919618,
257
+ "partial_rotary_factor": 0.5
258
+ },
259
+ "sliding_attention": {
260
+ "rope_type": "default",
261
+ "rope_theta": 10000.0,
262
+ "partial_rotary_factor": 1.0
263
+ }
264
+ },
265
+ "router_aux_loss_coef": 0.0,
266
+ "shared_expert_intermediate_size": 1024,
267
+ "sliding_window": 512,
268
+ "tie_word_embeddings": false,
269
+ "torch_dtype": "bfloat16",
270
+ "use_cache": true,
271
+ "vocab_size": 100352
272
+ }
configuration_laguna.py ADDED
@@ -0,0 +1,249 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ruff: noqa
2
+ # Copyright 2025 Poolside and the HuggingFace Inc. team. All rights reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ from transformers.configuration_utils import PreTrainedConfig
16
+ from transformers.modeling_rope_utils import RopeParameters
17
+ from transformers.utils.import_utils import is_causal_conv1d_available, is_flash_linear_attention_available
18
+
19
+
20
+ class LagunaConfig(PreTrainedConfig):
21
+ r"""
22
+ Configuration class for Laguna model.
23
+
24
+ Laguna is Poolside's MoE architecture with:
25
+ - Attention output gating (softplus gate)
26
+ - Sigmoid routing instead of softmax
27
+ - No QKV bias
28
+ - Explicit head_dim parameter
29
+
30
+ Args:
31
+ head_dim (`int`, *optional*, defaults to 128):
32
+ Dimension of attention heads. Laguna uses explicit head_dim rather than
33
+ computing it from hidden_size // num_attention_heads.
34
+ qkv_bias (`bool`, *optional*, defaults to `False`):
35
+ Whether to add bias to QKV projections. Laguna uses no QKV bias.
36
+ attention_bias (`bool`, *optional*, defaults to `False`):
37
+ Whether to add bias to attention output projection. Laguna uses no attention bias.
38
+ gating (`bool` or `str`, *optional*, defaults to `True`):
39
+ Attention output gating mode. When ``True`` or ``"per-element"`` a g_proj
40
+ linear layer with output size ``num_attention_heads * head_dim`` is added
41
+ and ``attn_output = attn_output * softplus(g_proj(x))``. When ``"per-head"``
42
+ g_proj has output size ``num_attention_heads`` and the gate broadcasts across
43
+ ``head_dim``. When ``False`` no gating is applied.
44
+ partial_rotary_factor (`float`, *optional*):
45
+ Fraction of head_dim to apply rotary embeddings to. When set, this value is
46
+ injected into ``rope_parameters`` (and ``swa_rope_parameters``) if not already
47
+ specified there. When ``None`` the default behaviour of the rope implementation
48
+ is used (typically full rotary).
49
+ num_attention_heads_per_layer (`list[int]`, *optional*):
50
+ Optional per-layer override for ``num_attention_heads``. When provided the list
51
+ length must equal ``num_hidden_layers`` and each entry is the head count used by
52
+ that layer. When ``None`` every layer uses ``num_attention_heads``.
53
+ vocab_size (`int`, *optional*, defaults to 100352):
54
+ Vocabulary size of the Laguna model.
55
+ hidden_size (`int`, *optional*, defaults to 2048):
56
+ Dimension of the hidden representations.
57
+ intermediate_size (`int`, *optional*, defaults to 8192):
58
+ Dimension of the MLP representations for dense layers.
59
+ num_hidden_layers (`int`, *optional*, defaults to 48):
60
+ Number of hidden layers in the Transformer.
61
+ num_attention_heads (`int`, *optional*, defaults to 32):
62
+ Number of attention heads.
63
+ num_key_value_heads (`int`, *optional*, defaults to 8):
64
+ Number of key-value heads for GQA.
65
+ max_position_embeddings (`int`, *optional*, defaults to 4096):
66
+ Maximum sequence length.
67
+ rms_norm_eps (`float`, *optional*, defaults to 1e-6):
68
+ Epsilon for RMSNorm layers.
69
+ sliding_window (`int`, *optional*):
70
+ Sliding window attention size. Used by layers whose type in ``layer_types``
71
+ is ``"sliding_attention"``. When ``None``, all layers use full attention.
72
+ layer_types (`list[str]`, *optional*):
73
+ Per-layer attention type. Each element should be ``"sliding_attention"`` or
74
+ ``"full_attention"``. Length must equal ``num_hidden_layers``. When ``None``,
75
+ all layers default to global attention.
76
+ swa_attention_sink_enabled (`bool`, *optional*, defaults to `False`):
77
+ Whether to enable learnable attention sinks on sliding-window attention layers.
78
+ When enabled, a per-head bias parameter is added that allows the model to attend
79
+ to position 0 even when it falls outside the sliding window.
80
+ swa_rope_parameters (`RopeParameters`, *optional*):
81
+ Separate RoPE configuration for sliding-window attention layers. When ``None``,
82
+ SWA layers use the same RoPE as global attention layers.
83
+ num_experts (`int`, *optional*, defaults to 256):
84
+ Number of routed experts.
85
+ num_experts_per_tok (`int`, *optional*, defaults to 16):
86
+ Number of experts selected per token (top-k).
87
+ moe_intermediate_size (`int`, *optional*, defaults to 1024):
88
+ Intermediate size of routed experts.
89
+ shared_expert_intermediate_size (`int`, *optional*, defaults to 1024):
90
+ Intermediate size of the shared expert.
91
+ norm_topk_prob (`bool`, *optional*, defaults to `True`):
92
+ Whether to normalize top-k routing probabilities.
93
+ decoder_sparse_step (`int`, *optional*, defaults to 1):
94
+ Frequency of MoE layers (1 = every layer is MoE after mlp_only_layers).
95
+ mlp_only_layers (`list[int]`, *optional*, defaults to `[0]`):
96
+ Layer indices that use dense MLP instead of MoE.
97
+ router_aux_loss_coef (`float`, *optional*, defaults to 0.001):
98
+ Auxiliary loss coefficient for load balancing.
99
+ moe_routed_scaling_factor (`float`, *optional*, defaults to 1.0):
100
+ Scalar multiplier applied to the routed-expert output before combining with the
101
+ shared-expert output.
102
+ moe_apply_router_weight_on_input (`bool`, *optional*, defaults to `False`):
103
+ When ``True`` the top-k routing weights are multiplied into each expert's input
104
+ rather than its output. Matches the numerical form used by the trained checkpoint.
105
+ moe_router_logit_softcapping (`float`, *optional*, defaults to 0.0):
106
+ Optional soft-capping value ``c`` applied to router logits as
107
+ ``x = tanh(x / c) * c`` before sigmoid + top-k. Disabled when ``0``.
108
+ rope_parameters (`RopeParameters`, *optional*):
109
+ RoPE configuration. Defaults to rope_theta=500000.0.
110
+ """
111
+
112
+ model_type = "laguna"
113
+ keys_to_ignore_at_inference = ["past_key_values"]
114
+ # PreTrainedConfig in transformers v5 no longer auto-declares these; subclasses
115
+ # opt in by providing class-level annotations with defaults.
116
+ pad_token_id: int | None = None
117
+ bos_token_id: int | None = None
118
+ eos_token_id: int | list[int] | None = None
119
+ base_model_tp_plan = {
120
+ "layers.*.self_attn.q_proj": "colwise",
121
+ "layers.*.self_attn.k_proj": "colwise",
122
+ "layers.*.self_attn.v_proj": "colwise",
123
+ "layers.*.self_attn.g_proj": "colwise", # Laguna-specific gating projection
124
+ "layers.*.self_attn.o_proj": "rowwise",
125
+ "layers.*.mlp.gate_proj": "colwise",
126
+ "layers.*.mlp.up_proj": "colwise",
127
+ "layers.*.mlp.down_proj": "rowwise",
128
+ }
129
+ base_model_pp_plan = {
130
+ "embed_tokens": (["input_ids"], ["inputs_embeds"]),
131
+ "layers": (["hidden_states", "attention_mask"], ["hidden_states"]),
132
+ "norm": (["hidden_states"], ["hidden_states"]),
133
+ }
134
+
135
+ def __init__(
136
+ self,
137
+ vocab_size: int = 100352,
138
+ hidden_size: int = 2048,
139
+ intermediate_size: int = 8192,
140
+ num_hidden_layers: int = 48,
141
+ num_attention_heads: int = 32,
142
+ num_key_value_heads: int = 8,
143
+ head_dim: int = 128,
144
+ qkv_bias: bool = False,
145
+ attention_bias: bool = False,
146
+ gating: bool | str = True,
147
+ hidden_act: str = "silu",
148
+ max_position_embeddings: int = 4096,
149
+ initializer_range: float = 0.02,
150
+ rms_norm_eps: float = 1e-6,
151
+ use_cache: bool = True,
152
+ tie_word_embeddings: bool = False,
153
+ rope_parameters: RopeParameters | dict[str, RopeParameters] | None = None,
154
+ partial_rotary_factor: float | None = None,
155
+ attention_dropout: float = 0.0,
156
+ sliding_window: int | None = None,
157
+ layer_types: list[str] | None = None,
158
+ num_attention_heads_per_layer: list[int] | None = None,
159
+ swa_attention_sink_enabled: bool = False,
160
+ swa_rope_parameters: RopeParameters | None = None,
161
+ num_experts: int = 256,
162
+ num_experts_per_tok: int = 16,
163
+ moe_intermediate_size: int = 1024,
164
+ shared_expert_intermediate_size: int = 1024,
165
+ norm_topk_prob: bool = True,
166
+ decoder_sparse_step: int = 1,
167
+ mlp_only_layers: list[int] | None = None,
168
+ router_aux_loss_coef: float = 0.001,
169
+ moe_routed_scaling_factor: float = 1.0,
170
+ moe_apply_router_weight_on_input: bool = False,
171
+ moe_router_logit_softcapping: float = 0.0,
172
+ output_router_logits: bool = False,
173
+ **kwargs,
174
+ ):
175
+ # Default mlp_only_layers: first layer is dense (moe_first_k_dense_replace=1)
176
+ if mlp_only_layers is None:
177
+ mlp_only_layers = [0]
178
+
179
+ # Default layer_types: all layers use full attention (Laguna-M). Laguna-XS
180
+ # ships an explicit list with a mix of "full_attention" and "sliding_attention".
181
+ # Downstream mask builders (``create_masks_for_generate``) iterate
182
+ # ``layer_types``, so it must be a list — not left as ``None``.
183
+ if layer_types is None:
184
+ layer_types = ["full_attention"] * num_hidden_layers
185
+
186
+ # Default rope_parameters with Laguna's theta
187
+ if rope_parameters is None:
188
+ rope_parameters = {"rope_type": "default", "rope_theta": 500000.0}
189
+
190
+ # config.json stores SWA rope nested in rope_parameters["sliding_attention"]
191
+ # and carries no top-level swa_rope_parameters. Derive it here, else the
192
+ # sliding-window layers silently reuse the full-attention rope.
193
+ if swa_rope_parameters is None and isinstance(rope_parameters, dict):
194
+ swa_rope_parameters = rope_parameters.get("sliding_attention")
195
+
196
+ # If ``partial_rotary_factor`` is set at the top level, inject it into any
197
+ # rope dict that does not already carry one so the rotary embedding picks
198
+ # it up consistently for both full-attention and SWA layers.
199
+ if partial_rotary_factor is not None:
200
+ if isinstance(rope_parameters, dict) and "partial_rotary_factor" not in rope_parameters:
201
+ rope_parameters = {**rope_parameters, "partial_rotary_factor": partial_rotary_factor}
202
+ if isinstance(swa_rope_parameters, dict) and "partial_rotary_factor" not in swa_rope_parameters:
203
+ swa_rope_parameters = {
204
+ **swa_rope_parameters,
205
+ "partial_rotary_factor": partial_rotary_factor,
206
+ }
207
+
208
+ self.vocab_size = vocab_size
209
+ self.hidden_size = hidden_size
210
+ self.intermediate_size = intermediate_size
211
+ self.num_hidden_layers = num_hidden_layers
212
+ self.num_attention_heads = num_attention_heads
213
+ self.num_key_value_heads = num_key_value_heads
214
+ self.head_dim = head_dim
215
+ self.qkv_bias = qkv_bias
216
+ self.attention_bias = attention_bias
217
+ self.gating = gating
218
+ self.hidden_act = hidden_act
219
+ self.max_position_embeddings = max_position_embeddings
220
+ self.initializer_range = initializer_range
221
+ self.rms_norm_eps = rms_norm_eps
222
+ self.use_cache = use_cache
223
+ self.rope_parameters = rope_parameters
224
+ self.partial_rotary_factor = partial_rotary_factor
225
+ self.attention_dropout = attention_dropout
226
+ # Sliding window attention arguments
227
+ self.sliding_window = sliding_window
228
+ self.layer_types = layer_types
229
+ self.num_attention_heads_per_layer = num_attention_heads_per_layer
230
+ self.swa_attention_sink_enabled = swa_attention_sink_enabled
231
+ self.swa_rope_parameters = swa_rope_parameters
232
+ # MoE arguments
233
+ self.num_experts = num_experts
234
+ self.num_experts_per_tok = num_experts_per_tok
235
+ self.moe_intermediate_size = moe_intermediate_size
236
+ self.shared_expert_intermediate_size = shared_expert_intermediate_size
237
+ self.norm_topk_prob = norm_topk_prob
238
+ self.decoder_sparse_step = decoder_sparse_step
239
+ self.mlp_only_layers = mlp_only_layers
240
+ self.router_aux_loss_coef = router_aux_loss_coef
241
+ self.moe_routed_scaling_factor = moe_routed_scaling_factor
242
+ self.moe_apply_router_weight_on_input = moe_apply_router_weight_on_input
243
+ self.moe_router_logit_softcapping = moe_router_logit_softcapping
244
+ self.output_router_logits = output_router_logits
245
+
246
+ super().__init__(tie_word_embeddings=tie_word_embeddings, **kwargs)
247
+
248
+
249
+ __all__ = ["LagunaConfig"]
generation_config.json ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token_id": 2,
3
+ "do_sample": true,
4
+ "eos_token_id": [
5
+ 2,
6
+ 24
7
+ ],
8
+ "pad_token_id": 9,
9
+ "temperature": 1.0,
10
+ "top_p": 1.0,
11
+ "min_p": 0.0,
12
+ "top_k": 20,
13
+ "tool_call_parser": "poolside_v1",
14
+ "reasoning_parser": "poolside_v1",
15
+ "default_chat_template_kwargs": {
16
+ "enable_thinking": true
17
+ },
18
+ "speculative_config": {
19
+ "method": "dflash",
20
+ "source": "huggingface",
21
+ "model": "poolside/Laguna-S-2.1-DFlash",
22
+ "num_speculative_tokens": 15
23
+ }
24
+ }
model.safetensors.index.json ADDED
The diff for this file is too large to render. See raw diff
 
modeling_laguna.py ADDED
@@ -0,0 +1,886 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ruff: noqa
2
+ # Copyright 2025 Poolside and the HuggingFace Inc. team. All rights reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from collections.abc import Callable
17
+
18
+ import torch
19
+ import torch.nn.functional as F
20
+ from torch import nn
21
+
22
+ from transformers.activations import ACT2FN
23
+ from transformers.cache_utils import Cache
24
+ from transformers.integrations import use_experts_implementation, use_kernelized_func
25
+ from transformers.modeling_flash_attention_utils import FlashAttentionKwargs
26
+ from transformers.modeling_layers import GradientCheckpointingLayer
27
+ from transformers.modeling_outputs import MoeModelOutputWithPast
28
+ from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS
29
+ from transformers.processing_utils import Unpack
30
+ from transformers.utils import auto_docstring, can_return_tuple, is_grouped_mm_available
31
+ from transformers.utils.generic import TransformersKwargs, merge_with_config_defaults
32
+ from transformers.utils.output_capturing import OutputRecorder, capture_outputs
33
+ from transformers.cache_utils import DynamicCache
34
+ from transformers.generation import GenerationMixin
35
+ from transformers.integrations import use_kernel_forward_from_hub
36
+ from transformers.masking_utils import create_causal_mask
37
+ from transformers.modeling_rope_utils import ROPE_INIT_FUNCTIONS, dynamic_rope_update
38
+ from transformers.modeling_utils import PreTrainedModel
39
+ from transformers.utils.generic import maybe_autocast
40
+ from .configuration_laguna import LagunaConfig
41
+
42
+ from transformers import initialization as init
43
+ from transformers.masking_utils import create_sliding_window_causal_mask
44
+ from transformers.modeling_outputs import MoeCausalLMOutputWithPast
45
+ from transformers.utils.import_utils import is_causal_conv1d_available, is_flash_linear_attention_available
46
+
47
+
48
+ @use_kernel_forward_from_hub("RMSNorm")
49
+ class LagunaRMSNorm(nn.Module):
50
+ def __init__(self, hidden_size, eps: float = 1e-6) -> None:
51
+ """
52
+ LagunaRMSNorm is equivalent to T5LayerNorm
53
+ """
54
+ super().__init__()
55
+ self.weight = nn.Parameter(torch.ones(hidden_size))
56
+ self.variance_epsilon = eps
57
+
58
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
59
+ input_dtype = hidden_states.dtype
60
+ hidden_states = hidden_states.to(torch.float32)
61
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
62
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
63
+ return self.weight * hidden_states.to(input_dtype)
64
+
65
+ def extra_repr(self):
66
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
67
+
68
+
69
+ class LagunaRotaryEmbedding(nn.Module):
70
+ inv_freq: torch.Tensor # fix linting for `register_buffer`
71
+
72
+ def __init__(self, config: LagunaConfig, device=None):
73
+ super().__init__()
74
+ self.max_seq_len_cached = config.max_position_embeddings
75
+ self.original_max_seq_len = config.max_position_embeddings
76
+
77
+ self.config = config
78
+
79
+ self.rope_type = self.config.rope_parameters["rope_type"]
80
+ rope_init_fn: Callable = self.compute_default_rope_parameters
81
+ if self.rope_type != "default":
82
+ rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
83
+ inv_freq, self.attention_scaling = rope_init_fn(self.config, device)
84
+
85
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
86
+ self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
87
+
88
+ @staticmethod
89
+ def compute_default_rope_parameters(config, device=None, seq_len=None) -> tuple["torch.Tensor", float]:
90
+ """
91
+ Computes the inverse frequencies according to the original RoPE implementation
92
+ Args:
93
+ config ([`~transformers.PreTrainedConfig`]):
94
+ The model configuration.
95
+ device (`torch.device`):
96
+ The device to use for initialization of the inverse frequencies.
97
+ seq_len (`int`, *optional*):
98
+ The current sequence length. Unused for this type of RoPE.
99
+ Returns:
100
+ Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
101
+ post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
102
+ """
103
+ base = config.rope_parameters["rope_theta"]
104
+ head_dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
105
+ partial = config.rope_parameters.get("partial_rotary_factor", 1.0)
106
+ dim = int(head_dim * partial)
107
+ inv_freq = 1.0 / (
108
+ base ** (torch.arange(0, dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / dim)
109
+ )
110
+ return inv_freq, 1.0
111
+
112
+ @torch.no_grad()
113
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
114
+ def forward(self, x, position_ids):
115
+ inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1).to(x.device)
116
+ position_ids_expanded = position_ids[:, None, :].float()
117
+
118
+ device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
119
+ with maybe_autocast(device_type=device_type, enabled=False): # Force float32
120
+ freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(1, 2)
121
+ emb = torch.cat((freqs, freqs), dim=-1)
122
+ cos = emb.cos() * self.attention_scaling
123
+ sin = emb.sin() * self.attention_scaling
124
+
125
+ return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
126
+
127
+
128
+ class LagunaMLP(nn.Module):
129
+ def __init__(self, config, intermediate_size=None):
130
+ super().__init__()
131
+ self.config = config
132
+ self.hidden_size = config.hidden_size
133
+ self.intermediate_size = config.intermediate_size if intermediate_size is None else intermediate_size
134
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
135
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
136
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
137
+ self.act_fn = ACT2FN[config.hidden_act]
138
+
139
+ def forward(self, x):
140
+ down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
141
+ return down_proj
142
+
143
+
144
+ class LagunaTopKRouter(nn.Module):
145
+ """Laguna MoE router using sigmoid scoring (not softmax).
146
+
147
+ Supports optional router-logit soft-capping and auxiliary-loss-free load
148
+ balancing (arXiv:2408.15664): the per-expert bias ``e_score_correction_bias``
149
+ is added to selection scores but the returned routing weights remain unbiased.
150
+ The bias lives on the router so accelerate's per-module hooks can co-locate it
151
+ with the gate — moving it to the experts module would cross a hook boundary
152
+ and leave the bias on meta under ``device_map="auto"`` / CPU-offload.
153
+ """
154
+
155
+ def __init__(self, config):
156
+ super().__init__()
157
+ self.top_k = config.num_experts_per_tok
158
+ self.num_experts = config.num_experts
159
+ self.norm_topk_prob = config.norm_topk_prob
160
+ self.hidden_dim = config.hidden_size
161
+ self.weight = nn.Parameter(torch.zeros(self.num_experts, self.hidden_dim))
162
+ # Zero-initialised so inference on checkpoints that don't ship the bias
163
+ # is a no-op. ``_checkpoint_conversion_mapping`` below remaps the
164
+ # ``mlp.experts.e_score_correction_bias`` key from vLLM-trained
165
+ # checkpoints onto this attribute.
166
+ self.e_score_correction_bias = nn.Parameter(torch.zeros(config.num_experts), requires_grad=False)
167
+ self.router_logit_softcapping = float(getattr(config, "moe_router_logit_softcapping", 0.0) or 0.0)
168
+
169
+ def forward(
170
+ self,
171
+ hidden_states: torch.Tensor,
172
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
173
+ hidden_states = hidden_states.reshape(-1, self.hidden_dim)
174
+ router_logits = F.linear(hidden_states, self.weight).float()
175
+ if self.router_logit_softcapping > 0.0:
176
+ router_logits = torch.tanh(router_logits / self.router_logit_softcapping) * self.router_logit_softcapping
177
+ routing_scores = torch.sigmoid(router_logits)
178
+ scores_for_selection = routing_scores + self.e_score_correction_bias.to(routing_scores.dtype)
179
+ _, selected_experts = torch.topk(scores_for_selection, self.top_k, dim=-1)
180
+ routing_weights = routing_scores.gather(-1, selected_experts)
181
+ if self.norm_topk_prob:
182
+ routing_weights = routing_weights / routing_weights.sum(dim=-1, keepdim=True)
183
+ routing_weights = routing_weights.to(hidden_states.dtype)
184
+ return router_logits, routing_weights, selected_experts
185
+
186
+
187
+ @use_experts_implementation
188
+ class LagunaExperts(nn.Module):
189
+ """Fused expert weights as 3D tensors for batched execution."""
190
+
191
+ def __init__(self, config):
192
+ super().__init__()
193
+ self.num_experts = config.num_experts
194
+ self.hidden_dim = config.hidden_size
195
+ self.intermediate_dim = config.moe_intermediate_size
196
+ self.gate_up_proj = nn.Parameter(torch.empty(self.num_experts, 2 * self.intermediate_dim, self.hidden_dim))
197
+ self.down_proj = nn.Parameter(torch.empty(self.num_experts, self.hidden_dim, self.intermediate_dim))
198
+ self.act_fn = ACT2FN[config.hidden_act]
199
+
200
+ def forward(
201
+ self,
202
+ hidden_states: torch.Tensor,
203
+ top_k_index: torch.Tensor,
204
+ top_k_weights: torch.Tensor,
205
+ ) -> torch.Tensor:
206
+ final_hidden_states = torch.zeros_like(hidden_states)
207
+ with torch.no_grad():
208
+ expert_mask = F.one_hot(top_k_index, num_classes=self.num_experts)
209
+ expert_mask = expert_mask.permute(2, 1, 0)
210
+ expert_hit = torch.greater(expert_mask.sum(dim=(-1, -2)), 0).nonzero()
211
+
212
+ for expert_idx in expert_hit:
213
+ expert_idx = expert_idx[0]
214
+ if expert_idx == self.num_experts:
215
+ continue
216
+ top_k_pos, token_idx = torch.where(expert_mask[expert_idx])
217
+ current_state = hidden_states[token_idx]
218
+ gate, up = F.linear(current_state, self.gate_up_proj[expert_idx]).chunk(2, dim=-1)
219
+ current_hidden_states = self.act_fn(gate) * up
220
+ current_hidden_states = F.linear(current_hidden_states, self.down_proj[expert_idx])
221
+ current_hidden_states = current_hidden_states * top_k_weights[token_idx, top_k_pos, None]
222
+ final_hidden_states.index_add_(0, token_idx, current_hidden_states.to(final_hidden_states.dtype))
223
+
224
+ return final_hidden_states
225
+
226
+
227
+ class LagunaSparseMoeBlock(nn.Module):
228
+ """Laguna MoE block using sigmoid router, fused expert tensors, and a shared expert."""
229
+
230
+ def __init__(self, config):
231
+ super().__init__()
232
+ self.num_experts = config.num_experts
233
+ self.routed_scaling_factor = float(getattr(config, "moe_routed_scaling_factor", 1.0))
234
+ # ``moe_apply_router_weight_on_input=True`` would require scaling each expert's
235
+ # input (rather than its output) by the routing weight. Supporting it cleanly
236
+ # alongside the fused experts kernels (``grouped_mm`` / ``batched_mm``) is future
237
+ # work; for now we fail loudly so a checkpoint that needs it can't silently
238
+ # diverge from its numerical form.
239
+ if getattr(config, "moe_apply_router_weight_on_input", False):
240
+ raise NotImplementedError(
241
+ "moe_apply_router_weight_on_input=True is not yet supported in the "
242
+ "transformers implementation of Laguna."
243
+ )
244
+ self.gate = LagunaTopKRouter(config)
245
+ self.experts = LagunaExperts(config)
246
+ self.shared_expert = LagunaMLP(config, intermediate_size=config.shared_expert_intermediate_size)
247
+
248
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
249
+ batch_size, sequence_length, hidden_dim = hidden_states.shape
250
+ hidden_states = hidden_states.view(-1, hidden_dim)
251
+
252
+ shared_expert_output = self.shared_expert(hidden_states)
253
+ _, routing_weights, selected_experts = self.gate(hidden_states)
254
+ expert_output = self.experts(hidden_states, selected_experts, routing_weights)
255
+ if self.routed_scaling_factor != 1.0:
256
+ expert_output = expert_output * self.routed_scaling_factor
257
+
258
+ expert_output = expert_output + shared_expert_output
259
+ expert_output = expert_output.reshape(batch_size, sequence_length, hidden_dim)
260
+ return expert_output
261
+
262
+
263
+ def rotate_half(x):
264
+ """Rotates half the hidden dims of the input."""
265
+ x1 = x[..., : x.shape[-1] // 2]
266
+ x2 = x[..., x.shape[-1] // 2 :]
267
+ return torch.cat((-x2, x1), dim=-1)
268
+
269
+
270
+ # Adapted from transformers.models.glm.modular_glm.apply_rotary_pos_emb
271
+ def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
272
+ """Applies Rotary Position Embedding to the query and key tensors.
273
+
274
+ Removes the interleaving of cos and sin from GLM
275
+
276
+ Args:
277
+ q (`torch.Tensor`): The query tensor.
278
+ k (`torch.Tensor`): The key tensor.
279
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
280
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
281
+ unsqueeze_dim (`int`, *optional*, defaults to 1):
282
+ The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
283
+ sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
284
+ that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
285
+ k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
286
+ cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
287
+ the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
288
+ Returns:
289
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
290
+ """
291
+ cos = cos.unsqueeze(unsqueeze_dim)
292
+ sin = sin.unsqueeze(unsqueeze_dim)
293
+
294
+ # Keep half or full tensor for later concatenation
295
+ rotary_dim = cos.shape[-1]
296
+ q_rot, q_pass = q[..., :rotary_dim], q[..., rotary_dim:]
297
+ k_rot, k_pass = k[..., :rotary_dim], k[..., rotary_dim:]
298
+
299
+ # Apply rotary embeddings on the first half or full tensor
300
+ q_embed = (q_rot * cos) + (rotate_half(q_rot) * sin)
301
+ k_embed = (k_rot * cos) + (rotate_half(k_rot) * sin)
302
+
303
+ # Concatenate back to full shape
304
+ q_embed = torch.cat([q_embed, q_pass], dim=-1)
305
+ k_embed = torch.cat([k_embed, k_pass], dim=-1)
306
+ return q_embed, k_embed
307
+
308
+
309
+ def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
310
+ """
311
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
312
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
313
+ """
314
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
315
+ if n_rep == 1:
316
+ return hidden_states
317
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
318
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
319
+
320
+
321
+ def eager_attention_forward(
322
+ module: nn.Module,
323
+ query: torch.Tensor,
324
+ key: torch.Tensor,
325
+ value: torch.Tensor,
326
+ attention_mask: torch.Tensor | None,
327
+ scaling: float,
328
+ dropout: float = 0.0,
329
+ **kwargs: Unpack[TransformersKwargs],
330
+ ):
331
+ key_states = repeat_kv(key, module.num_key_value_groups)
332
+ value_states = repeat_kv(value, module.num_key_value_groups)
333
+
334
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
335
+ if attention_mask is not None:
336
+ attn_weights = attn_weights + attention_mask
337
+
338
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
339
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
340
+ attn_output = torch.matmul(attn_weights, value_states)
341
+ attn_output = attn_output.transpose(1, 2).contiguous()
342
+
343
+ return attn_output, attn_weights
344
+
345
+
346
+ # Laguna attention is identical to Qwen2MoE attention except:
347
+ # - No QKV bias
348
+ # - Explicit head_dim from config
349
+ # - Output gating: attn_output = attn_output * softplus(g_proj(hidden_states)) (optional)
350
+ # - Per-layer sliding window attention with optional attention sinks
351
+ @use_kernelized_func(apply_rotary_pos_emb)
352
+ class LagunaAttention(nn.Module):
353
+ def __init__(self, config: LagunaConfig, layer_idx: int, num_heads: int | None = None):
354
+ super().__init__()
355
+ self.config = config
356
+ self.layer_idx = layer_idx
357
+ self.head_dim = config.head_dim
358
+ # Allow the caller (decoder layer) to supply a per-layer head count; fall back
359
+ # to config.num_attention_heads when not provided.
360
+ self.num_heads = num_heads if num_heads is not None else config.num_attention_heads
361
+ self.num_key_value_groups = self.num_heads // config.num_key_value_heads
362
+ self.scaling = self.head_dim**-0.5
363
+ self.attention_dropout = config.attention_dropout
364
+ self.is_causal = True
365
+
366
+ # Per-layer sliding window (follows Gemma2/Cohere2 convention)
367
+ layer_types = getattr(config, "layer_types", None)
368
+ if layer_types is not None:
369
+ self.is_sliding = layer_types[layer_idx] == "sliding_attention"
370
+ self.sliding_window = config.sliding_window if self.is_sliding else None
371
+ else:
372
+ self.is_sliding = False
373
+ self.sliding_window = None
374
+
375
+ # Laguna: no QKV bias, explicit head_dim
376
+ self.q_proj = nn.Linear(config.hidden_size, self.num_heads * config.head_dim, bias=False)
377
+ self.k_proj = nn.Linear(config.hidden_size, config.num_key_value_heads * config.head_dim, bias=False)
378
+ self.v_proj = nn.Linear(config.hidden_size, config.num_key_value_heads * config.head_dim, bias=False)
379
+ self.o_proj = nn.Linear(self.num_heads * config.head_dim, config.hidden_size, bias=False)
380
+
381
+ # Laguna-specific: optional gating projection.
382
+ # ``gating`` may be:
383
+ # - True / "per-element": one gate per (head, head_dim) channel
384
+ # - "per-head": one gate per head, broadcast across head_dim
385
+ # - False: no gating
386
+ gating = getattr(config, "gating", True)
387
+ self.gating = bool(gating)
388
+ self.gate_per_head = gating == "per-head"
389
+ if self.gating:
390
+ g_out = self.num_heads if self.gate_per_head else self.num_heads * config.head_dim
391
+ self.g_proj = nn.Linear(config.hidden_size, g_out, bias=False)
392
+
393
+ # Attention sinks (learnable per-head bias for SWA layers)
394
+ if self.is_sliding and getattr(config, "swa_attention_sink_enabled", False):
395
+ self.sink = nn.Parameter(torch.zeros(self.num_heads))
396
+
397
+ # QK normalization (RMSNorm applied per-head after reshape, before RoPE)
398
+ self.q_norm = LagunaRMSNorm(config.head_dim, eps=config.rms_norm_eps)
399
+ self.k_norm = LagunaRMSNorm(config.head_dim, eps=config.rms_norm_eps)
400
+
401
+ def forward(
402
+ self,
403
+ hidden_states: torch.Tensor,
404
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
405
+ attention_mask: torch.Tensor | None,
406
+ past_key_values: Cache | None = None,
407
+ **kwargs: Unpack[FlashAttentionKwargs],
408
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
409
+ input_shape = hidden_states.shape[:-1]
410
+ hidden_shape = (*input_shape, -1, self.head_dim)
411
+
412
+ query_states = self.q_proj(hidden_states)
413
+ key_states = self.k_proj(hidden_states)
414
+ value_states = self.v_proj(hidden_states)
415
+
416
+ query_states = query_states.view(hidden_shape).transpose(1, 2)
417
+ key_states = key_states.view(hidden_shape).transpose(1, 2)
418
+ value_states = value_states.view(hidden_shape).transpose(1, 2)
419
+
420
+ # QK normalization (applied per-head before RoPE)
421
+ query_states = self.q_norm(query_states)
422
+ key_states = self.k_norm(key_states)
423
+
424
+ cos, sin = position_embeddings
425
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
426
+
427
+ if past_key_values is not None:
428
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
429
+
430
+ # ``attention_mask`` here is already the correct mask for this layer type —
431
+ # ``LagunaModel.forward`` builds separate full-attention and sliding-attention
432
+ # masks (using ``create_causal_mask`` / ``create_sliding_window_causal_mask``)
433
+ # and the decoder layer passes the right one in.
434
+ attention_interface: Callable = eager_attention_forward
435
+ if self.config._attn_implementation != "eager":
436
+ attention_interface = ALL_ATTENTION_FUNCTIONS[self.config._attn_implementation]
437
+
438
+ attn_output, attn_weights = attention_interface(
439
+ self,
440
+ query_states,
441
+ key_states,
442
+ value_states,
443
+ attention_mask,
444
+ dropout=0.0 if not self.training else self.attention_dropout,
445
+ scaling=self.scaling,
446
+ **kwargs,
447
+ )
448
+
449
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
450
+
451
+ # Laguna-specific: apply gating BEFORE o_proj (optional)
452
+ if self.gating:
453
+ gate = F.softplus(self.g_proj(hidden_states).float()).to(attn_output.dtype)
454
+ if self.gate_per_head:
455
+ # gate: [..., num_heads]; broadcast across head_dim
456
+ attn_shape = attn_output.shape
457
+ attn_output = (
458
+ attn_output.view(*attn_shape[:-1], self.num_heads, self.head_dim) * gate.unsqueeze(-1)
459
+ ).view(attn_shape)
460
+ else:
461
+ attn_output = attn_output * gate
462
+
463
+ attn_output = self.o_proj(attn_output)
464
+
465
+ return attn_output, attn_weights
466
+
467
+
468
+ class LagunaDecoderLayer(GradientCheckpointingLayer):
469
+ """Laguna decoder layer with gated attention and sigmoid-routed MoE."""
470
+
471
+ def __init__(self, config: LagunaConfig, layer_idx: int):
472
+ super().__init__()
473
+ per_layer_heads = getattr(config, "num_attention_heads_per_layer", None)
474
+ layer_num_heads = per_layer_heads[layer_idx] if per_layer_heads is not None else config.num_attention_heads
475
+ # Layer type drives mask and position-embedding dispatch in ``LagunaModel.forward``.
476
+ layer_types = getattr(config, "layer_types", None)
477
+ self.attention_type = layer_types[layer_idx] if layer_types is not None else "full_attention"
478
+ self.self_attn = LagunaAttention(config, layer_idx, num_heads=layer_num_heads)
479
+ # Use MoE or dense MLP based on layer configuration
480
+ if (layer_idx not in config.mlp_only_layers) and (
481
+ config.num_experts > 0 and (layer_idx + 1) % config.decoder_sparse_step == 0
482
+ ):
483
+ self.mlp = LagunaSparseMoeBlock(config)
484
+ else:
485
+ self.mlp = LagunaMLP(config, intermediate_size=config.intermediate_size)
486
+ self.input_layernorm = LagunaRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
487
+ self.post_attention_layernorm = LagunaRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
488
+ self.hidden_size = config.hidden_size
489
+
490
+ def forward(
491
+ self,
492
+ hidden_states: torch.Tensor,
493
+ attention_mask: torch.Tensor | None = None,
494
+ position_ids: torch.LongTensor | None = None,
495
+ past_key_values: Cache | None = None,
496
+ use_cache: bool | None = False,
497
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
498
+ **kwargs: Unpack[TransformersKwargs],
499
+ ) -> torch.Tensor:
500
+ residual = hidden_states
501
+ hidden_states = self.input_layernorm(hidden_states)
502
+ # Self Attention
503
+ hidden_states, _ = self.self_attn(
504
+ hidden_states=hidden_states,
505
+ attention_mask=attention_mask,
506
+ position_ids=position_ids,
507
+ past_key_values=past_key_values,
508
+ use_cache=use_cache,
509
+ position_embeddings=position_embeddings,
510
+ **kwargs,
511
+ )
512
+ hidden_states = residual + hidden_states
513
+
514
+ # Fully Connected
515
+ residual = hidden_states
516
+ hidden_states = self.post_attention_layernorm(hidden_states)
517
+ hidden_states = self.mlp(hidden_states)
518
+ hidden_states = residual + hidden_states
519
+ return hidden_states
520
+
521
+
522
+ @auto_docstring
523
+ class LagunaPreTrainedModel(PreTrainedModel):
524
+ config: LagunaConfig
525
+ base_model_prefix = "model"
526
+ supports_gradient_checkpointing = True
527
+ _no_split_modules = ["LagunaDecoderLayer"]
528
+ _skip_keys_device_placement = ["past_key_values"]
529
+ _supports_flash_attn = True
530
+ _supports_sdpa = True
531
+ _supports_flex_attn = True
532
+ _can_compile_fullgraph = (
533
+ is_grouped_mm_available()
534
+ ) # https://huggingface.co/docs/transformers/experts_interface#torchcompile
535
+ _supports_attention_backend = True
536
+ _can_record_outputs = {
537
+ "router_logits": OutputRecorder(LagunaTopKRouter, index=0),
538
+ "hidden_states": LagunaDecoderLayer,
539
+ "attentions": LagunaAttention,
540
+ }
541
+ # vLLM-trained Laguna checkpoints store the aux-loss-free routing bias on the
542
+ # experts module (``mlp.experts.e_score_correction_bias``). In this impl the
543
+ # bias lives on the router to stay co-located with its consumer across
544
+ # accelerate's per-module hooks, so remap the legacy key on load.
545
+ _checkpoint_conversion_mapping = {
546
+ r"^(.*)\.mlp\.experts\.e_score_correction_bias$": r"\1.mlp.gate.e_score_correction_bias",
547
+ }
548
+
549
+ @torch.no_grad()
550
+ def _init_weights(self, module):
551
+ super()._init_weights(module)
552
+ std = self.config.initializer_range
553
+ if isinstance(module, LagunaExperts):
554
+ init.normal_(module.gate_up_proj, mean=0.0, std=std)
555
+ init.normal_(module.down_proj, mean=0.0, std=std)
556
+ elif isinstance(module, LagunaTopKRouter):
557
+ init.normal_(module.weight, mean=0.0, std=std)
558
+ # Bare ``nn.Parameter``s that are not covered by the parent's generic
559
+ # Linear/Embedding/norm handling need their own rules so that the
560
+ # __init__ and from_pretrained(state_dict={}) paths produce identical
561
+ # weights under a fixed seed.
562
+ if isinstance(module, LagunaTopKRouter):
563
+ torch.nn.init.zeros_(module.e_score_correction_bias)
564
+ if isinstance(module, LagunaAttention) and hasattr(module, "sink"):
565
+ torch.nn.init.zeros_(module.sink)
566
+
567
+
568
+ class LagunaModel(LagunaPreTrainedModel):
569
+ def __init__(self, config: LagunaConfig):
570
+ super().__init__(config)
571
+ self.padding_idx = config.pad_token_id
572
+ self.vocab_size = config.vocab_size
573
+
574
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
575
+ self.layers = nn.ModuleList(
576
+ [LagunaDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
577
+ )
578
+ self.norm = LagunaRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
579
+
580
+ # ``LagunaRotaryEmbedding`` inherits ``Qwen2MoeRotaryEmbedding``'s flat-shape
581
+ # contract — it reads ``config.rope_parameters["rope_type"]`` at the outer
582
+ # level. Laguna stores rope nested by layer type (``{"full_attention": {...},
583
+ # ...}``), so pass a config clone with the full-attention sub-dict flattened.
584
+ rp = getattr(config, "rope_parameters", None)
585
+ if isinstance(rp, dict) and isinstance(rp.get("full_attention"), dict):
586
+ import copy
587
+
588
+ full_config = copy.deepcopy(config)
589
+ full_config.rope_parameters = dict(rp["full_attention"])
590
+ self.rotary_emb = LagunaRotaryEmbedding(config=full_config)
591
+ else:
592
+ self.rotary_emb = LagunaRotaryEmbedding(config=config)
593
+
594
+ # Separate RoPE for sliding-window attention layers (when configured).
595
+ # Be careful with ``partial_rotary_factor`` — ``PreTrainedConfig.standardize_rope_params``
596
+ # unconditionally overwrites ``rope_parameters["partial_rotary_factor"]`` with
597
+ # ``self.partial_rotary_factor``, so we must align the top-level field on the
598
+ # cloned config to the SWA value, otherwise the global partial factor silently
599
+ # clobbers the SWA one.
600
+ if getattr(config, "swa_rope_parameters", None) is not None:
601
+ import copy
602
+
603
+ swa_config = copy.deepcopy(config)
604
+ swa_config.rope_parameters = dict(config.swa_rope_parameters)
605
+ swa_partial = swa_config.rope_parameters.get("partial_rotary_factor")
606
+ swa_config.partial_rotary_factor = swa_partial
607
+ self.swa_rotary_emb = LagunaRotaryEmbedding(config=swa_config)
608
+ else:
609
+ self.swa_rotary_emb = None
610
+
611
+ self.gradient_checkpointing = False
612
+
613
+ # Initialize weights and apply final processing
614
+ self.post_init()
615
+
616
+ @merge_with_config_defaults
617
+ @capture_outputs
618
+ @auto_docstring
619
+ def forward(
620
+ self,
621
+ input_ids: torch.LongTensor | None = None,
622
+ attention_mask: torch.Tensor | None = None,
623
+ position_ids: torch.LongTensor | None = None,
624
+ past_key_values: Cache | None = None,
625
+ inputs_embeds: torch.FloatTensor | None = None,
626
+ use_cache: bool | None = None,
627
+ **kwargs: Unpack[TransformersKwargs],
628
+ ) -> MoeModelOutputWithPast:
629
+ from transformers.cache_utils import DynamicCache
630
+ from transformers.masking_utils import create_causal_mask, create_sliding_window_causal_mask
631
+
632
+ if (input_ids is None) ^ (inputs_embeds is not None):
633
+ raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
634
+
635
+ if inputs_embeds is None:
636
+ inputs_embeds = self.embed_tokens(input_ids)
637
+
638
+ if use_cache and past_key_values is None:
639
+ past_key_values = DynamicCache(config=self.config)
640
+
641
+ if position_ids is None:
642
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
643
+ position_ids = (
644
+ torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
645
+ ).unsqueeze(0)
646
+
647
+ # Build one mask per layer-type so each layer can be dispatched with the right
648
+ # attention pattern (follows the afmoe / cohere2 v5 convention).
649
+ layer_types = getattr(self.config, "layer_types", None)
650
+ has_swa = layer_types is not None and "sliding_attention" in layer_types
651
+ if not isinstance(causal_mask_mapping := attention_mask, dict):
652
+ mask_kwargs = {
653
+ "config": self.config,
654
+ "inputs_embeds": inputs_embeds,
655
+ "attention_mask": attention_mask,
656
+ "past_key_values": past_key_values,
657
+ "position_ids": position_ids,
658
+ }
659
+ causal_mask_mapping = {"full_attention": create_causal_mask(**mask_kwargs)}
660
+ if has_swa:
661
+ causal_mask_mapping["sliding_attention"] = create_sliding_window_causal_mask(**mask_kwargs)
662
+
663
+ hidden_states = inputs_embeds
664
+ global_pe = self.rotary_emb(hidden_states, position_ids)
665
+ # Per-layer-type position embeddings: Laguna optionally uses a different rope for
666
+ # sliding layers (``swa_rope_parameters``). When absent, SWA layers share the
667
+ # global rope.
668
+ if has_swa:
669
+ swa_pe = self.swa_rotary_emb(hidden_states, position_ids) if self.swa_rotary_emb is not None else global_pe
670
+ position_embeddings_mapping = {"full_attention": global_pe, "sliding_attention": swa_pe}
671
+ else:
672
+ position_embeddings_mapping = None
673
+
674
+ for decoder_layer in self.layers[: self.config.num_hidden_layers]:
675
+ layer_attn_mask = causal_mask_mapping[decoder_layer.attention_type]
676
+ layer_pos_emb = (
677
+ position_embeddings_mapping[decoder_layer.attention_type]
678
+ if position_embeddings_mapping is not None
679
+ else global_pe
680
+ )
681
+ hidden_states = decoder_layer(
682
+ hidden_states,
683
+ attention_mask=layer_attn_mask,
684
+ position_ids=position_ids,
685
+ past_key_values=past_key_values,
686
+ use_cache=use_cache,
687
+ position_embeddings=layer_pos_emb,
688
+ **kwargs,
689
+ )
690
+
691
+ hidden_states = self.norm(hidden_states)
692
+
693
+ return MoeModelOutputWithPast(
694
+ last_hidden_state=hidden_states,
695
+ past_key_values=past_key_values,
696
+ )
697
+
698
+
699
+ def load_balancing_loss_func(
700
+ gate_logits: torch.Tensor | tuple[torch.Tensor] | None,
701
+ num_experts: int | None = None,
702
+ top_k=2,
703
+ attention_mask: torch.Tensor | None = None,
704
+ ) -> torch.Tensor | int:
705
+ r"""
706
+ Computes auxiliary load balancing loss as in Switch Transformer - implemented in Pytorch.
707
+
708
+ See Switch Transformer (https://huggingface.co/papers/2101.03961) for more details. This function implements the loss
709
+ function presented in equations (4) - (6) of the paper. It aims at penalizing cases where the routing between
710
+ experts is too unbalanced.
711
+
712
+ Args:
713
+ gate_logits:
714
+ Logits from the `gate`, should be a tuple of model.config.num_hidden_layers tensors of
715
+ shape [batch_size X sequence_length, num_experts].
716
+ num_experts:
717
+ Number of experts
718
+ top_k:
719
+ The number of experts to route per-token, can be also interpreted as the `top-k` routing
720
+ parameter.
721
+ attention_mask (`torch.Tensor`, *optional*):
722
+ The attention_mask used in forward function
723
+ shape [batch_size X sequence_length] if not None.
724
+
725
+ Returns:
726
+ The auxiliary loss.
727
+ """
728
+ if gate_logits is None or not isinstance(gate_logits, tuple):
729
+ return 0
730
+
731
+ if isinstance(gate_logits, tuple):
732
+ compute_device = gate_logits[0].device
733
+ concatenated_gate_logits = torch.cat([layer_gate.to(compute_device) for layer_gate in gate_logits], dim=0)
734
+
735
+ routing_weights = torch.nn.functional.softmax(concatenated_gate_logits, dim=-1)
736
+
737
+ _, selected_experts = torch.topk(routing_weights, top_k, dim=-1)
738
+
739
+ expert_mask = torch.nn.functional.one_hot(selected_experts, num_experts)
740
+
741
+ if attention_mask is None:
742
+ # Compute the percentage of tokens routed to each experts
743
+ tokens_per_expert = torch.mean(expert_mask.float(), dim=0)
744
+
745
+ # Compute the average probability of routing to these experts
746
+ router_prob_per_expert = torch.mean(routing_weights, dim=0)
747
+ else:
748
+ batch_size, sequence_length = attention_mask.shape
749
+ num_hidden_layers = concatenated_gate_logits.shape[0] // (batch_size * sequence_length)
750
+
751
+ # Compute the mask that masks all padding tokens as 0 with the same shape of expert_mask
752
+ expert_attention_mask = (
753
+ attention_mask[None, :, :, None, None]
754
+ .expand((num_hidden_layers, batch_size, sequence_length, top_k, num_experts))
755
+ .reshape(-1, top_k, num_experts)
756
+ .to(compute_device)
757
+ )
758
+
759
+ # Compute the percentage of tokens routed to each experts
760
+ tokens_per_expert = torch.sum(expert_mask.float() * expert_attention_mask, dim=0) / torch.sum(
761
+ expert_attention_mask, dim=0
762
+ )
763
+
764
+ # Compute the mask that masks all padding tokens as 0 with the same shape of tokens_per_expert
765
+ router_per_expert_attention_mask = (
766
+ attention_mask[None, :, :, None]
767
+ .expand((num_hidden_layers, batch_size, sequence_length, num_experts))
768
+ .reshape(-1, num_experts)
769
+ .to(compute_device)
770
+ )
771
+
772
+ # Compute the average probability of routing to these experts
773
+ router_prob_per_expert = torch.sum(routing_weights * router_per_expert_attention_mask, dim=0) / torch.sum(
774
+ router_per_expert_attention_mask, dim=0
775
+ )
776
+
777
+ overall_loss = torch.sum(tokens_per_expert * router_prob_per_expert.unsqueeze(0))
778
+ return overall_loss * num_experts
779
+
780
+
781
+ @auto_docstring
782
+ class LagunaForCausalLM(LagunaPreTrainedModel, GenerationMixin):
783
+ _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
784
+ _tp_plan = {"lm_head": "colwise_gather_output"}
785
+ _pp_plan = {"lm_head": (["hidden_states"], ["logits"])}
786
+
787
+ def __init__(self, config):
788
+ super().__init__(config)
789
+ self.model = LagunaModel(config)
790
+ self.vocab_size = config.vocab_size
791
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
792
+ self.router_aux_loss_coef = config.router_aux_loss_coef
793
+ self.num_experts = config.num_experts
794
+ self.num_experts_per_tok = config.num_experts_per_tok
795
+
796
+ # Initialize weights and apply final processing
797
+ self.post_init()
798
+
799
+ @can_return_tuple
800
+ @auto_docstring
801
+ def forward(
802
+ self,
803
+ input_ids: torch.LongTensor | None = None,
804
+ attention_mask: torch.Tensor | None = None,
805
+ position_ids: torch.LongTensor | None = None,
806
+ past_key_values: Cache | None = None,
807
+ inputs_embeds: torch.FloatTensor | None = None,
808
+ labels: torch.LongTensor | None = None,
809
+ use_cache: bool | None = None,
810
+ output_router_logits: bool | None = None,
811
+ logits_to_keep: int | torch.Tensor = 0,
812
+ **kwargs: Unpack[TransformersKwargs],
813
+ ) -> MoeCausalLMOutputWithPast:
814
+ r"""
815
+ labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
816
+ Labels for computing the masked language modeling loss. Indices should either be in `[0, ...,
817
+ config.vocab_size]` or -100 (see `input_ids` docstring). Tokens with indices set to `-100` are ignored
818
+ (masked), the loss is only computed for the tokens with labels in `[0, ..., config.vocab_size]`.
819
+ """
820
+
821
+ output_router_logits = (
822
+ output_router_logits if output_router_logits is not None else self.config.output_router_logits
823
+ )
824
+
825
+ # decoder outputs consists of (dec_features, layer_state, dec_hidden, dec_attn)
826
+ outputs: MoeModelOutputWithPast = self.model(
827
+ input_ids=input_ids,
828
+ attention_mask=attention_mask,
829
+ position_ids=position_ids,
830
+ past_key_values=past_key_values,
831
+ inputs_embeds=inputs_embeds,
832
+ use_cache=use_cache,
833
+ output_router_logits=output_router_logits,
834
+ **kwargs,
835
+ )
836
+
837
+ hidden_states = outputs.last_hidden_state
838
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
839
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
840
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
841
+
842
+ loss = None
843
+ if labels is not None:
844
+ loss = self.loss_function(logits, labels, self.vocab_size, **kwargs)
845
+
846
+ aux_loss = None
847
+ if output_router_logits:
848
+ aux_loss = load_balancing_loss_func(
849
+ outputs.router_logits,
850
+ self.num_experts,
851
+ self.num_experts_per_tok,
852
+ attention_mask,
853
+ )
854
+ if labels is not None:
855
+ loss += self.router_aux_loss_coef * aux_loss.to(loss.device) # make sure to reside in the same device
856
+
857
+ return MoeCausalLMOutputWithPast(
858
+ loss=loss,
859
+ aux_loss=aux_loss,
860
+ logits=logits,
861
+ past_key_values=outputs.past_key_values,
862
+ hidden_states=outputs.hidden_states,
863
+ attentions=outputs.attentions,
864
+ router_logits=outputs.router_logits,
865
+ )
866
+
867
+
868
+ __all__ = ["LagunaForCausalLM", "LagunaModel", "LagunaPreTrainedModel"]
869
+
870
+
871
+ # --- Added: register the native Laguna checkpoint-conversion for trust_remote_code loads.
872
+ # transformers >=5.12 skips checkpoint-conversion mappings for custom (remote) code
873
+ # unless explicitly registered, which broke loading the shipped per-expert MoE weights.
874
+ try:
875
+ from transformers.conversion_mapping import (
876
+ get_checkpoint_conversion_mapping as _lg_get,
877
+ register_checkpoint_conversion_mapping as _lg_reg,
878
+ USER_REGISTERED_MAPPINGS as _lg_user,
879
+ )
880
+
881
+ if "laguna" not in _lg_user:
882
+ _lg_m = _lg_get("laguna")
883
+ if _lg_m is not None:
884
+ _lg_reg("laguna", _lg_m, overwrite=True)
885
+ except Exception:
886
+ pass
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "backend": "tokenizers",
3
+ "bos_token": "〈|EOS|〉",
4
+ "clean_up_tokenization_spaces": false,
5
+ "cls_token": "〈|CLS|〉",
6
+ "eos_token": "〈|EOS|〉",
7
+ "is_local": true,
8
+ "local_files_only": false,
9
+ "mask_token": "〈|MASK|〉",
10
+ "model_max_length": 1000000000000000019884624838656,
11
+ "pad_token": "〈|PAD|〉",
12
+ "sep_token": "〈|SEP|〉",
13
+ "tokenizer_class": "TokenizersBackend",
14
+ "tool_parser_type": "glm47",
15
+ "unk_token": "〈|UNK|〉"
16
+ }