File size: 13,142 Bytes
2f389f3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# ruff: noqa
# Copyright 2025 Poolside and the HuggingFace Inc. team. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from transformers.configuration_utils import PreTrainedConfig
from transformers.modeling_rope_utils import RopeParameters
from transformers.utils.import_utils import is_causal_conv1d_available, is_flash_linear_attention_available


class LagunaConfig(PreTrainedConfig):
    r"""
    Configuration class for Laguna model.

    Laguna is Poolside's MoE architecture with:
    - Attention output gating (softplus gate)
    - Sigmoid routing instead of softmax
    - No QKV bias
    - Explicit head_dim parameter

    Args:
        head_dim (`int`, *optional*, defaults to 128):
            Dimension of attention heads. Laguna uses explicit head_dim rather than
            computing it from hidden_size // num_attention_heads.
        qkv_bias (`bool`, *optional*, defaults to `False`):
            Whether to add bias to QKV projections. Laguna uses no QKV bias.
        attention_bias (`bool`, *optional*, defaults to `False`):
            Whether to add bias to attention output projection. Laguna uses no attention bias.
        gating (`bool` or `str`, *optional*, defaults to `True`):
            Attention output gating mode. When ``True`` or ``"per-element"`` a g_proj
            linear layer with output size ``num_attention_heads * head_dim`` is added
            and ``attn_output = attn_output * softplus(g_proj(x))``. When ``"per-head"``
            g_proj has output size ``num_attention_heads`` and the gate broadcasts across
            ``head_dim``. When ``False`` no gating is applied.
        partial_rotary_factor (`float`, *optional*):
            Fraction of head_dim to apply rotary embeddings to. When set, this value is
            injected into ``rope_parameters`` (and ``swa_rope_parameters``) if not already
            specified there. When ``None`` the default behaviour of the rope implementation
            is used (typically full rotary).
        num_attention_heads_per_layer (`list[int]`, *optional*):
            Optional per-layer override for ``num_attention_heads``. When provided the list
            length must equal ``num_hidden_layers`` and each entry is the head count used by
            that layer. When ``None`` every layer uses ``num_attention_heads``.
        vocab_size (`int`, *optional*, defaults to 100352):
            Vocabulary size of the Laguna model.
        hidden_size (`int`, *optional*, defaults to 2048):
            Dimension of the hidden representations.
        intermediate_size (`int`, *optional*, defaults to 8192):
            Dimension of the MLP representations for dense layers.
        num_hidden_layers (`int`, *optional*, defaults to 48):
            Number of hidden layers in the Transformer.
        num_attention_heads (`int`, *optional*, defaults to 32):
            Number of attention heads.
        num_key_value_heads (`int`, *optional*, defaults to 8):
            Number of key-value heads for GQA.
        max_position_embeddings (`int`, *optional*, defaults to 4096):
            Maximum sequence length.
        rms_norm_eps (`float`, *optional*, defaults to 1e-6):
            Epsilon for RMSNorm layers.
        sliding_window (`int`, *optional*):
            Sliding window attention size. Used by layers whose type in ``layer_types``
            is ``"sliding_attention"``. When ``None``, all layers use full attention.
        layer_types (`list[str]`, *optional*):
            Per-layer attention type. Each element should be ``"sliding_attention"`` or
            ``"full_attention"``. Length must equal ``num_hidden_layers``. When ``None``,
            all layers default to global attention.
        swa_attention_sink_enabled (`bool`, *optional*, defaults to `False`):
            Whether to enable learnable attention sinks on sliding-window attention layers.
            When enabled, a per-head bias parameter is added that allows the model to attend
            to position 0 even when it falls outside the sliding window.
        swa_rope_parameters (`RopeParameters`, *optional*):
            Separate RoPE configuration for sliding-window attention layers. When ``None``,
            SWA layers use the same RoPE as global attention layers.
        num_experts (`int`, *optional*, defaults to 256):
            Number of routed experts.
        num_experts_per_tok (`int`, *optional*, defaults to 16):
            Number of experts selected per token (top-k).
        moe_intermediate_size (`int`, *optional*, defaults to 1024):
            Intermediate size of routed experts.
        shared_expert_intermediate_size (`int`, *optional*, defaults to 1024):
            Intermediate size of the shared expert.
        norm_topk_prob (`bool`, *optional*, defaults to `True`):
            Whether to normalize top-k routing probabilities.
        decoder_sparse_step (`int`, *optional*, defaults to 1):
            Frequency of MoE layers (1 = every layer is MoE after mlp_only_layers).
        mlp_only_layers (`list[int]`, *optional*, defaults to `[0]`):
            Layer indices that use dense MLP instead of MoE.
        router_aux_loss_coef (`float`, *optional*, defaults to 0.001):
            Auxiliary loss coefficient for load balancing.
        moe_routed_scaling_factor (`float`, *optional*, defaults to 1.0):
            Scalar multiplier applied to the routed-expert output before combining with the
            shared-expert output.
        moe_apply_router_weight_on_input (`bool`, *optional*, defaults to `False`):
            When ``True`` the top-k routing weights are multiplied into each expert's input
            rather than its output. Matches the numerical form used by the trained checkpoint.
        moe_router_logit_softcapping (`float`, *optional*, defaults to 0.0):
            Optional soft-capping value ``c`` applied to router logits as
            ``x = tanh(x / c) * c`` before sigmoid + top-k. Disabled when ``0``.
        rope_parameters (`RopeParameters`, *optional*):
            RoPE configuration. Defaults to rope_theta=500000.0.
    """

    model_type = "laguna"
    keys_to_ignore_at_inference = ["past_key_values"]
    # PreTrainedConfig in transformers v5 no longer auto-declares these; subclasses
    # opt in by providing class-level annotations with defaults.
    pad_token_id: int | None = None
    bos_token_id: int | None = None
    eos_token_id: int | list[int] | None = None
    base_model_tp_plan = {
        "layers.*.self_attn.q_proj": "colwise",
        "layers.*.self_attn.k_proj": "colwise",
        "layers.*.self_attn.v_proj": "colwise",
        "layers.*.self_attn.g_proj": "colwise",  # Laguna-specific gating projection
        "layers.*.self_attn.o_proj": "rowwise",
        "layers.*.mlp.gate_proj": "colwise",
        "layers.*.mlp.up_proj": "colwise",
        "layers.*.mlp.down_proj": "rowwise",
    }
    base_model_pp_plan = {
        "embed_tokens": (["input_ids"], ["inputs_embeds"]),
        "layers": (["hidden_states", "attention_mask"], ["hidden_states"]),
        "norm": (["hidden_states"], ["hidden_states"]),
    }

    def __init__(
        self,
        vocab_size: int = 100352,
        hidden_size: int = 2048,
        intermediate_size: int = 8192,
        num_hidden_layers: int = 48,
        num_attention_heads: int = 32,
        num_key_value_heads: int = 8,
        head_dim: int = 128,
        qkv_bias: bool = False,
        attention_bias: bool = False,
        gating: bool | str = True,
        hidden_act: str = "silu",
        max_position_embeddings: int = 4096,
        initializer_range: float = 0.02,
        rms_norm_eps: float = 1e-6,
        use_cache: bool = True,
        tie_word_embeddings: bool = False,
        rope_parameters: RopeParameters | dict[str, RopeParameters] | None = None,
        partial_rotary_factor: float | None = None,
        attention_dropout: float = 0.0,
        sliding_window: int | None = None,
        layer_types: list[str] | None = None,
        num_attention_heads_per_layer: list[int] | None = None,
        swa_attention_sink_enabled: bool = False,
        swa_rope_parameters: RopeParameters | None = None,
        num_experts: int = 256,
        num_experts_per_tok: int = 16,
        moe_intermediate_size: int = 1024,
        shared_expert_intermediate_size: int = 1024,
        norm_topk_prob: bool = True,
        decoder_sparse_step: int = 1,
        mlp_only_layers: list[int] | None = None,
        router_aux_loss_coef: float = 0.001,
        moe_routed_scaling_factor: float = 1.0,
        moe_apply_router_weight_on_input: bool = False,
        moe_router_logit_softcapping: float = 0.0,
        output_router_logits: bool = False,
        **kwargs,
    ):
        # Default mlp_only_layers: first layer is dense (moe_first_k_dense_replace=1)
        if mlp_only_layers is None:
            mlp_only_layers = [0]

        # Default layer_types: all layers use full attention (Laguna-M). Laguna-XS
        # ships an explicit list with a mix of "full_attention" and "sliding_attention".
        # Downstream mask builders (``create_masks_for_generate``) iterate
        # ``layer_types``, so it must be a list — not left as ``None``.
        if layer_types is None:
            layer_types = ["full_attention"] * num_hidden_layers

        # Default rope_parameters with Laguna's theta
        if rope_parameters is None:
            rope_parameters = {"rope_type": "default", "rope_theta": 500000.0}

        # config.json stores SWA rope nested in rope_parameters["sliding_attention"]
        # and carries no top-level swa_rope_parameters. Derive it here, else the
        # sliding-window layers silently reuse the full-attention rope.
        if swa_rope_parameters is None and isinstance(rope_parameters, dict):
            swa_rope_parameters = rope_parameters.get("sliding_attention")

        # If ``partial_rotary_factor`` is set at the top level, inject it into any
        # rope dict that does not already carry one so the rotary embedding picks
        # it up consistently for both full-attention and SWA layers.
        if partial_rotary_factor is not None:
            if isinstance(rope_parameters, dict) and "partial_rotary_factor" not in rope_parameters:
                rope_parameters = {**rope_parameters, "partial_rotary_factor": partial_rotary_factor}
            if isinstance(swa_rope_parameters, dict) and "partial_rotary_factor" not in swa_rope_parameters:
                swa_rope_parameters = {
                    **swa_rope_parameters,
                    "partial_rotary_factor": partial_rotary_factor,
                }

        self.vocab_size = vocab_size
        self.hidden_size = hidden_size
        self.intermediate_size = intermediate_size
        self.num_hidden_layers = num_hidden_layers
        self.num_attention_heads = num_attention_heads
        self.num_key_value_heads = num_key_value_heads
        self.head_dim = head_dim
        self.qkv_bias = qkv_bias
        self.attention_bias = attention_bias
        self.gating = gating
        self.hidden_act = hidden_act
        self.max_position_embeddings = max_position_embeddings
        self.initializer_range = initializer_range
        self.rms_norm_eps = rms_norm_eps
        self.use_cache = use_cache
        self.rope_parameters = rope_parameters
        self.partial_rotary_factor = partial_rotary_factor
        self.attention_dropout = attention_dropout
        # Sliding window attention arguments
        self.sliding_window = sliding_window
        self.layer_types = layer_types
        self.num_attention_heads_per_layer = num_attention_heads_per_layer
        self.swa_attention_sink_enabled = swa_attention_sink_enabled
        self.swa_rope_parameters = swa_rope_parameters
        # MoE arguments
        self.num_experts = num_experts
        self.num_experts_per_tok = num_experts_per_tok
        self.moe_intermediate_size = moe_intermediate_size
        self.shared_expert_intermediate_size = shared_expert_intermediate_size
        self.norm_topk_prob = norm_topk_prob
        self.decoder_sparse_step = decoder_sparse_step
        self.mlp_only_layers = mlp_only_layers
        self.router_aux_loss_coef = router_aux_loss_coef
        self.moe_routed_scaling_factor = moe_routed_scaling_factor
        self.moe_apply_router_weight_on_input = moe_apply_router_weight_on_input
        self.moe_router_logit_softcapping = moe_router_logit_softcapping
        self.output_router_logits = output_router_logits

        super().__init__(tie_word_embeddings=tie_word_embeddings, **kwargs)


__all__ = ["LagunaConfig"]